From f9f3ccb63b6aae467fc46a18f8f93c2646289178 Mon Sep 17 00:00:00 2001 From: Arnaud Diederen Date: Mon, 20 Apr 2015 14:26:30 +0200 Subject: [PATCH] IDA Pro 6.8 support What's new: https://www.hex-rays.com/products/ida/6.8/index.shtml --- Scripts/3rd/BboeVt.py | 310 +- Scripts/AsmViewer.py | 430 +- Scripts/DbgCmd.py | 202 +- Scripts/DrvsDispatch.py | 208 +- Scripts/FindInstructions.py | 278 +- Scripts/ImportExportViewer.py | 246 +- Scripts/PteDump.py | 170 +- Scripts/SEHGraph.py | 298 +- Scripts/VirusTotal.py | 734 +- Scripts/msdnapihelp.py | 136 +- basetsd.h | 8 +- build.py | 4 +- docs/notes.txt | 106 +- examples/chooser.py | 100 +- examples/colours.py | 38 +- examples/debughook.py | 214 +- examples/ex1.idc | 70 +- examples/ex_askusingform.py | 758 +- examples/ex_choose2.py | 266 +- examples/ex_cli.py | 200 +- examples/ex_custdata.py | 432 +- examples/ex_custview.py | 366 +- examples/ex_dbg.py | 82 +- examples/ex_debug_names.py | 30 +- examples/ex_expr.py | 32 +- examples/ex_func_chooser.py | 74 +- examples/ex_gdl_qflow_chart.py | 86 +- examples/ex_graph.py | 140 +- examples/ex_imports.py | 56 +- examples/ex_prefix_plugin.py | 84 +- examples/ex_pyqt.py | 70 +- examples/ex_pyside.py | 68 +- examples/hotkey.py | 48 +- examples/structure.py | 98 +- idapython.sln | 52 +- idapython.vcxproj | 904 +-- idapython.vcxproj.filters | 638 +- patch_directors_cc.py | 3 + python.cfg | 4 - python.cpp | 3614 +++++---- python/idautils.py | 1660 ++-- python/idc.py | 27 +- python/init.py | 2 +- pywraps.hpp | 23 + pywraps/deploy.bat | 4 +- pywraps/deploy.py | 196 +- pywraps/driver.cpp | 344 +- pywraps/driver_bytes.cpp | 36 +- pywraps/driver_chooser.cpp | 218 +- pywraps/driver_cli.cpp | 56 +- pywraps/driver_custview.cpp | 768 +- pywraps/driver_dbg.cpp | 158 +- pywraps/driver_diskio.cpp | 92 +- pywraps/driver_expr.cpp | 78 +- pywraps/driver_graph.cpp | 86 +- pywraps/driver_kernwin.cpp | 154 +- pywraps/driver_nalt.cpp | 36 +- pywraps/driver_notifywhen.cpp | 72 +- pywraps/link_gen.py | 670 +- pywraps/py_appcall.py | 2062 ++--- pywraps/py_askusingform.hpp | 774 +- pywraps/py_askusingform.py | 3900 ++++----- pywraps/py_choose.hpp | 156 +- pywraps/py_choose2.hpp | 1817 +++-- pywraps/py_choose2.py | 816 +- pywraps/py_cli.hpp | 590 +- pywraps/py_cli.py | 374 +- pywraps/py_custdata.hpp | 1366 ++-- pywraps/py_custdata.py | 494 +- pywraps/py_custview.hpp | 2266 +++--- pywraps/py_custview.py | 944 +-- pywraps/py_cvt.hpp | 1514 ++-- pywraps/py_dbg.hpp | 1570 ++-- pywraps/py_diskio.hpp | 128 +- pywraps/py_diskio.py | 10 +- pywraps/py_expr.hpp | 202 +- pywraps/py_expr.py | 338 +- pywraps/py_gdl.py | 176 +- pywraps/py_graph.hpp | 1632 ++-- pywraps/py_graph.py | 322 +- pywraps/py_idaapi.hpp | 1664 ++-- pywraps/py_idaapi.py | 1210 +-- pywraps/py_idaview.hpp | 2 +- pywraps/py_idp.hpp | 4 +- pywraps/py_kernwin.py | 242 +- pywraps/py_lines.hpp | 428 +- pywraps/py_lines.py | 68 +- pywraps/py_linput.hpp | 834 +- pywraps/py_loader.hpp | 184 +- pywraps/py_nalt.hpp | 1092 +-- pywraps/py_nalt.py | 380 +- pywraps/py_name.hpp | 64 +- pywraps/py_name.py | 106 +- pywraps/py_notifywhen.hpp | 620 +- pywraps/py_notifywhen.py | 126 +- pywraps/py_plgform.hpp | 314 +- pywraps/py_plgform.py | 242 +- pywraps/py_qfile.hpp | 814 +- pywraps/py_typeinf.hpp | 1375 ++-- pywraps/py_ua.hpp | 1826 +++-- pywraps/py_ua.py | 2 +- pywraps/py_view_base.hpp | 80 +- pywraps/pywraps.hpp | 2 +- pywraps/pywraps.sln | 64 +- pywraps/pywraps.vcproj | 3688 ++++----- pywraps/readme.txt | 230 +- pywraps/sidaapi.py | 454 +- pywraps/sidc.py | 626 +- pywraps/swig_stub.cpp | 12 +- pywraps/swig_stub.h | 52 +- swig/bytes.i | 2450 +++--- swig/dbg.i | 924 +-- swig/diskio.i | 1060 +-- swig/enum.i | 3 +- swig/expr.i | 676 +- swig/fpro.i | 808 +- swig/frame.i | 10 +- swig/gdl.i | 228 +- swig/graph.i | 1966 ++--- swig/idaapi.i | 5570 ++++++------- swig/idd.i | 1806 ++--- swig/idp.i | 3336 ++++---- swig/kernwin.i | 13431 ++++++++++++++++--------------- swig/lines.i | 659 +- swig/loader.i | 442 +- swig/moves.i | 2 +- swig/nalt.i | 1764 ++-- swig/name.i | 281 +- swig/netnode.i | 15 +- swig/pro.i | 346 +- swig/queue.i | 2 +- swig/registry.i | 290 +- swig/struct.i | 2 + swig/typeconv.i | 677 +- swig/typeinf.i | 1970 ++--- swig/ua.i | 2960 ++++--- swig/view.i | 2482 +++--- tools/swigdocs.py | 196 +- 138 files changed, 49628 insertions(+), 49321 deletions(-) diff --git a/Scripts/3rd/BboeVt.py b/Scripts/3rd/BboeVt.py index 8ae9f5ab..5562cf41 100644 --- a/Scripts/3rd/BboeVt.py +++ b/Scripts/3rd/BboeVt.py @@ -1,156 +1,156 @@ -""" -Original code by Bryce Boe: http://www.bryceboe.com/2010/09/01/submitting-binaries-to-virustotal/ - -Modified by Elias Bachaalany - -""" - -import hashlib, httplib, mimetypes, os, pprint, simplejson, sys, urlparse - -# ----------------------------------------------------------------------- -DEFAULT_TYPE = 'application/octet-stream' -FILE_REPORT_URL = 'https://www.virustotal.com/api/get_file_report.json' -SCAN_URL = 'https://www.virustotal.com/api/scan_file.json' -API_KEY = "" # Put API key here. Register an account in VT Community - - -# ----------------------------------------------------------------------- -# The following function is modified from the snippet at: -# http://code.activestate.com/recipes/146306/ -def _encode_multipart_formdata(fields, files=()): - """ - fields is a dictionary of name to value for regular form fields. - files is a sequence of (name, filename, value) elements for data to be - uploaded as files. - Return (content_type, body) ready for httplib.HTTP instance - """ - BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' - CRLF = '\r\n' - L = [] - for key, value in fields.items(): - L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"' % key) - L.append('') - L.append(value) - for (key, filename, value) in files: - L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % - (key, filename)) - content_type = mimetypes.guess_type(filename)[0] or DEFAULT_TYPE - L.append('Content-Type: %s' % content_type) - L.append('') - L.append(value) - L.append('--' + BOUNDARY + '--') - L.append('') - body = CRLF.join(L) - content_type = 'multipart/form-data; boundary=%s' % BOUNDARY - return content_type, body - - -# ----------------------------------------------------------------------- -def _post_multipart(url, fields, files=()): - """ - url is the full to send the post request to. - fields is a dictionary of name to value for regular form fields. - files is a sequence of (name, filename, value) elements for data to be - uploaded as files. - Return body of http response. - """ - content_type, data = _encode_multipart_formdata(fields, files) - url_parts = urlparse.urlparse(url) - if url_parts.scheme == 'http': - h = httplib.HTTPConnection(url_parts.netloc) - elif url_parts.scheme == 'https': - h = httplib.HTTPSConnection(url_parts.netloc) - else: - raise Exception('Unsupported URL scheme') - path = urlparse.urlunparse(('', '') + url_parts[2:]) - h.request('POST', path, data, {'content-type':content_type}) - return h.getresponse().read() - - -# ----------------------------------------------------------------------- -def set_apikey(key, dbg = False): - """ - Set the VT API key - """ - global API_KEY - API_KEY = key - if dbg: - httplib.HTTPConnection.debuglevel = 1 - - - -# ----------------------------------------------------------------------- -def scan_file(filename): - """ - Uploads a file for scanning. - - @param filename: The filename to upload - - @return: - None if upload failed - - scan_id value if upload succeeds - - raises an exception on IO failures - """ - files = [('file', filename, open(filename, 'rb').read())] - json = _post_multipart(SCAN_URL, {'key':API_KEY}, files) - data = simplejson.loads(json) - return str(data['scan_id']) if data['result'] == 1 else None - - -# ----------------------------------------------------------------------- -def get_file_md5_hash(filename): - f = open(filename, 'rb') - r = hashlib.md5(f.read()).hexdigest() - f.close() - return r - - -# ----------------------------------------------------------------------- -def get_file_report(filename=None, md5sum=None): - """ - Returns an report for a file or md5su. - - @param filename: File name to get report. The file is used just - to compute its MD5Sum - @param md5sum: MD5sum string (in case filename was not passed) - - @return: - None: if file was not previously analyzed - - A dictionary if report exists: key=scanner, value=reported name - """ - if filename is None and md5sum is None: - raise Exception('Either filename or md5sum should be passed!') - - # Filename passed? Compute its MD5 - if filename: - global LAST_FILE_HASH - LAST_FILE_HASH = md5sum = get_file_md5_hash(filename) - - # Form the request - json = _post_multipart(FILE_REPORT_URL, {'resource':md5sum, 'key':API_KEY}) - data = simplejson.loads(json) - if data['result'] != 1: - # No results - return None - else: - # date, result_dict = data['report'] - return data['report'][1] - - -# ----------------------------------------------------------------------- -def pretty_print(obj): - pprint.pprint(obj) - - -# ----------------------------------------------------------------------- -if __name__ == '__main__': - if len(sys.argv) != 2: - print('Usage: %s filename' % sys.argv[0]) - sys.exit(1) - - filename = sys.argv[1] - if not os.path.isfile(filename): - print('%s is not a valid file' % filename) - sys.exit(1) - +""" +Original code by Bryce Boe: http://www.bryceboe.com/2010/09/01/submitting-binaries-to-virustotal/ + +Modified by Elias Bachaalany + +""" + +import hashlib, httplib, mimetypes, os, pprint, simplejson, sys, urlparse + +# ----------------------------------------------------------------------- +DEFAULT_TYPE = 'application/octet-stream' +FILE_REPORT_URL = 'https://www.virustotal.com/api/get_file_report.json' +SCAN_URL = 'https://www.virustotal.com/api/scan_file.json' +API_KEY = "" # Put API key here. Register an account in VT Community + + +# ----------------------------------------------------------------------- +# The following function is modified from the snippet at: +# http://code.activestate.com/recipes/146306/ +def _encode_multipart_formdata(fields, files=()): + """ + fields is a dictionary of name to value for regular form fields. + files is a sequence of (name, filename, value) elements for data to be + uploaded as files. + Return (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' + CRLF = '\r\n' + L = [] + for key, value in fields.items(): + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key, filename, value) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)) + content_type = mimetypes.guess_type(filename)[0] or DEFAULT_TYPE + L.append('Content-Type: %s' % content_type) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + + +# ----------------------------------------------------------------------- +def _post_multipart(url, fields, files=()): + """ + url is the full to send the post request to. + fields is a dictionary of name to value for regular form fields. + files is a sequence of (name, filename, value) elements for data to be + uploaded as files. + Return body of http response. + """ + content_type, data = _encode_multipart_formdata(fields, files) + url_parts = urlparse.urlparse(url) + if url_parts.scheme == 'http': + h = httplib.HTTPConnection(url_parts.netloc) + elif url_parts.scheme == 'https': + h = httplib.HTTPSConnection(url_parts.netloc) + else: + raise Exception('Unsupported URL scheme') + path = urlparse.urlunparse(('', '') + url_parts[2:]) + h.request('POST', path, data, {'content-type':content_type}) + return h.getresponse().read() + + +# ----------------------------------------------------------------------- +def set_apikey(key, dbg = False): + """ + Set the VT API key + """ + global API_KEY + API_KEY = key + if dbg: + httplib.HTTPConnection.debuglevel = 1 + + + +# ----------------------------------------------------------------------- +def scan_file(filename): + """ + Uploads a file for scanning. + + @param filename: The filename to upload + + @return: - None if upload failed + - scan_id value if upload succeeds + - raises an exception on IO failures + """ + files = [('file', filename, open(filename, 'rb').read())] + json = _post_multipart(SCAN_URL, {'key':API_KEY}, files) + data = simplejson.loads(json) + return str(data['scan_id']) if data['result'] == 1 else None + + +# ----------------------------------------------------------------------- +def get_file_md5_hash(filename): + f = open(filename, 'rb') + r = hashlib.md5(f.read()).hexdigest() + f.close() + return r + + +# ----------------------------------------------------------------------- +def get_file_report(filename=None, md5sum=None): + """ + Returns an report for a file or md5su. + + @param filename: File name to get report. The file is used just + to compute its MD5Sum + @param md5sum: MD5sum string (in case filename was not passed) + + @return: - None: if file was not previously analyzed + - A dictionary if report exists: key=scanner, value=reported name + """ + if filename is None and md5sum is None: + raise Exception('Either filename or md5sum should be passed!') + + # Filename passed? Compute its MD5 + if filename: + global LAST_FILE_HASH + LAST_FILE_HASH = md5sum = get_file_md5_hash(filename) + + # Form the request + json = _post_multipart(FILE_REPORT_URL, {'resource':md5sum, 'key':API_KEY}) + data = simplejson.loads(json) + if data['result'] != 1: + # No results + return None + else: + # date, result_dict = data['report'] + return data['report'][1] + + +# ----------------------------------------------------------------------- +def pretty_print(obj): + pprint.pprint(obj) + + +# ----------------------------------------------------------------------- +if __name__ == '__main__': + if len(sys.argv) != 2: + print('Usage: %s filename' % sys.argv[0]) + sys.exit(1) + + filename = sys.argv[1] + if not os.path.isfile(filename): + print('%s is not a valid file' % filename) + sys.exit(1) + get_file_report(filename=filename) \ No newline at end of file diff --git a/Scripts/AsmViewer.py b/Scripts/AsmViewer.py index ae70add7..bcec4284 100644 --- a/Scripts/AsmViewer.py +++ b/Scripts/AsmViewer.py @@ -1,216 +1,216 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to use customview in Python -# The sample will allow you to open an assembly file and display it in color -# (c) Hex-Rays -# -import idaapi -import idautils -import idc -import os - -# ---------------------------------------------------------------------- -class asm_colorizer_t(object): - def is_id(self, ch): - return ch == '_' or ch.isalpha() or '0' <= ch <= '9' - - def get_identifier(self, line, x, e): - i = x - is_digit = line[i].isdigit() - while i < e: - ch = line[i] - if not self.is_id(ch): - if ch != '.' or not is_digit: - break - i += 1 - return (i, line[x:i]) - - def get_quoted_string(self, line, x, e): - quote = line[x] - i = x + 1 - while i < e: - ch = line[i] - if ch == '\\' and line[i+1] == quote: - i += 1 - elif ch == quote: - i += 1 # also take the quote - break - i += 1 - return (i, line[x:i]) - - def colorize(self, lines): - for line in lines: - line = line.rstrip() - if not line: - self.add_line() - continue - x = 0 - e = len(line) - s = "" - while x < e: - ch = line[x] - # String? - if ch == '"' or ch == "'": - x, w = self.get_quoted_string(line, x, e) - s += self.as_string(w) - # Tab? - elif ch == '\t': - s += ' ' * 4 - x += 1 - # Comment? - elif ch == ';': - s += self.as_comment(line[x:]) - # Done with this line - break - elif ch == '.' and x + 1 < e: - x, w = self.get_identifier(line, x + 1, e) - s += self.as_directive(ch + w) - # Identifiers? - elif self.is_id(ch): - x, w = self.get_identifier(line, x, e) - # Number? - if ch.isdigit(): - s += self.as_num(w) - # Other identifier - else: - s += self.as_id(w) - # Output as is - else: - s += ch - x += 1 - self.add_line(s) - -# ----------------------------------------------------------------------- -class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): - def Create(self, fn): - # Create the customview - if not idaapi.simplecustviewer_t.Create(self, "Viewing file - %s" % os.path.basename(fn)): - return False - - self.instruction_list = idautils.GetInstructionList() - self.instruction_list.extend(["ret"]) - self.register_list = idautils.GetRegisterList() - self.register_list.extend(["eax", "ebx", "ecx", "edx", "edi", "esi", "ebp", "esp"]) - - self.fn = fn - if not self.reload_file(): - return False - - self.id_refresh = self.AddPopupMenu("Refresh") - self.id_close = self.AddPopupMenu("Close") - - return True - - def reload_file(self): - if not self.colorize_file(self.fn): - self.Close() - return False - return True - - def colorize_file(self, fn): - try: - f = open(fn, "r") - lines = f.readlines() - f.close() - self.ClearLines() - self.colorize(lines) - return True - except: - return False - - def add_line(self, s=None): - if not s: - s = "" - self.AddLine(s) - - def as_comment(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_RPTCMT) - - def as_id(self, s): - t = s.lower() - if t in self.register_list: - return idaapi.COLSTR(s, idaapi.SCOLOR_REG) - elif t in self.instruction_list: - return idaapi.COLSTR(s, idaapi.SCOLOR_INSN) - else: - return s - - def as_string(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_STRING) - - def as_num(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_NUMBER) - - def as_directive(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_KEYWORD) - - def OnPopupMenu(self, menu_id): - """ - A context (or popup) menu item was executed. - @param menu_id: ID previously registered with AddPopupMenu() - @return: Boolean - """ - if self.id_refresh == menu_id: - return self.reload_file() - elif self.id_close == menu_id: - self.Close() - return True - return False - - def OnKeydown(self, vkey, shift): - """ - User pressed a key - @param vkey: Virtual key code - @param shift: Shift flag - @return Boolean. True if you handled the event - """ - # ESCAPE - if vkey == 27: - self.Close() - elif vkey == ord('H'): - lineno = self.GetLineNo() - if lineno is not None: - line, fg, bg = self.GetLine(lineno) - if line and line[0] != idaapi.SCOLOR_INV: - s = idaapi.SCOLOR_INV + line + idaapi.SCOLOR_INV - self.EditLine(lineno, s, fg, bg) - self.Refresh() - elif vkey == ord('C'): - self.ClearLines() - self.Refresh() - elif vkey == ord('S'): - print "Selection (x1, y1, x2, y2) = ", self.GetSelection() - elif vkey == ord('I'): - print "Position (line, x, y) = ", self.GetPos(mouse = 0) - else: - return False - return True - -# ----------------------------------------------------------------------- -class asmviewplg(idaapi.plugin_t): - flags = idaapi.PLUGIN_KEEP - comment = "ASM viewer" - help = "This is help" - wanted_name = "ASM file viewer" - wanted_hotkey = "Alt-F8" - def __init__(self): - self.view = None - - def init(self): - return idaapi.PLUGIN_KEEP - def run(self, arg): - if self.view: - self.Close() - fn = idc.AskFile(0, "*.asm", "Select ASM file to view") - if not fn: - return - self.view = asmview_t() - if not self.view.Create(fn): - return - self.view.Show() - - def term(self): - if self.view: - self.view.Close() - -def PLUGIN_ENTRY(): +# ----------------------------------------------------------------------- +# This is an example illustrating how to use customview in Python +# The sample will allow you to open an assembly file and display it in color +# (c) Hex-Rays +# +import idaapi +import idautils +import idc +import os + +# ---------------------------------------------------------------------- +class asm_colorizer_t(object): + def is_id(self, ch): + return ch == '_' or ch.isalpha() or '0' <= ch <= '9' + + def get_identifier(self, line, x, e): + i = x + is_digit = line[i].isdigit() + while i < e: + ch = line[i] + if not self.is_id(ch): + if ch != '.' or not is_digit: + break + i += 1 + return (i, line[x:i]) + + def get_quoted_string(self, line, x, e): + quote = line[x] + i = x + 1 + while i < e: + ch = line[i] + if ch == '\\' and line[i+1] == quote: + i += 1 + elif ch == quote: + i += 1 # also take the quote + break + i += 1 + return (i, line[x:i]) + + def colorize(self, lines): + for line in lines: + line = line.rstrip() + if not line: + self.add_line() + continue + x = 0 + e = len(line) + s = "" + while x < e: + ch = line[x] + # String? + if ch == '"' or ch == "'": + x, w = self.get_quoted_string(line, x, e) + s += self.as_string(w) + # Tab? + elif ch == '\t': + s += ' ' * 4 + x += 1 + # Comment? + elif ch == ';': + s += self.as_comment(line[x:]) + # Done with this line + break + elif ch == '.' and x + 1 < e: + x, w = self.get_identifier(line, x + 1, e) + s += self.as_directive(ch + w) + # Identifiers? + elif self.is_id(ch): + x, w = self.get_identifier(line, x, e) + # Number? + if ch.isdigit(): + s += self.as_num(w) + # Other identifier + else: + s += self.as_id(w) + # Output as is + else: + s += ch + x += 1 + self.add_line(s) + +# ----------------------------------------------------------------------- +class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): + def Create(self, fn): + # Create the customview + if not idaapi.simplecustviewer_t.Create(self, "Viewing file - %s" % os.path.basename(fn)): + return False + + self.instruction_list = idautils.GetInstructionList() + self.instruction_list.extend(["ret"]) + self.register_list = idautils.GetRegisterList() + self.register_list.extend(["eax", "ebx", "ecx", "edx", "edi", "esi", "ebp", "esp"]) + + self.fn = fn + if not self.reload_file(): + return False + + self.id_refresh = self.AddPopupMenu("Refresh") + self.id_close = self.AddPopupMenu("Close") + + return True + + def reload_file(self): + if not self.colorize_file(self.fn): + self.Close() + return False + return True + + def colorize_file(self, fn): + try: + f = open(fn, "r") + lines = f.readlines() + f.close() + self.ClearLines() + self.colorize(lines) + return True + except: + return False + + def add_line(self, s=None): + if not s: + s = "" + self.AddLine(s) + + def as_comment(self, s): + return idaapi.COLSTR(s, idaapi.SCOLOR_RPTCMT) + + def as_id(self, s): + t = s.lower() + if t in self.register_list: + return idaapi.COLSTR(s, idaapi.SCOLOR_REG) + elif t in self.instruction_list: + return idaapi.COLSTR(s, idaapi.SCOLOR_INSN) + else: + return s + + def as_string(self, s): + return idaapi.COLSTR(s, idaapi.SCOLOR_STRING) + + def as_num(self, s): + return idaapi.COLSTR(s, idaapi.SCOLOR_NUMBER) + + def as_directive(self, s): + return idaapi.COLSTR(s, idaapi.SCOLOR_KEYWORD) + + def OnPopupMenu(self, menu_id): + """ + A context (or popup) menu item was executed. + @param menu_id: ID previously registered with AddPopupMenu() + @return: Boolean + """ + if self.id_refresh == menu_id: + return self.reload_file() + elif self.id_close == menu_id: + self.Close() + return True + return False + + def OnKeydown(self, vkey, shift): + """ + User pressed a key + @param vkey: Virtual key code + @param shift: Shift flag + @return Boolean. True if you handled the event + """ + # ESCAPE + if vkey == 27: + self.Close() + elif vkey == ord('H'): + lineno = self.GetLineNo() + if lineno is not None: + line, fg, bg = self.GetLine(lineno) + if line and line[0] != idaapi.SCOLOR_INV: + s = idaapi.SCOLOR_INV + line + idaapi.SCOLOR_INV + self.EditLine(lineno, s, fg, bg) + self.Refresh() + elif vkey == ord('C'): + self.ClearLines() + self.Refresh() + elif vkey == ord('S'): + print "Selection (x1, y1, x2, y2) = ", self.GetSelection() + elif vkey == ord('I'): + print "Position (line, x, y) = ", self.GetPos(mouse = 0) + else: + return False + return True + +# ----------------------------------------------------------------------- +class asmviewplg(idaapi.plugin_t): + flags = idaapi.PLUGIN_KEEP + comment = "ASM viewer" + help = "This is help" + wanted_name = "ASM file viewer" + wanted_hotkey = "Alt-F8" + def __init__(self): + self.view = None + + def init(self): + return idaapi.PLUGIN_KEEP + def run(self, arg): + if self.view: + self.Close() + fn = idc.AskFile(0, "*.asm", "Select ASM file to view") + if not fn: + return + self.view = asmview_t() + if not self.view.Create(fn): + return + self.view.Show() + + def term(self): + if self.view: + self.view.Close() + +def PLUGIN_ENTRY(): return asmviewplg() \ No newline at end of file diff --git a/Scripts/DbgCmd.py b/Scripts/DbgCmd.py index 602566d9..a15544b2 100644 --- a/Scripts/DbgCmd.py +++ b/Scripts/DbgCmd.py @@ -1,101 +1,101 @@ -# ----------------------------------------------------------------------- -# Debugger command prompt with CustomViewers -# (c) Hex-Rays -# -import idaapi -import idc -from idaapi import simplecustviewer_t - -def SendDbgCommand(cmd): - """Sends a command to the debugger and returns the output string. - An exception will be raised if the debugger is not running or the current debugger does not export - the 'SendDbgCommand' IDC command. - """ - s = Eval('SendDbgCommand("%s");' % cmd) - if s.startswith("IDC_FAILURE"): - raise Exception, "Debugger command is available only when the debugger is active!" - return s - -# ----------------------------------------------------------------------- -class dbgcmd_t(simplecustviewer_t): - def Create(self): - # Form the title - title = "Debugger command window" - # Create the customview - if not simplecustviewer_t.Create(self, title): - return False - self.last_cmd = "" - self.menu_clear = self.AddPopupMenu("Clear") - self.menu_cmd = self.AddPopupMenu("New command") - - self.ResetOutput() - return True - - def IssueCommand(self): - s = idaapi.askstr(0, self.last_cmd, "Please enter a debugger command") - if not s: - return - - # Save last command - self.last_cmd = s - - # Add it using a different color - self.AddLine("debugger>" + idaapi.COLSTR(s, idaapi.SCOLOR_VOIDOP)) - - try: - r = SendDbgCommand(s).split("\n") - for s in r: - self.AddLine(idaapi.COLSTR(s, idaapi.SCOLOR_LIBNAME)) - except: - self.AddLine(idaapi.COLSTR("Debugger is not active or does not export SendDbgCommand()", idaapi.SCOLOR_ERROR)) - self.Refresh() - - def ResetOutput(self): - self.ClearLines() - self.AddLine(idaapi.COLSTR("Please press INS to enter command; X to clear output", idaapi.SCOLOR_AUTOCMT)) - self.Refresh() - - def OnKeydown(self, vkey, shift): - # ESCAPE? - if vkey == 27: - self.Close() - # VK_INSERT - elif vkey == 45: - self.IssueCommand() - elif vkey == ord('X'): - self.ResetOutput() - else: - return False - return True - - def OnPopupMenu(self, menu_id): - if menu_id == self.menu_clear: - self.ResetOutput() - elif menu_id == self.menu_cmd: - self.IssueCommand() - else: - # Unhandled - return False - return True - -# ----------------------------------------------------------------------- -def show_win(): - x = dbgcmd_t() - if not x.Create(): - print "Failed to create debugger command line!" - return None - x.Show() - return x - -try: - # created already? - dbgcmd - dbgcmd.Close() - del dbgcmd -except: - pass - -dbgcmd = show_win() -if not dbgcmd: - del dbgcmd - +# ----------------------------------------------------------------------- +# Debugger command prompt with CustomViewers +# (c) Hex-Rays +# +import idaapi +import idc +from idaapi import simplecustviewer_t + +def SendDbgCommand(cmd): + """Sends a command to the debugger and returns the output string. + An exception will be raised if the debugger is not running or the current debugger does not export + the 'SendDbgCommand' IDC command. + """ + s = Eval('SendDbgCommand("%s");' % cmd) + if s.startswith("IDC_FAILURE"): + raise Exception, "Debugger command is available only when the debugger is active!" + return s + +# ----------------------------------------------------------------------- +class dbgcmd_t(simplecustviewer_t): + def Create(self): + # Form the title + title = "Debugger command window" + # Create the customview + if not simplecustviewer_t.Create(self, title): + return False + self.last_cmd = "" + self.menu_clear = self.AddPopupMenu("Clear") + self.menu_cmd = self.AddPopupMenu("New command") + + self.ResetOutput() + return True + + def IssueCommand(self): + s = idaapi.askstr(0, self.last_cmd, "Please enter a debugger command") + if not s: + return + + # Save last command + self.last_cmd = s + + # Add it using a different color + self.AddLine("debugger>" + idaapi.COLSTR(s, idaapi.SCOLOR_VOIDOP)) + + try: + r = SendDbgCommand(s).split("\n") + for s in r: + self.AddLine(idaapi.COLSTR(s, idaapi.SCOLOR_LIBNAME)) + except: + self.AddLine(idaapi.COLSTR("Debugger is not active or does not export SendDbgCommand()", idaapi.SCOLOR_ERROR)) + self.Refresh() + + def ResetOutput(self): + self.ClearLines() + self.AddLine(idaapi.COLSTR("Please press INS to enter command; X to clear output", idaapi.SCOLOR_AUTOCMT)) + self.Refresh() + + def OnKeydown(self, vkey, shift): + # ESCAPE? + if vkey == 27: + self.Close() + # VK_INSERT + elif vkey == 45: + self.IssueCommand() + elif vkey == ord('X'): + self.ResetOutput() + else: + return False + return True + + def OnPopupMenu(self, menu_id): + if menu_id == self.menu_clear: + self.ResetOutput() + elif menu_id == self.menu_cmd: + self.IssueCommand() + else: + # Unhandled + return False + return True + +# ----------------------------------------------------------------------- +def show_win(): + x = dbgcmd_t() + if not x.Create(): + print "Failed to create debugger command line!" + return None + x.Show() + return x + +try: + # created already? + dbgcmd + dbgcmd.Close() + del dbgcmd +except: + pass + +dbgcmd = show_win() +if not dbgcmd: + del dbgcmd + diff --git a/Scripts/DrvsDispatch.py b/Scripts/DrvsDispatch.py index 544e7ba9..db984d3b 100644 --- a/Scripts/DrvsDispatch.py +++ b/Scripts/DrvsDispatch.py @@ -1,105 +1,105 @@ -""" - -A script to demonstrate how to send commands to the debugger and then parse and use the output in IDA - -Copyright (c) 1990-2009 Hex-Rays -ALL RIGHTS RESERVED. - -""" - -import re -from idaapi import Choose - -# ----------------------------------------------------------------------- -def CmdDriverList(): - s = Eval('WinDbgCommand("lm o");') - if "IDC_FAILURE" in s: return False - return s - -# ----------------------------------------------------------------------- -def CmdDrvObj(drvname, flag=2): - return Eval('WinDbgCommand("!drvobj %s %d");' % (drvname, flag)) - -# ----------------------------------------------------------------------- -def CmdReloadForce(): - s = Eval('WinDbgCommand(".reload /f");') - if "IDC_FAILURE" in s: return False - return True - -# ----------------------------------------------------------------------- -# class to hold dispatch entry information -class DispatchEntry: - def __init__(self, addr, name): - self.addr = addr - self.name = name - def __repr__(self): - return "%08X: %s" % (self.addr, self.name) - -# ----------------------------------------------------------------------- -def GetDriverDispatch(): - - # return a list of arrays of the form: [addr, name] - ret_list = [] - - # build the RE for parsing output from the "lm o" command - re_drv = re.compile('^[a-f0-9]+\s+[a-f0-9]+\s+(\S+)', re.I) - - # build the RE for parsing output from the "!drvobj DRV_NAME 2" command - re_tbl = re.compile('^\[\d{2}\]\s+IRP_MJ_(\S+)\s+([0-9a-f]+)', re.I) - - # force reloading of module symbols - if not CmdReloadForce(): - print "Could not communicate with WinDbg, make sure the debugger is running!" - return None - - # get driver list - lm_out = CmdDriverList() - if not lm_out: - return "Failed to get driver list!" - - # for each line - for line in lm_out.split("\n"): - # parse - r = re_drv.match(line) - if not r: continue - - # extract driver name - drvname = r.group(1).strip() - - # execute "drvobj" command - tbl_out = CmdDrvObj(drvname) - - if not tbl_out: - print "Failed to get driver object for", drvname - continue - - # for each line - for line in tbl_out.split("\n"): - # parse - r = re_tbl.match(line) - if not r: continue - disp_addr = int(r.group(2), 16) # convert hex string to number - disp_name = "Dispatch" + r.group(1) - ret_list.append(DispatchEntry(disp_addr, drvname + "_" + disp_name)) - - return ret_list - -# ----------------------------------------------------------------------- -# Chooser class -class DispatchChoose(Choose): - def __init__(self, list, title): - Choose.__init__(self, list, title) - self.width = 250 - - def enter(self, n): - o = self.list[n-1] - idc.Jump(o.addr) - -# ----------------------------------------------------------------------- -# main -r = GetDriverDispatch() -if r: - c = DispatchChoose(r, "Dispatch table browser") - c.choose() -else: +""" + +A script to demonstrate how to send commands to the debugger and then parse and use the output in IDA + +Copyright (c) 1990-2009 Hex-Rays +ALL RIGHTS RESERVED. + +""" + +import re +from idaapi import Choose + +# ----------------------------------------------------------------------- +def CmdDriverList(): + s = Eval('WinDbgCommand("lm o");') + if "IDC_FAILURE" in s: return False + return s + +# ----------------------------------------------------------------------- +def CmdDrvObj(drvname, flag=2): + return Eval('WinDbgCommand("!drvobj %s %d");' % (drvname, flag)) + +# ----------------------------------------------------------------------- +def CmdReloadForce(): + s = Eval('WinDbgCommand(".reload /f");') + if "IDC_FAILURE" in s: return False + return True + +# ----------------------------------------------------------------------- +# class to hold dispatch entry information +class DispatchEntry: + def __init__(self, addr, name): + self.addr = addr + self.name = name + def __repr__(self): + return "%08X: %s" % (self.addr, self.name) + +# ----------------------------------------------------------------------- +def GetDriverDispatch(): + + # return a list of arrays of the form: [addr, name] + ret_list = [] + + # build the RE for parsing output from the "lm o" command + re_drv = re.compile('^[a-f0-9]+\s+[a-f0-9]+\s+(\S+)', re.I) + + # build the RE for parsing output from the "!drvobj DRV_NAME 2" command + re_tbl = re.compile('^\[\d{2}\]\s+IRP_MJ_(\S+)\s+([0-9a-f]+)', re.I) + + # force reloading of module symbols + if not CmdReloadForce(): + print "Could not communicate with WinDbg, make sure the debugger is running!" + return None + + # get driver list + lm_out = CmdDriverList() + if not lm_out: + return "Failed to get driver list!" + + # for each line + for line in lm_out.split("\n"): + # parse + r = re_drv.match(line) + if not r: continue + + # extract driver name + drvname = r.group(1).strip() + + # execute "drvobj" command + tbl_out = CmdDrvObj(drvname) + + if not tbl_out: + print "Failed to get driver object for", drvname + continue + + # for each line + for line in tbl_out.split("\n"): + # parse + r = re_tbl.match(line) + if not r: continue + disp_addr = int(r.group(2), 16) # convert hex string to number + disp_name = "Dispatch" + r.group(1) + ret_list.append(DispatchEntry(disp_addr, drvname + "_" + disp_name)) + + return ret_list + +# ----------------------------------------------------------------------- +# Chooser class +class DispatchChoose(Choose): + def __init__(self, list, title): + Choose.__init__(self, list, title) + self.width = 250 + + def enter(self, n): + o = self.list[n-1] + idc.Jump(o.addr) + +# ----------------------------------------------------------------------- +# main +r = GetDriverDispatch() +if r: + c = DispatchChoose(r, "Dispatch table browser") + c.choose() +else: print "Failed to retrieve dispatchers list!" \ No newline at end of file diff --git a/Scripts/FindInstructions.py b/Scripts/FindInstructions.py index 353d5252..5bfaac58 100644 --- a/Scripts/FindInstructions.py +++ b/Scripts/FindInstructions.py @@ -1,139 +1,139 @@ -""" -FindInstructions.py: A script to help you find desired opcodes/instructions in a database - -The script accepts opcodes and assembly statements (which will be assembled) separated by semicolon - -The general syntax is: - find(asm or opcodes, x=Bool, asm_where=ea) - -* Example: - find("asm_statement1;asm_statement2;de ea dc 0d e0;asm_statement3;xx yy zz;...") -* To filter-out non-executable segments pass x=True - find("jmp dword ptr [esp]", x=True) -* To specify in which context the instructions should be assembled, pass asm_where=ea: - find("jmp dword ptr [esp]", asm_where=here()) - -Copyright (c) 1990-2009 Hex-Rays -ALL RIGHTS RESERVED. - -v1.0 - initial version -""" -import idaapi -import idautils -import idc - -# ----------------------------------------------------------------------- -def FindInstructions(instr, asm_where=None): - """ - Finds instructions/opcodes - @return: Returns a tuple(True, [ ea, ... ]) or a tuple(False, "error message") - """ - if not asm_where: - # get first segment - asm_where = FirstSeg() - if asm_where == idaapi.BADADDR: - return (False, "No segments defined") - - # regular expression to distinguish between opcodes and instructions - re_opcode = re.compile('^[0-9a-f]{2} *', re.I) - - # split lines - lines = instr.split(";") - - # all the assembled buffers (for each instruction) - bufs = [] - for line in lines: - if re_opcode.match(line): - # convert from hex string to a character list then join the list to form one string - buf = ''.join([chr(int(x, 16)) for x in line.split()]) - else: - # assemble the instruction - ret, buf = Assemble(asm_where, line) - if not ret: - return (False, "Failed to assemble:"+line) - # add the assembled buffer - bufs.append(buf) - - # join the buffer into one string - buf = ''.join(bufs) - - # take total assembled instructions length - tlen = len(buf) - - # convert from binary string to space separated hex string - bin_str = ' '.join(["%02X" % ord(x) for x in buf]) - - # find all binary strings - print "Searching for: [%s]" % bin_str - ea = MinEA() - ret = [] - while True: - ea = FindBinary(ea, SEARCH_DOWN, bin_str) - if ea == idaapi.BADADDR: - break - ret.append(ea) - Message(".") - ea += tlen - if not ret: - return (False, "Could not match [%s]" % bin_str) - Message("\n") - return (True, ret) - -# ----------------------------------------------------------------------- -# Chooser class -class SearchResultChoose(Choose): - def __init__(self, list, title): - Choose.__init__(self, list, title) - self.width = 250 - - def enter(self, n): - o = self.list[n-1] - Jump(o.ea) - -# ----------------------------------------------------------------------- -# class to represent the results -class SearchResult: - def __init__(self, ea): - self.ea = ea - if not isCode(GetFlags(ea)): - MakeCode(ea) - t = idaapi.generate_disasm_line(ea) - if t: - line = idaapi.tag_remove(t) - else: - line = "" - func = GetFunctionName(ea) - self.display = hex(ea) + ": " - if func: - self.display += func + ": " - else: - n = SegName(ea) - if n: self.display += n + ": " - self.display += line - - def __str__(self): - return self.display - -# ----------------------------------------------------------------------- -def find(s=None, x=False, asm_where=None): - b, ret = FindInstructions(s, asm_where) - if b: - # executable segs only? - if x: - results = [] - for ea in ret: - seg = idaapi.getseg(ea) - if (not seg) or (seg.perm & idaapi.SEGPERM_EXEC) == 0: - continue - results.append(SearchResult(ea)) - else: - results = [SearchResult(ea) for ea in ret] - title = "Search result for: [%s]" % s - idaapi.close_chooser(title) - c = SearchResultChoose(results, title) - c.choose() - else: - print ret - -# ----------------------------------------------------------------------- -print "Please use find('asm_stmt1;xx yy;...', x=Bool,asm_where=ea) to search for instructions or opcodes. Specify x=true to filter out non-executable segments" +""" +FindInstructions.py: A script to help you find desired opcodes/instructions in a database + +The script accepts opcodes and assembly statements (which will be assembled) separated by semicolon + +The general syntax is: + find(asm or opcodes, x=Bool, asm_where=ea) + +* Example: + find("asm_statement1;asm_statement2;de ea dc 0d e0;asm_statement3;xx yy zz;...") +* To filter-out non-executable segments pass x=True + find("jmp dword ptr [esp]", x=True) +* To specify in which context the instructions should be assembled, pass asm_where=ea: + find("jmp dword ptr [esp]", asm_where=here()) + +Copyright (c) 1990-2009 Hex-Rays +ALL RIGHTS RESERVED. + +v1.0 - initial version +""" +import idaapi +import idautils +import idc + +# ----------------------------------------------------------------------- +def FindInstructions(instr, asm_where=None): + """ + Finds instructions/opcodes + @return: Returns a tuple(True, [ ea, ... ]) or a tuple(False, "error message") + """ + if not asm_where: + # get first segment + asm_where = FirstSeg() + if asm_where == idaapi.BADADDR: + return (False, "No segments defined") + + # regular expression to distinguish between opcodes and instructions + re_opcode = re.compile('^[0-9a-f]{2} *', re.I) + + # split lines + lines = instr.split(";") + + # all the assembled buffers (for each instruction) + bufs = [] + for line in lines: + if re_opcode.match(line): + # convert from hex string to a character list then join the list to form one string + buf = ''.join([chr(int(x, 16)) for x in line.split()]) + else: + # assemble the instruction + ret, buf = Assemble(asm_where, line) + if not ret: + return (False, "Failed to assemble:"+line) + # add the assembled buffer + bufs.append(buf) + + # join the buffer into one string + buf = ''.join(bufs) + + # take total assembled instructions length + tlen = len(buf) + + # convert from binary string to space separated hex string + bin_str = ' '.join(["%02X" % ord(x) for x in buf]) + + # find all binary strings + print "Searching for: [%s]" % bin_str + ea = MinEA() + ret = [] + while True: + ea = FindBinary(ea, SEARCH_DOWN, bin_str) + if ea == idaapi.BADADDR: + break + ret.append(ea) + Message(".") + ea += tlen + if not ret: + return (False, "Could not match [%s]" % bin_str) + Message("\n") + return (True, ret) + +# ----------------------------------------------------------------------- +# Chooser class +class SearchResultChoose(Choose): + def __init__(self, list, title): + Choose.__init__(self, list, title) + self.width = 250 + + def enter(self, n): + o = self.list[n-1] + Jump(o.ea) + +# ----------------------------------------------------------------------- +# class to represent the results +class SearchResult: + def __init__(self, ea): + self.ea = ea + if not isCode(GetFlags(ea)): + MakeCode(ea) + t = idaapi.generate_disasm_line(ea) + if t: + line = idaapi.tag_remove(t) + else: + line = "" + func = GetFunctionName(ea) + self.display = hex(ea) + ": " + if func: + self.display += func + ": " + else: + n = SegName(ea) + if n: self.display += n + ": " + self.display += line + + def __str__(self): + return self.display + +# ----------------------------------------------------------------------- +def find(s=None, x=False, asm_where=None): + b, ret = FindInstructions(s, asm_where) + if b: + # executable segs only? + if x: + results = [] + for ea in ret: + seg = idaapi.getseg(ea) + if (not seg) or (seg.perm & idaapi.SEGPERM_EXEC) == 0: + continue + results.append(SearchResult(ea)) + else: + results = [SearchResult(ea) for ea in ret] + title = "Search result for: [%s]" % s + idaapi.close_chooser(title) + c = SearchResultChoose(results, title) + c.choose() + else: + print ret + +# ----------------------------------------------------------------------- +print "Please use find('asm_stmt1;xx yy;...', x=Bool,asm_where=ea) to search for instructions or opcodes. Specify x=true to filter out non-executable segments" diff --git a/Scripts/ImportExportViewer.py b/Scripts/ImportExportViewer.py index afe0aac8..7c668bae 100644 --- a/Scripts/ImportExportViewer.py +++ b/Scripts/ImportExportViewer.py @@ -1,124 +1,124 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to: -# - enumerate imports -# - enumerate entrypoints -# - Use PluginForm class -# - Use PySide with PluginForm to create a Python UI -# -# (c) Hex-Rays -# -import idaapi -import idautils -from idaapi import PluginForm -from PySide import QtGui, QtCore - -# -------------------------------------------------------------------------- -class ImpExpForm_t(PluginForm): - - def imports_names_cb(self, ea, name, ord): - self.items.append((ea, '' if not name else name, ord)) - # True -> Continue enumeration - return True - - - def BuildImports(self): - tree = {} - nimps = idaapi.get_import_module_qty() - - for i in xrange(0, nimps): - name = idaapi.get_import_module_name(i) - if not name: - continue - # Create a list for imported names - self.items = [] - - # Enum imported entries in this module - idaapi.enum_import_names(i, self.imports_names_cb) - - if name not in tree: - tree[name] = [] - tree[name].extend(self.items) - - return tree - - - def BuildExports(self): - return list(idautils.Entries()) - - - def PopulateTree(self): - # Clear previous items - self.tree.clear() - - # Build imports - root = QtGui.QTreeWidgetItem(self.tree) - root.setText(0, "Imports") - - for dll_name, imp_entries in self.BuildImports().items(): - imp_dll = QtGui.QTreeWidgetItem(root) - imp_dll.setText(0, dll_name) - - for imp_ea, imp_name, imp_ord in imp_entries: - item = QtGui.QTreeWidgetItem(imp_dll) - item.setText(0, "%s [0x%08x]" %(imp_name, imp_ea)) - - - # Build exports - root = QtGui.QTreeWidgetItem(self.tree) - root.setText(0, "Exports") - - for exp_i, exp_ord, exp_ea, exp_name in self.BuildExports(): - item = QtGui.QTreeWidgetItem(root) - item.setText(0, "%s [#%d] [0x%08x]" % (exp_name, exp_ord, exp_ea)) - - - def OnCreate(self, form): - """ - Called when the plugin form is created - """ - - # Get parent widget - self.parent = self.FormToPySideWidget(form) - - # Create tree control - self.tree = QtGui.QTreeWidget() - self.tree.setHeaderLabels(("Names",)) - self.tree.setColumnWidth(0, 100) - - # Create layout - layout = QtGui.QVBoxLayout() - layout.addWidget(self.tree) - - self.PopulateTree() - # Populate PluginForm - self.parent.setLayout(layout) - - - def OnClose(self, form): - """ - Called when the plugin form is closed - """ - global ImpExpForm - del ImpExpForm - print "Closed" - - - def Show(self): - """Creates the form is not created or focuses it if it was""" - return PluginForm.Show(self, - "Imports / Exports viewer", - options = PluginForm.FORM_PERSIST) - -# -------------------------------------------------------------------------- -def main(): - global ImpExpForm - - try: - ImpExpForm - except: - ImpExpForm = ImpExpForm_t() - - ImpExpForm.Show() - -# -------------------------------------------------------------------------- +# ----------------------------------------------------------------------- +# This is an example illustrating how to: +# - enumerate imports +# - enumerate entrypoints +# - Use PluginForm class +# - Use PySide with PluginForm to create a Python UI +# +# (c) Hex-Rays +# +import idaapi +import idautils +from idaapi import PluginForm +from PySide import QtGui, QtCore + +# -------------------------------------------------------------------------- +class ImpExpForm_t(PluginForm): + + def imports_names_cb(self, ea, name, ord): + self.items.append((ea, '' if not name else name, ord)) + # True -> Continue enumeration + return True + + + def BuildImports(self): + tree = {} + nimps = idaapi.get_import_module_qty() + + for i in xrange(0, nimps): + name = idaapi.get_import_module_name(i) + if not name: + continue + # Create a list for imported names + self.items = [] + + # Enum imported entries in this module + idaapi.enum_import_names(i, self.imports_names_cb) + + if name not in tree: + tree[name] = [] + tree[name].extend(self.items) + + return tree + + + def BuildExports(self): + return list(idautils.Entries()) + + + def PopulateTree(self): + # Clear previous items + self.tree.clear() + + # Build imports + root = QtGui.QTreeWidgetItem(self.tree) + root.setText(0, "Imports") + + for dll_name, imp_entries in self.BuildImports().items(): + imp_dll = QtGui.QTreeWidgetItem(root) + imp_dll.setText(0, dll_name) + + for imp_ea, imp_name, imp_ord in imp_entries: + item = QtGui.QTreeWidgetItem(imp_dll) + item.setText(0, "%s [0x%08x]" %(imp_name, imp_ea)) + + + # Build exports + root = QtGui.QTreeWidgetItem(self.tree) + root.setText(0, "Exports") + + for exp_i, exp_ord, exp_ea, exp_name in self.BuildExports(): + item = QtGui.QTreeWidgetItem(root) + item.setText(0, "%s [#%d] [0x%08x]" % (exp_name, exp_ord, exp_ea)) + + + def OnCreate(self, form): + """ + Called when the plugin form is created + """ + + # Get parent widget + self.parent = self.FormToPySideWidget(form) + + # Create tree control + self.tree = QtGui.QTreeWidget() + self.tree.setHeaderLabels(("Names",)) + self.tree.setColumnWidth(0, 100) + + # Create layout + layout = QtGui.QVBoxLayout() + layout.addWidget(self.tree) + + self.PopulateTree() + # Populate PluginForm + self.parent.setLayout(layout) + + + def OnClose(self, form): + """ + Called when the plugin form is closed + """ + global ImpExpForm + del ImpExpForm + print "Closed" + + + def Show(self): + """Creates the form is not created or focuses it if it was""" + return PluginForm.Show(self, + "Imports / Exports viewer", + options = PluginForm.FORM_PERSIST) + +# -------------------------------------------------------------------------- +def main(): + global ImpExpForm + + try: + ImpExpForm + except: + ImpExpForm = ImpExpForm_t() + + ImpExpForm.Show() + +# -------------------------------------------------------------------------- main() \ No newline at end of file diff --git a/Scripts/PteDump.py b/Scripts/PteDump.py index 23e37b57..1bd20f36 100644 --- a/Scripts/PteDump.py +++ b/Scripts/PteDump.py @@ -1,85 +1,85 @@ -import idaapi -import idc -from idaapi import Choose2 - -def parse_pte(str): - try: - parse_pte.re - except: - parse_pte.re = re.compile('PDE at ([0-9a-f]+)\s*PTE at ([0-9a-f]+)\ncontains ([0-9a-f]+)\s*contains ([0-9a-f]+)\npfn ([0-9]+)\s*([^ ]+)\s*pfn ([0-9a-f]+)\s*([^\r\n]+)', re.I | re.M) - parse_pte.items = ('pde', 'pte', 'pdec', 'ptec', 'pdepfn', 'pdepfns', 'ptepfn', 'ptepfns') - - m = parse_pte.re.search(s) - r = {} - for i in range(0, len(parse_pte.items)): - r[parse_pte.items[i]] = m.group(i+1) - return r - -class MyChoose2(Choose2): - - def __init__(self, title, ea1, ea2): - Choose2.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ]) - self.ea1 = ea1 - self.ea2 = ea2 - self.n = 0 - self.icon = 5 - self.items = [] - self.Refresh() - self.selcount = 0 - - def OnGetLine(self, n): - print("getline %d" % n) - return self.items[n] - - def OnGetSize(self): - n = len(self.items) - self.Refresh() - return n - - def OnRefresh(self, n): - print("refresh %d" % n) - return n - - def Refresh(self): - items = [] - PG = 0x1000 - ea1 = self.ea1 - npages = (self.ea2 - ea1) / PG - for i in range(npages): - r = idc.SendDbgCommand("!pte %x" % ea1) - if not r: - return False - r = parse_pte(r) - items.append([hex(ea1), r['ptepfns']]) - ea1 += PG - - self.items = items - print(self.items) - return True - - @staticmethod - def Execute(ea1, ea2): - c = MyChoose2("PTE Viewer [%x..%x]" % (ea1, ea2), ea1, ea2) - return (c, c.Show()) - - -def DumpPTE(ea1, ea2): - items = [] - PG = 0x1000 - npages = (ea2 - ea1) / PG - for i in range(npages): - r = idc.SendDbgCommand("!pte %x" % ea1) - if not r: - return False - print r - r = parse_pte(r) - print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns'])) - ea1 += PG - -def DumpSegPTE(ea): - DumpPTE(idc.SegStart(ea), idc.SegEnd(ea)) - -DumpSegPTE(here()) - -#MyChoose2.Execute(0xF718F000, 0xF718F000+0x1000) - +import idaapi +import idc +from idaapi import Choose2 + +def parse_pte(str): + try: + parse_pte.re + except: + parse_pte.re = re.compile('PDE at ([0-9a-f]+)\s*PTE at ([0-9a-f]+)\ncontains ([0-9a-f]+)\s*contains ([0-9a-f]+)\npfn ([0-9]+)\s*([^ ]+)\s*pfn ([0-9a-f]+)\s*([^\r\n]+)', re.I | re.M) + parse_pte.items = ('pde', 'pte', 'pdec', 'ptec', 'pdepfn', 'pdepfns', 'ptepfn', 'ptepfns') + + m = parse_pte.re.search(s) + r = {} + for i in range(0, len(parse_pte.items)): + r[parse_pte.items[i]] = m.group(i+1) + return r + +class MyChoose2(Choose2): + + def __init__(self, title, ea1, ea2): + Choose2.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ]) + self.ea1 = ea1 + self.ea2 = ea2 + self.n = 0 + self.icon = 5 + self.items = [] + self.Refresh() + self.selcount = 0 + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + self.Refresh() + return n + + def OnRefresh(self, n): + print("refresh %d" % n) + return n + + def Refresh(self): + items = [] + PG = 0x1000 + ea1 = self.ea1 + npages = (self.ea2 - ea1) / PG + for i in range(npages): + r = idc.SendDbgCommand("!pte %x" % ea1) + if not r: + return False + r = parse_pte(r) + items.append([hex(ea1), r['ptepfns']]) + ea1 += PG + + self.items = items + print(self.items) + return True + + @staticmethod + def Execute(ea1, ea2): + c = MyChoose2("PTE Viewer [%x..%x]" % (ea1, ea2), ea1, ea2) + return (c, c.Show()) + + +def DumpPTE(ea1, ea2): + items = [] + PG = 0x1000 + npages = (ea2 - ea1) / PG + for i in range(npages): + r = idc.SendDbgCommand("!pte %x" % ea1) + if not r: + return False + print r + r = parse_pte(r) + print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns'])) + ea1 += PG + +def DumpSegPTE(ea): + DumpPTE(idc.SegStart(ea), idc.SegEnd(ea)) + +DumpSegPTE(here()) + +#MyChoose2.Execute(0xF718F000, 0xF718F000+0x1000) + diff --git a/Scripts/SEHGraph.py b/Scripts/SEHGraph.py index 9cd32164..88ab71d0 100644 --- a/Scripts/SEHGraph.py +++ b/Scripts/SEHGraph.py @@ -1,149 +1,149 @@ -""" - -A script that graphs all the exception handlers in a given process - -It will be easy to see what thread uses what handler and what handlers are commonly used between threads - -Copyright (c) 1990-2009 Hex-Rays -ALL RIGHTS RESERVED. - - -v1.0 - initial version - -""" - -import idaapi -import idautils -import idc - -from idaapi import GraphViewer - -# ----------------------------------------------------------------------- -# Since Windbg debug module does not support get_thread_sreg_base() -# we will call the debugger engine "dg" command and parse its output -def WindbgGetRegBase(tid): - s = idc.Eval('WinDbgCommand("dg %x")' % cpu.fs) - if "IDC_FAILURE" in s: - return 0 - m = re.compile("[0-9a-f]{4} ([0-9a-f]{8})") - t = m.match(s.split('\n')[-2]) - if not t: - return 0 - return int(t.group(1), 16) - -# ----------------------------------------------------------------------- -def GetFsBase(tid): - idc.SelectThread(tid) - base = idaapi.dbg_get_thread_sreg_base(tid, cpu.fs) - if base != 0: - return base - return WindbgGetRegBase(tid) - -# ----------------------------------------------------------------------- -# Walks the SEH chain and returns a list of handlers -def GetExceptionChain(tid): - fs_base = GetFsBase(tid) - exc_rr = Dword(fs_base) - result = [] - while exc_rr != 0xffffffff: - prev = Dword(exc_rr) - handler = Dword(exc_rr + 4) - exc_rr = prev - result.append(handler) - return result - -# ----------------------------------------------------------------------- -class SEHGraph(GraphViewer): - def __init__(self, title, result): - GraphViewer.__init__(self, title) - self.result = result - self.names = {} # ea -> name - - def OnRefresh(self): - self.Clear() - addr_id = {} - - for (tid, chain) in self.result.items(): - # Each node data will contain a tuple of the form: (Boolean->Is_thread, Int->Value, String->Label) - # For threads the is_thread will be true and the value will hold the thread id - # For exception handlers, is_thread=False and Value=Handler address - - # Add the thread node - id_parent = self.AddNode( (True, tid, "Thread %X" % tid) ) - - # Add each handler - for handler in chain: - # Check if a function is created at the handler's address - f = idaapi.get_func(handler) - if not f: - # create function - idc.MakeFunction(handler, idaapi.BADADDR) - - # Node label is function name or address - s = GetFunctionName(handler) - if not s: - s = "%x" % handler - - # cache name - self.names[handler] = s - - # Get the node id given the handler address - # We use an addr -> id dictionary so that similar addresses get similar node id - if not addr_id.has_key(handler): - id = self.AddNode( (False, handler, s) ) - addr_id[handler] = id # add this ID - else: - id = addr_id[handler] - - # Link handlers to each other - self.AddEdge(id_parent, id) - id_parent = id - - return True - - def OnGetText(self, node_id): - is_thread, value, label = self[node_id] - if is_thread: - return (label, 0xff00f0) - return label - - def OnDblClick(self, node_id): - is_thread, value, label = self[node_id] - if is_thread: - idc.SelectThread(value) - self.Show() - s = "SEH chain for " + hex(value) - t = "-" * len(s) - print t - print s - print t - for handler in self.result[value]: - print "%x: %s" % (handler, self.names[handler]) - print t - else: - idc.Jump(value) - return True - - -# ----------------------------------------------------------------------- -def main(): - if not idaapi.dbg_can_query(): - print "The debugger must be active and suspended before using this script!" - return - - # Save current thread id - tid = GetCurrentThreadId() - - # Iterate through all function instructions and take only call instructions - result = {} - for tid in idautils.Threads(): - result[tid] = GetExceptionChain(tid) - - # Restore previously selected thread - idc.SelectThread(tid) - - # Build the graph - g = SEHGraph("SEH graph", result) - g.Show() - -main() +""" + +A script that graphs all the exception handlers in a given process + +It will be easy to see what thread uses what handler and what handlers are commonly used between threads + +Copyright (c) 1990-2009 Hex-Rays +ALL RIGHTS RESERVED. + + +v1.0 - initial version + +""" + +import idaapi +import idautils +import idc + +from idaapi import GraphViewer + +# ----------------------------------------------------------------------- +# Since Windbg debug module does not support get_thread_sreg_base() +# we will call the debugger engine "dg" command and parse its output +def WindbgGetRegBase(tid): + s = idc.Eval('WinDbgCommand("dg %x")' % cpu.fs) + if "IDC_FAILURE" in s: + return 0 + m = re.compile("[0-9a-f]{4} ([0-9a-f]{8})") + t = m.match(s.split('\n')[-2]) + if not t: + return 0 + return int(t.group(1), 16) + +# ----------------------------------------------------------------------- +def GetFsBase(tid): + idc.SelectThread(tid) + base = idaapi.dbg_get_thread_sreg_base(tid, cpu.fs) + if base != 0: + return base + return WindbgGetRegBase(tid) + +# ----------------------------------------------------------------------- +# Walks the SEH chain and returns a list of handlers +def GetExceptionChain(tid): + fs_base = GetFsBase(tid) + exc_rr = Dword(fs_base) + result = [] + while exc_rr != 0xffffffff: + prev = Dword(exc_rr) + handler = Dword(exc_rr + 4) + exc_rr = prev + result.append(handler) + return result + +# ----------------------------------------------------------------------- +class SEHGraph(GraphViewer): + def __init__(self, title, result): + GraphViewer.__init__(self, title) + self.result = result + self.names = {} # ea -> name + + def OnRefresh(self): + self.Clear() + addr_id = {} + + for (tid, chain) in self.result.items(): + # Each node data will contain a tuple of the form: (Boolean->Is_thread, Int->Value, String->Label) + # For threads the is_thread will be true and the value will hold the thread id + # For exception handlers, is_thread=False and Value=Handler address + + # Add the thread node + id_parent = self.AddNode( (True, tid, "Thread %X" % tid) ) + + # Add each handler + for handler in chain: + # Check if a function is created at the handler's address + f = idaapi.get_func(handler) + if not f: + # create function + idc.MakeFunction(handler, idaapi.BADADDR) + + # Node label is function name or address + s = GetFunctionName(handler) + if not s: + s = "%x" % handler + + # cache name + self.names[handler] = s + + # Get the node id given the handler address + # We use an addr -> id dictionary so that similar addresses get similar node id + if not addr_id.has_key(handler): + id = self.AddNode( (False, handler, s) ) + addr_id[handler] = id # add this ID + else: + id = addr_id[handler] + + # Link handlers to each other + self.AddEdge(id_parent, id) + id_parent = id + + return True + + def OnGetText(self, node_id): + is_thread, value, label = self[node_id] + if is_thread: + return (label, 0xff00f0) + return label + + def OnDblClick(self, node_id): + is_thread, value, label = self[node_id] + if is_thread: + idc.SelectThread(value) + self.Show() + s = "SEH chain for " + hex(value) + t = "-" * len(s) + print t + print s + print t + for handler in self.result[value]: + print "%x: %s" % (handler, self.names[handler]) + print t + else: + idc.Jump(value) + return True + + +# ----------------------------------------------------------------------- +def main(): + if not idaapi.dbg_can_query(): + print "The debugger must be active and suspended before using this script!" + return + + # Save current thread id + tid = GetCurrentThreadId() + + # Iterate through all function instructions and take only call instructions + result = {} + for tid in idautils.Threads(): + result[tid] = GetExceptionChain(tid) + + # Restore previously selected thread + idc.SelectThread(tid) + + # Build the graph + g = SEHGraph("SEH graph", result) + g.Show() + +main() diff --git a/Scripts/VirusTotal.py b/Scripts/VirusTotal.py index 2ddc437c..ca2a86f8 100644 --- a/Scripts/VirusTotal.py +++ b/Scripts/VirusTotal.py @@ -1,367 +1,367 @@ -# ----------------------------------------------------------------------- -# VirusTotal IDA Plugin -# By Elias Bachaalany -# (c) Hex-Rays 2011 -# -# Special thanks: -# - VirusTotal team -# - Bryce Boe for his VirusTotal Python code -# -import idaapi -import idc -from idaapi import Choose2, plugin_t -import BboeVt as vt -import webbrowser -import urllib -import os - - -PLUGIN_TEST = 0 - -# ----------------------------------------------------------------------- -# Configuration file -VT_CFGFILE = os.path.join(idaapi.get_user_idadir(), "virustotal.cfg") - -# ----------------------------------------------------------------------- -# VirusTotal Icon in PNG format -VT_ICON = ( - "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" - "\x00\x00\x00\x10\x00\x00\x00\x10\x04\x03\x00\x00\x00\xED\xDD\xE2" - "\x52\x00\x00\x00\x30\x50\x4C\x54\x45\x03\x8B\xD3\x5C\xB4\xE3\x9C" - "\xD1\xED\xF7\xFB\xFD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xD3\xF2\x42\x61\x00\x00\x00" - "\x4B\x49\x44\x41\x54\x78\x9C\x2D\xCA\xC1\x0D\x80\x30\x0C\x43\x51" - "\x27\x2C\x50\x89\x05\x40\x2C\x40\xEB\xFD\x77\xC3\x76\xC9\xE9\xEB" - "\xC5\x20\x5F\xE8\x1A\x0F\x97\xA3\xD0\xE4\x1D\xF9\x49\xD1\x59\x29" - "\x4C\x43\x9B\xD0\x15\x01\xB5\x4A\x9C\xE4\x70\x14\x39\xB3\x31\xF8" - "\x15\x70\x04\xF4\xDA\x20\x39\x02\x8A\x0D\xA8\x0F\x94\xA7\x09\x0E" - "\xC5\x16\x2D\x54\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82") - - -# ----------------------------------------------------------------------- -class VirusTotalConfig(object): - def __init__(self): - self.Default() - - - def Default(self): - self.md5sum = GetInputMD5() - self.infile = idaapi.dbg_get_input_path() - if not self.infile: - self.infile = "" - - # Persistent options - self.apikey = "" - self.options = 1 | 2 - - - def Read(self): - """ - Read configuration from file - """ - if not os.path.exists(VT_CFGFILE): - return - f = open(VT_CFGFILE, 'r') - lines = f.readlines() - for i, line in enumerate(lines): - line = line.strip() - if i == 0: - self.apikey = line - elif i == 1: - self.options = int(line) - else: - break - - - def Write(self): - """ - Write configuration to file - """ - lines = (self.apikey.strip(), str(self.options)) - try: - f = open(VT_CFGFILE, 'w') - f.write("\n".join(lines)) - f.close() - except: - pass - - -# ----------------------------------------------------------------------- -def VtReport(apikey, filename=None, md5sum=None): - if filename is None and md5sum is None: - return (False, "No parameters passed!") - - # Check filename existance - if filename is not None and not os.path.exists(filename): - return (False, "Input file '%s' does not exist!" % filename) - - #print("fn=%s md5=%s" % (filename, md5sum)) - # Get file report from VirusTotal - try: - vt.set_apikey(apikey) - result = vt.get_file_report(filename=filename, md5sum=md5sum) - except Exception as e: - return (False, "Exception:\n%s" % str(e)) - - # Already analyzed? - if result is not None: - # Transform the results - items = [] - for av, mwname in result.items(): - mwname = str(mwname) if mwname else "n/a" - av = str(av) - items.append([av, mwname]) - result = items - - return (True, result) - - -# ----------------------------------------------------------------------- -class VirusTotalChooser(Choose2): - """ - Chooser class to display results from VT - """ - def __init__(self, title, items, icon, embedded=False): - Choose2.__init__(self, - title, - [ ["Antivirus", 20], ["Result", 40] ], - embedded=embedded) - self.items = items - self.icon = icon - - - def GetItems(self): - return self.items - - - def SetItems(self, items): - self.items = [] if items is None else items - - - def OnClose(self): - pass - - - def OnGetLine(self, n): - return self.items[n] - - - def OnGetSize(self): - return len(self.items) - - - def OnSelectLine(self, n): - # Google search for the malware name and the antivirus name - s = urllib.urlencode({"q" : " ".join(self.items[n])}) - webbrowser.open_new_tab("http://www.google.com/search?%s" % s) - - -# -------------------------------------------------------------------------- -class VirusTotalForm(Form): - def __init__(self, icon): - self.EChooser = VirusTotalChooser("E1", [], icon, embedded=True) - Form.__init__(self, r"""STARTITEM {id:txtInput} -VirusTotal - IDAPython plugin v1.0 (c) Hex-Rays - -{FormChangeCb} -<#API key#~A~pi key:{txtApiKey}> - -Options: -<#Open results in a chooser when form closes#~P~opout results on close:{rOptRemember}> -<#Use MD5 checksum#~M~D5Sum:{rOptMD5}> -<#Use file on disk#~F~ile:{rOptFile}>{grpOptions}> - -<#Type input (file or MD5 string)#~I~nput:{txtInput}> - -<#Get reports from VT#~R~eport:{btnReport}> -""", { - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - 'txtApiKey' : Form.StringInput(swidth=80), - 'grpOptions' : Form.ChkGroupControl(("rOptRemember", "rOptMD5", "rOptFile")), - 'txtInput' : Form.FileInput(open=True), - 'btnReport' : Form.ButtonInput(self.OnReportClick), - 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) - }) - - - - def OnReportClick(self, code=0): - pass - - - def OnFormChange(self, fid): - if fid == self.rOptMD5.id or fid == self.rOptFile.id: - input = (self.cfg.md5sum, self.cfg.infile) - if fid == self.rOptMD5.id: - c1 = self.rOptMD5 - c2 = self.rOptFile - idx = 0 - else: - c1 = self.rOptFile - c2 = self.rOptMD5 - idx = 1 - - v = not self.GetControlValue(c1) - if v: idx = not idx - - # Uncheck the opposite input type - self.SetControlValue(c2, v) - - # Set input field depending on input type - self.SetControlValue(self.txtInput, input[idx]) - # - # Report button - # - elif fid == self.btnReport.id: - input = self.GetControlValue(self.txtInput) - as_file = self.GetControlValue(self.rOptFile) - apikey = self.GetControlValue(self.txtApiKey) - - ok, r = VtReport(self.cfg.apikey, - filename=input if as_file else None, - md5sum=None if as_file else input) - - # Error? - if not ok: - idc.Warning(r) - return 1 - - # Pass the result - self.EChooser.SetItems(r) - - # We have results and it was a file? Print its MD5 - if r and as_file: - print("%s: %s" % (vt.LAST_FILE_HASH, input)) - - # Refresh the embedded chooser control - # (Could also clear previous results if not were retrieved during this run) - self.RefreshField(self.cEChooser) - - # Store the input for the caller - self.cfg.input = input - - # No results and file as input was supplied? - if r is None: - if as_file: - # Propose to upload - if idc.AskYN(0, "HIDECANCEL\nNo previous results. Do you want to submit the file:\n\n'%s'\n\nto VirusTotal?" % input) == 0: - return 1 - - try: - r = vt.scan_file(input) - except Exception as e: - idc.Warning("Exceptio during upload: %s" % str(e)) - else: - if r is None: - idc.Warning("Failed to upload the file!") - else: - idc.Warning("File uploaded. Check again later to get the analysis report. Scan id: %s" % r) - else: - idc.Warning("No results found for hash: %s" % input) - - return 1 - - - def Show(self, cfg): - # Compile the form once - if not self.Compiled(): - _, args = self.Compile() - #print args[0] - - # Populate the form - self.txtApiKey.value = cfg.apikey - self.grpOptions.value = cfg.options - self.txtInput.value = cfg.infile if self.rOptFile.checked else cfg.md5sum - - # Remember the config - self.cfg = cfg - - # Execute the form - ok = self.Execute() - - # Forget the cfg - del self.cfg - - # Success? - if ok != 0: - # Update config - cfg.options = self.grpOptions.value - cfg.apikey = self.txtApiKey.value - - # Popup results? - if self.rOptRemember.checked: - ok = 2 - - return ok - - -# ----------------------------------------------------------------------- -class VirusTotalPlugin_t(plugin_t): - flags = idaapi.PLUGIN_UNL - comment = "VirusTotal plugin for IDA" - help = "" - wanted_name = "VirusTotal report" - wanted_hotkey = "Alt-F8" - - - def init(self): - # Some initialization - self.icon_id = 0 - return idaapi.PLUGIN_OK - - - def run(self, arg=0): - # Load icon from the memory and save its id - self.icon_id = idaapi.load_custom_icon(data=VT_ICON, format="png") - if self.icon_id == 0: - raise RuntimeError("Failed to load icon data!") - - # Create config object - cfg = VirusTotalConfig() - - # Read previous config - cfg.Read() - - # Create form - f = VirusTotalForm(self.icon_id) - - # Show the form - ok = f.Show(cfg) - if ok == 0: - f.Free() - return - - # Save configuration - cfg.Write() - - # Spawn a non-modal chooser w/ the results if any - if ok == 2 and f.EChooser.GetItems(): - VirusTotalChooser( - "VirusTotal results [%s]" % cfg.input, - f.EChooser.GetItems(), - self.icon_id).Show() - - f.Free() - return - - - def term(self): - # Free the custom icon - if self.icon_id != 0: - idaapi.free_custom_icon(self.icon_id) - - -# ----------------------------------------------------------------------- -def PLUGIN_ENTRY(): - return VirusTotalPlugin_t() - -# -------------------------------------------------------------------------- -if PLUGIN_TEST: - # Create form - f = PLUGIN_ENTRY() - f.init() - f.run() - f.term() - - +# ----------------------------------------------------------------------- +# VirusTotal IDA Plugin +# By Elias Bachaalany +# (c) Hex-Rays 2011 +# +# Special thanks: +# - VirusTotal team +# - Bryce Boe for his VirusTotal Python code +# +import idaapi +import idc +from idaapi import Choose2, plugin_t +import BboeVt as vt +import webbrowser +import urllib +import os + + +PLUGIN_TEST = 0 + +# ----------------------------------------------------------------------- +# Configuration file +VT_CFGFILE = os.path.join(idaapi.get_user_idadir(), "virustotal.cfg") + +# ----------------------------------------------------------------------- +# VirusTotal Icon in PNG format +VT_ICON = ( + "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" + "\x00\x00\x00\x10\x00\x00\x00\x10\x04\x03\x00\x00\x00\xED\xDD\xE2" + "\x52\x00\x00\x00\x30\x50\x4C\x54\x45\x03\x8B\xD3\x5C\xB4\xE3\x9C" + "\xD1\xED\xF7\xFB\xFD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xD3\xF2\x42\x61\x00\x00\x00" + "\x4B\x49\x44\x41\x54\x78\x9C\x2D\xCA\xC1\x0D\x80\x30\x0C\x43\x51" + "\x27\x2C\x50\x89\x05\x40\x2C\x40\xEB\xFD\x77\xC3\x76\xC9\xE9\xEB" + "\xC5\x20\x5F\xE8\x1A\x0F\x97\xA3\xD0\xE4\x1D\xF9\x49\xD1\x59\x29" + "\x4C\x43\x9B\xD0\x15\x01\xB5\x4A\x9C\xE4\x70\x14\x39\xB3\x31\xF8" + "\x15\x70\x04\xF4\xDA\x20\x39\x02\x8A\x0D\xA8\x0F\x94\xA7\x09\x0E" + "\xC5\x16\x2D\x54\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82") + + +# ----------------------------------------------------------------------- +class VirusTotalConfig(object): + def __init__(self): + self.Default() + + + def Default(self): + self.md5sum = GetInputMD5() + self.infile = idaapi.dbg_get_input_path() + if not self.infile: + self.infile = "" + + # Persistent options + self.apikey = "" + self.options = 1 | 2 + + + def Read(self): + """ + Read configuration from file + """ + if not os.path.exists(VT_CFGFILE): + return + f = open(VT_CFGFILE, 'r') + lines = f.readlines() + for i, line in enumerate(lines): + line = line.strip() + if i == 0: + self.apikey = line + elif i == 1: + self.options = int(line) + else: + break + + + def Write(self): + """ + Write configuration to file + """ + lines = (self.apikey.strip(), str(self.options)) + try: + f = open(VT_CFGFILE, 'w') + f.write("\n".join(lines)) + f.close() + except: + pass + + +# ----------------------------------------------------------------------- +def VtReport(apikey, filename=None, md5sum=None): + if filename is None and md5sum is None: + return (False, "No parameters passed!") + + # Check filename existance + if filename is not None and not os.path.exists(filename): + return (False, "Input file '%s' does not exist!" % filename) + + #print("fn=%s md5=%s" % (filename, md5sum)) + # Get file report from VirusTotal + try: + vt.set_apikey(apikey) + result = vt.get_file_report(filename=filename, md5sum=md5sum) + except Exception as e: + return (False, "Exception:\n%s" % str(e)) + + # Already analyzed? + if result is not None: + # Transform the results + items = [] + for av, mwname in result.items(): + mwname = str(mwname) if mwname else "n/a" + av = str(av) + items.append([av, mwname]) + result = items + + return (True, result) + + +# ----------------------------------------------------------------------- +class VirusTotalChooser(Choose2): + """ + Chooser class to display results from VT + """ + def __init__(self, title, items, icon, embedded=False): + Choose2.__init__(self, + title, + [ ["Antivirus", 20], ["Result", 40] ], + embedded=embedded) + self.items = items + self.icon = icon + + + def GetItems(self): + return self.items + + + def SetItems(self, items): + self.items = [] if items is None else items + + + def OnClose(self): + pass + + + def OnGetLine(self, n): + return self.items[n] + + + def OnGetSize(self): + return len(self.items) + + + def OnSelectLine(self, n): + # Google search for the malware name and the antivirus name + s = urllib.urlencode({"q" : " ".join(self.items[n])}) + webbrowser.open_new_tab("http://www.google.com/search?%s" % s) + + +# -------------------------------------------------------------------------- +class VirusTotalForm(Form): + def __init__(self, icon): + self.EChooser = VirusTotalChooser("E1", [], icon, embedded=True) + Form.__init__(self, r"""STARTITEM {id:txtInput} +VirusTotal - IDAPython plugin v1.0 (c) Hex-Rays + +{FormChangeCb} +<#API key#~A~pi key:{txtApiKey}> + +Options: +<#Open results in a chooser when form closes#~P~opout results on close:{rOptRemember}> +<#Use MD5 checksum#~M~D5Sum:{rOptMD5}> +<#Use file on disk#~F~ile:{rOptFile}>{grpOptions}> + +<#Type input (file or MD5 string)#~I~nput:{txtInput}> + +<#Get reports from VT#~R~eport:{btnReport}> +""", { + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'txtApiKey' : Form.StringInput(swidth=80), + 'grpOptions' : Form.ChkGroupControl(("rOptRemember", "rOptMD5", "rOptFile")), + 'txtInput' : Form.FileInput(open=True), + 'btnReport' : Form.ButtonInput(self.OnReportClick), + 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) + }) + + + + def OnReportClick(self, code=0): + pass + + + def OnFormChange(self, fid): + if fid == self.rOptMD5.id or fid == self.rOptFile.id: + input = (self.cfg.md5sum, self.cfg.infile) + if fid == self.rOptMD5.id: + c1 = self.rOptMD5 + c2 = self.rOptFile + idx = 0 + else: + c1 = self.rOptFile + c2 = self.rOptMD5 + idx = 1 + + v = not self.GetControlValue(c1) + if v: idx = not idx + + # Uncheck the opposite input type + self.SetControlValue(c2, v) + + # Set input field depending on input type + self.SetControlValue(self.txtInput, input[idx]) + # + # Report button + # + elif fid == self.btnReport.id: + input = self.GetControlValue(self.txtInput) + as_file = self.GetControlValue(self.rOptFile) + apikey = self.GetControlValue(self.txtApiKey) + + ok, r = VtReport(self.cfg.apikey, + filename=input if as_file else None, + md5sum=None if as_file else input) + + # Error? + if not ok: + idc.Warning(r) + return 1 + + # Pass the result + self.EChooser.SetItems(r) + + # We have results and it was a file? Print its MD5 + if r and as_file: + print("%s: %s" % (vt.LAST_FILE_HASH, input)) + + # Refresh the embedded chooser control + # (Could also clear previous results if not were retrieved during this run) + self.RefreshField(self.cEChooser) + + # Store the input for the caller + self.cfg.input = input + + # No results and file as input was supplied? + if r is None: + if as_file: + # Propose to upload + if idc.AskYN(0, "HIDECANCEL\nNo previous results. Do you want to submit the file:\n\n'%s'\n\nto VirusTotal?" % input) == 0: + return 1 + + try: + r = vt.scan_file(input) + except Exception as e: + idc.Warning("Exceptio during upload: %s" % str(e)) + else: + if r is None: + idc.Warning("Failed to upload the file!") + else: + idc.Warning("File uploaded. Check again later to get the analysis report. Scan id: %s" % r) + else: + idc.Warning("No results found for hash: %s" % input) + + return 1 + + + def Show(self, cfg): + # Compile the form once + if not self.Compiled(): + _, args = self.Compile() + #print args[0] + + # Populate the form + self.txtApiKey.value = cfg.apikey + self.grpOptions.value = cfg.options + self.txtInput.value = cfg.infile if self.rOptFile.checked else cfg.md5sum + + # Remember the config + self.cfg = cfg + + # Execute the form + ok = self.Execute() + + # Forget the cfg + del self.cfg + + # Success? + if ok != 0: + # Update config + cfg.options = self.grpOptions.value + cfg.apikey = self.txtApiKey.value + + # Popup results? + if self.rOptRemember.checked: + ok = 2 + + return ok + + +# ----------------------------------------------------------------------- +class VirusTotalPlugin_t(plugin_t): + flags = idaapi.PLUGIN_UNL + comment = "VirusTotal plugin for IDA" + help = "" + wanted_name = "VirusTotal report" + wanted_hotkey = "Alt-F8" + + + def init(self): + # Some initialization + self.icon_id = 0 + return idaapi.PLUGIN_OK + + + def run(self, arg=0): + # Load icon from the memory and save its id + self.icon_id = idaapi.load_custom_icon(data=VT_ICON, format="png") + if self.icon_id == 0: + raise RuntimeError("Failed to load icon data!") + + # Create config object + cfg = VirusTotalConfig() + + # Read previous config + cfg.Read() + + # Create form + f = VirusTotalForm(self.icon_id) + + # Show the form + ok = f.Show(cfg) + if ok == 0: + f.Free() + return + + # Save configuration + cfg.Write() + + # Spawn a non-modal chooser w/ the results if any + if ok == 2 and f.EChooser.GetItems(): + VirusTotalChooser( + "VirusTotal results [%s]" % cfg.input, + f.EChooser.GetItems(), + self.icon_id).Show() + + f.Free() + return + + + def term(self): + # Free the custom icon + if self.icon_id != 0: + idaapi.free_custom_icon(self.icon_id) + + +# ----------------------------------------------------------------------- +def PLUGIN_ENTRY(): + return VirusTotalPlugin_t() + +# -------------------------------------------------------------------------- +if PLUGIN_TEST: + # Create form + f = PLUGIN_ENTRY() + f.init() + f.run() + f.term() + + diff --git a/Scripts/msdnapihelp.py b/Scripts/msdnapihelp.py index 08117e34..8a90d602 100644 --- a/Scripts/msdnapihelp.py +++ b/Scripts/msdnapihelp.py @@ -1,68 +1,68 @@ -""" -User contributed script: MSDN API HELP plugin - -This script fetches the API reference (from MSDN) of a given highlighted identifier -and returns the results in a new web browser page. - -This script depends on the feedparser package: http://code.google.com/p/feedparser/ - -10/05/2010 -- initial version - - -""" - -import idaapi - -# ----------------------------------------------------------------------- -class msdnapihelp_plugin_t(idaapi.plugin_t): - flags = idaapi.PLUGIN_UNL - comment = "Online MSDN API Help" - help = "Help me" - wanted_name = "MSDN API Help" - wanted_hotkey = "F3" - - def init(self): - return idaapi.PLUGIN_OK - - - @staticmethod - def sanitize_name(name): - t = idaapi.FUNC_IMPORT_PREFIX - if name.startswith(t): - return name[len(t):] - return name - - - def run(self, arg): - # Get the highlighted identifier - id = idaapi.get_highlighted_identifier() - if not id: - print "No identifier was highlighted" - return - - import webbrowser - - try: - import feedparser - except: - idaapi.warning('Feedparser package not installed') - return - - id = self.sanitize_name(id) - print "Looking up '%s' in MSDN online" % id - d = feedparser.parse("http://social.msdn.microsoft.com/Search/Feed.aspx?locale=en-us&format=RSS&Query=%s" % id) - if len(d['entries']) > 0: - url = d['entries'][0].link - webbrowser.open_new_tab(url) - else: - print "API documentation not found for: %s" % id - - - def term(self): - pass - - -# ----------------------------------------------------------------------- -def PLUGIN_ENTRY(): - return msdnapihelp_plugin_t() +""" +User contributed script: MSDN API HELP plugin + +This script fetches the API reference (from MSDN) of a given highlighted identifier +and returns the results in a new web browser page. + +This script depends on the feedparser package: http://code.google.com/p/feedparser/ + +10/05/2010 +- initial version + + +""" + +import idaapi + +# ----------------------------------------------------------------------- +class msdnapihelp_plugin_t(idaapi.plugin_t): + flags = idaapi.PLUGIN_UNL + comment = "Online MSDN API Help" + help = "Help me" + wanted_name = "MSDN API Help" + wanted_hotkey = "F3" + + def init(self): + return idaapi.PLUGIN_OK + + + @staticmethod + def sanitize_name(name): + t = idaapi.FUNC_IMPORT_PREFIX + if name.startswith(t): + return name[len(t):] + return name + + + def run(self, arg): + # Get the highlighted identifier + id = idaapi.get_highlighted_identifier() + if not id: + print "No identifier was highlighted" + return + + import webbrowser + + try: + import feedparser + except: + idaapi.warning('Feedparser package not installed') + return + + id = self.sanitize_name(id) + print "Looking up '%s' in MSDN online" % id + d = feedparser.parse("http://social.msdn.microsoft.com/Search/Feed.aspx?locale=en-us&format=RSS&Query=%s" % id) + if len(d['entries']) > 0: + url = d['entries'][0].link + webbrowser.open_new_tab(url) + else: + print "API documentation not found for: %s" % id + + + def term(self): + pass + + +# ----------------------------------------------------------------------- +def PLUGIN_ENTRY(): + return msdnapihelp_plugin_t() diff --git a/basetsd.h b/basetsd.h index be25793f..5fdca7ce 100644 --- a/basetsd.h +++ b/basetsd.h @@ -1,4 +1,4 @@ -#ifndef _BASETSD_H -#define _BASETSD_H -/* Microsoft free compilers seem to lack this file and Python needs it */ -#endif +#ifndef _BASETSD_H +#define _BASETSD_H +/* Microsoft free compilers seem to lack this file and Python needs it */ +#endif diff --git a/build.py b/build.py index f0006e42..46ac3ec7 100644 --- a/build.py +++ b/build.py @@ -24,7 +24,7 @@ VERBOSE = True IDA_MAJOR_VERSION = 6 -IDA_MINOR_VERSION = 7 +IDA_MINOR_VERSION = 8 if 'IDA' in os.environ: IDA_SDK = os.environ['IDA'] @@ -40,7 +40,7 @@ # IDAPython version VERSION_MAJOR = 1 VERSION_MINOR = 7 -VERSION_PATCH = 1 +VERSION_PATCH = 0 # Determine Python version PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) diff --git a/docs/notes.txt b/docs/notes.txt index e1dbf0ac..ab893478 100644 --- a/docs/notes.txt +++ b/docs/notes.txt @@ -1,53 +1,53 @@ -Assorted notes --------------- - -Wrapped functions and constants: - -All the symbols from the idaapi module are listed in symbollist.txt. -Documentation for the plugin API functions functions is in the IDA -SDK header files. All function and symbol names directly translate -to the C++ counterparts. If you try to use a function that is not -wrapped yet you will get an exception like this: - - Traceback (most recent call last): - File "", line 1, in ? - NameError: name 'foobar' is not defined - -If this happens you can check the function in symbollist.txt. If it -is not included and it should be please report it to the author. - - -Data types: - -All the C++ data types are mapped to corresponding Python data types. -For example ea_t maps to a Python integer. Complex data types (like -structures and classes) are mapped to Python classes that have the -same attributes as the original type. - - -Arguments and return values: - -Generally all function arguments should be the same type as specified -by the original headers. Pointers to complex types (structures, classes) -are checked and must match the original declarations. - -For example comment = get_func_comment("aa", 0) will raise an exception: - - Traceback (most recent call last): - File "", line 1, in ? - TypeError: Type error. Got aa, expected _p_func_t - -When calling functions that return a string in a buffer (usually with -maximum size) the buffer and size parameter is omitted. These functions -return either the result in a string or None if the call fails and returns -NULL. The output buffers are maximized at MAXSTR. - - Example: - - C++: get_func_name(0x1234, buf, sizeof(buf)); - Python: name = get_func_name(0x1234) - -Any function that should return a char * is going to return either a -Python string (up to MAXSTR) or None. - - +Assorted notes +-------------- + +Wrapped functions and constants: + +All the symbols from the idaapi module are listed in symbollist.txt. +Documentation for the plugin API functions functions is in the IDA +SDK header files. All function and symbol names directly translate +to the C++ counterparts. If you try to use a function that is not +wrapped yet you will get an exception like this: + + Traceback (most recent call last): + File "", line 1, in ? + NameError: name 'foobar' is not defined + +If this happens you can check the function in symbollist.txt. If it +is not included and it should be please report it to the author. + + +Data types: + +All the C++ data types are mapped to corresponding Python data types. +For example ea_t maps to a Python integer. Complex data types (like +structures and classes) are mapped to Python classes that have the +same attributes as the original type. + + +Arguments and return values: + +Generally all function arguments should be the same type as specified +by the original headers. Pointers to complex types (structures, classes) +are checked and must match the original declarations. + +For example comment = get_func_comment("aa", 0) will raise an exception: + + Traceback (most recent call last): + File "", line 1, in ? + TypeError: Type error. Got aa, expected _p_func_t + +When calling functions that return a string in a buffer (usually with +maximum size) the buffer and size parameter is omitted. These functions +return either the result in a string or None if the call fails and returns +NULL. The output buffers are maximized at MAXSTR. + + Example: + + C++: get_func_name(0x1234, buf, sizeof(buf)); + Python: name = get_func_name(0x1234) + +Any function that should return a char * is going to return either a +Python string (up to MAXSTR) or None. + + diff --git a/examples/chooser.py b/examples/chooser.py index de3a5405..b4590630 100644 --- a/examples/chooser.py +++ b/examples/chooser.py @@ -1,50 +1,50 @@ -#--------------------------------------------------------------------- -# Chooser test -# -# This script demonstrates the usage of the class-based chooser. -# -# Author: Gergely Erdelyi -#--------------------------------------------------------------------- -from idaapi import Choose - -# -# Modal chooser -# - -# Get a modal Choose instance -chooser = Choose([], "MyChooser", 1) -# List to choose from -chooser.list = [ "First", "Second", "Third" ] -# Set the width -chooser.width = 50 -# Run the chooser -ch = chooser.choose() -# Print the results -if ch > 0: - print "You chose %d which is %s" % (ch, chooser.list[ch-1]) -else: - print "Escape from chooser" - -# -# Normal chooser -# -class MyChoose(Choose): - """ - You have to subclass Chooser to override the enter() method - """ - def __init__(self, list=[], name="Choose"): - Choose.__init__(self, list, name) - # Set the width - self.width = 50 - self.deflt = 1 - - def enter(self, n): - print "Enter called. Do some stuff here." - print "The chosen item is %d = %s" % (n, self.list[n-1]) - print "Now press ESC to leave." - -# Get a Choose instance -chooser = MyChoose([ "First", "Second", "Third" ], "MyChoose") - -# Run the chooser -ch = chooser.choose() +#--------------------------------------------------------------------- +# Chooser test +# +# This script demonstrates the usage of the class-based chooser. +# +# Author: Gergely Erdelyi +#--------------------------------------------------------------------- +from idaapi import Choose + +# +# Modal chooser +# + +# Get a modal Choose instance +chooser = Choose([], "MyChooser", 1) +# List to choose from +chooser.list = [ "First", "Second", "Third" ] +# Set the width +chooser.width = 50 +# Run the chooser +ch = chooser.choose() +# Print the results +if ch > 0: + print "You chose %d which is %s" % (ch, chooser.list[ch-1]) +else: + print "Escape from chooser" + +# +# Normal chooser +# +class MyChoose(Choose): + """ + You have to subclass Chooser to override the enter() method + """ + def __init__(self, list=[], name="Choose"): + Choose.__init__(self, list, name) + # Set the width + self.width = 50 + self.deflt = 1 + + def enter(self, n): + print "Enter called. Do some stuff here." + print "The chosen item is %d = %s" % (n, self.list[n-1]) + print "Now press ESC to leave." + +# Get a Choose instance +chooser = MyChoose([ "First", "Second", "Third" ], "MyChoose") + +# Run the chooser +ch = chooser.choose() diff --git a/examples/colours.py b/examples/colours.py index 90f50ae9..49f0ce2a 100644 --- a/examples/colours.py +++ b/examples/colours.py @@ -1,19 +1,19 @@ -#--------------------------------------------------------------------- -# Colour test -# -# This script demonstrates the usage of background colours. -# -# Author: Gergely Erdelyi -#--------------------------------------------------------------------- - -# Set the colour of the current segment to BLUE -SetColor(here(), CIC_SEGM, 0xc02020) -# Set the colour of the current function to GREEN -SetColor(here(), CIC_FUNC, 0x208020) -# Set the colour of the current item to RED -SetColor(here(), CIC_ITEM, 0x2020c0) - -# Print the colours just set -print "%x" % GetColor(here(), CIC_SEGM) -print "%x" % GetColor(here(), CIC_FUNC) -print "%x" % GetColor(here(), CIC_ITEM) +#--------------------------------------------------------------------- +# Colour test +# +# This script demonstrates the usage of background colours. +# +# Author: Gergely Erdelyi +#--------------------------------------------------------------------- + +# Set the colour of the current segment to BLUE +SetColor(here(), CIC_SEGM, 0xc02020) +# Set the colour of the current function to GREEN +SetColor(here(), CIC_FUNC, 0x208020) +# Set the colour of the current item to RED +SetColor(here(), CIC_ITEM, 0x2020c0) + +# Print the colours just set +print "%x" % GetColor(here(), CIC_SEGM) +print "%x" % GetColor(here(), CIC_FUNC) +print "%x" % GetColor(here(), CIC_ITEM) diff --git a/examples/debughook.py b/examples/debughook.py index acc4feab..d47f98d9 100644 --- a/examples/debughook.py +++ b/examples/debughook.py @@ -1,107 +1,107 @@ -#--------------------------------------------------------------------- -# 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 -# -# Maintained By: IDAPython Team -# -#--------------------------------------------------------------------- -from idaapi import * - -class MyDbgHook(DBG_Hooks): - """ Own debug hook class that implementd the callback functions """ - - def dbg_process_start(self, pid, tid, ea, name, base, size): - print("Process started, pid=%d tid=%d name=%s" % (pid, tid, name)) - - def dbg_process_exit(self, pid, tid, ea, code): - print("Process exited pid=%d tid=%d ea=0x%x code=%d" % (pid, tid, ea, code)) - - def dbg_library_unload(self, pid, tid, ea, info): - print("Library unloaded: pid=%d tid=%d ea=0x%x info=%s" % (pid, tid, ea, info)) - return 0 - - def dbg_process_attach(self, pid, tid, ea, name, base, size): - print("Process attach pid=%d tid=%d ea=0x%x name=%s base=%x size=%x" % (pid, tid, ea, name, base, size)) - - def dbg_process_detach(self, pid, tid, ea): - print("Process detached, pid=%d tid=%d ea=0x%x" % (pid, tid, ea)) - return 0 - - def dbg_library_load(self, pid, tid, ea, name, base, size): - print "Library loaded: pid=%d tid=%d name=%s base=%x" % (pid, tid, name, base) - - def dbg_bpt(self, tid, ea): - print "Break point at 0x%x pid=%d" % (ea, tid) - # return values: - # -1 - to display a breakpoint warning dialog - # if the process is suspended. - # 0 - to never display a breakpoint warning dialog. - # 1 - to always display a breakpoint warning dialog. - return 0 - - def dbg_suspend_process(self): - print "Process suspended" - - def dbg_exception(self, pid, tid, ea, exc_code, exc_can_cont, exc_ea, exc_info): - print("Exception: pid=%d tid=%d ea=0x%x exc_code=0x%x can_continue=%d exc_ea=0x%x exc_info=%s" % ( - pid, tid, ea, exc_code & idaapi.BADADDR, exc_can_cont, exc_ea, exc_info)) - # return values: - # -1 - to display an exception warning dialog - # if the process is suspended. - # 0 - to never display an exception warning dialog. - # 1 - to always display an exception warning dialog. - return 0 - - def dbg_trace(self, tid, ea): - print("Trace tid=%d ea=0x%x" % (tid, ea)) - # return values: - # 1 - do not log this trace event; - # 0 - log it - return 0 - - def dbg_step_into(self): - print("Step into") - self.dbg_step_over() - - def dbg_run_to(self, pid, tid=0, ea=0): - print "Runto: tid=%d" % tid - idaapi.continue_process() - - - def dbg_step_over(self): - eip = GetRegValue("EIP") - print("0x%x %s" % (eip, GetDisasm(eip))) - - self.steps += 1 - if self.steps >= 5: - request_exit_process() - else: - request_step_over() - - -# Remove an existing debug hook -try: - if debughook: - print("Removing previous hook ...") - debughook.unhook() -except: - pass - -# Install the debug hook -debughook = MyDbgHook() -debughook.hook() -debughook.steps = 0 - -# Stop at the entry point -ep = GetLongPrm(INF_START_IP) -request_run_to(ep) - -# Step one instruction -request_step_over() - -# Start debugging -run_requests() +#--------------------------------------------------------------------- +# 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 +# +# Maintained By: IDAPython Team +# +#--------------------------------------------------------------------- +from idaapi import * + +class MyDbgHook(DBG_Hooks): + """ Own debug hook class that implementd the callback functions """ + + def dbg_process_start(self, pid, tid, ea, name, base, size): + print("Process started, pid=%d tid=%d name=%s" % (pid, tid, name)) + + def dbg_process_exit(self, pid, tid, ea, code): + print("Process exited pid=%d tid=%d ea=0x%x code=%d" % (pid, tid, ea, code)) + + def dbg_library_unload(self, pid, tid, ea, info): + print("Library unloaded: pid=%d tid=%d ea=0x%x info=%s" % (pid, tid, ea, info)) + return 0 + + def dbg_process_attach(self, pid, tid, ea, name, base, size): + print("Process attach pid=%d tid=%d ea=0x%x name=%s base=%x size=%x" % (pid, tid, ea, name, base, size)) + + def dbg_process_detach(self, pid, tid, ea): + print("Process detached, pid=%d tid=%d ea=0x%x" % (pid, tid, ea)) + return 0 + + def dbg_library_load(self, pid, tid, ea, name, base, size): + print "Library loaded: pid=%d tid=%d name=%s base=%x" % (pid, tid, name, base) + + def dbg_bpt(self, tid, ea): + print "Break point at 0x%x pid=%d" % (ea, tid) + # return values: + # -1 - to display a breakpoint warning dialog + # if the process is suspended. + # 0 - to never display a breakpoint warning dialog. + # 1 - to always display a breakpoint warning dialog. + return 0 + + def dbg_suspend_process(self): + print "Process suspended" + + def dbg_exception(self, pid, tid, ea, exc_code, exc_can_cont, exc_ea, exc_info): + print("Exception: pid=%d tid=%d ea=0x%x exc_code=0x%x can_continue=%d exc_ea=0x%x exc_info=%s" % ( + pid, tid, ea, exc_code & idaapi.BADADDR, exc_can_cont, exc_ea, exc_info)) + # return values: + # -1 - to display an exception warning dialog + # if the process is suspended. + # 0 - to never display an exception warning dialog. + # 1 - to always display an exception warning dialog. + return 0 + + def dbg_trace(self, tid, ea): + print("Trace tid=%d ea=0x%x" % (tid, ea)) + # return values: + # 1 - do not log this trace event; + # 0 - log it + return 0 + + def dbg_step_into(self): + print("Step into") + self.dbg_step_over() + + def dbg_run_to(self, pid, tid=0, ea=0): + print "Runto: tid=%d" % tid + idaapi.continue_process() + + + def dbg_step_over(self): + eip = GetRegValue("EIP") + print("0x%x %s" % (eip, GetDisasm(eip))) + + self.steps += 1 + if self.steps >= 5: + request_exit_process() + else: + request_step_over() + + +# Remove an existing debug hook +try: + if debughook: + print("Removing previous hook ...") + debughook.unhook() +except: + pass + +# Install the debug hook +debughook = MyDbgHook() +debughook.hook() +debughook.steps = 0 + +# Stop at the entry point +ep = GetLongPrm(INF_START_IP) +request_run_to(ep) + +# Step one instruction +request_step_over() + +# Start debugging +run_requests() diff --git a/examples/ex1.idc b/examples/ex1.idc index a0916d3f..e3c0aa76 100644 --- a/examples/ex1.idc +++ b/examples/ex1.idc @@ -1,35 +1,35 @@ -// -// Reference Lister -// -// List all functions and all references to them in the current section. -// -// Implemented in IDC -// -#include - -static main() -{ - auto ea, func, ref; - - // Get current ea - ea = ScreenEA(); - - // Loop from start to end in the current segment - for (func=SegStart(ea); - func != BADADDR && func < SegEnd(ea); - func=NextFunction(func)) - { - // If the current address is function process it - if (GetFunctionFlags(func) != -1) - { - Message("Function %s at 0x%x\n", GetFunctionName(func), func); - - // Find all code references to func - for (ref=RfirstB(func); ref != BADADDR; ref=RnextB(func, ref)) - { - Message(" called from %s(0x%x)\n", GetFunctionName(ref), ref); - } - - } - } -} +// +// Reference Lister +// +// List all functions and all references to them in the current section. +// +// Implemented in IDC +// +#include + +static main() +{ + auto ea, func, ref; + + // Get current ea + ea = ScreenEA(); + + // Loop from start to end in the current segment + for (func=SegStart(ea); + func != BADADDR && func < SegEnd(ea); + func=NextFunction(func)) + { + // If the current address is function process it + if (GetFunctionFlags(func) != -1) + { + Message("Function %s at 0x%x\n", GetFunctionName(func), func); + + // Find all code references to func + for (ref=RfirstB(func); ref != BADADDR; ref=RnextB(func, ref)) + { + Message(" called from %s(0x%x)\n", GetFunctionName(ref), ref); + } + + } + } +} diff --git a/examples/ex_askusingform.py b/examples/ex_askusingform.py index 61ba5733..04f7b780 100644 --- a/examples/ex_askusingform.py +++ b/examples/ex_askusingform.py @@ -1,379 +1,379 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to use the Form class -# (c) Hex-Rays -# -from idaapi import Form - -# -# -------------------------------------------------------------------------- -class TestEmbeddedChooserClass(Choose2): - """ - A simple chooser to be used as an embedded chooser - """ - def __init__(self, title, nb = 5, flags=0): - Choose2.__init__(self, - title, - [ ["Address", 10], ["Name", 30] ], - embedded=True, width=30, height=20, flags=flags) - self.n = 0 - self.items = [ self.make_item() for x in xrange(0, nb+1) ] - self.icon = 5 - self.selcount = 0 - - def make_item(self): - r = [str(self.n), "func_%04d" % self.n] - self.n += 1 - return r - - def OnClose(self): - pass - - def OnGetLine(self, n): - print("getline %d" % n) - return self.items[n] - - def OnGetSize(self): - n = len(self.items) - print("getsize -> %d" % n) - return n - -# -------------------------------------------------------------------------- -class MyForm(Form): - def __init__(self): - self.invert = False - self.EChooser = TestEmbeddedChooserClass("E1", flags=Choose2.CH_MULTI) - Form.__init__(self, r"""STARTITEM {id:rNormal} -BUTTON YES* Yeah -BUTTON NO Nope -BUTTON CANCEL Nevermind -Form Test - -{FormChangeCb} -This is a string: +{cStr1}+ -This is an address: +{cAddr1}+ - -Escape\{control} -This is a string: '{cStr2}' -This is a number: {cVal1} - -<#Hint1#Enter name:{iStr1}> -<#Hint2#Select color:{iColor1}> -Browse test -<#Select a file to open#Browse to open:{iFileOpen}> -<#Select a file to save#Browse to save:{iFileSave}> -<#Select dir#Browse for dir:{iDir}> -Type -<#Select type#Write a type:{iType}> -Numbers -<##Enter a selector value:{iSegment}> -<##Enter a raw hex:{iRawHex}> -<##Enter a character:{iChar}> -<##Enter an address:{iAddr}> -Button test -<##Button1:{iButton1}> <##Button2:{iButton2}> - -Check boxes: - - -{cGroup1}> - -Radio boxes: - - -{cGroup2}> - -The end! -""", { - 'cStr1': Form.StringLabel("Hello"), - 'cStr2': Form.StringLabel("StringTest"), - 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR), - 'cVal1' : Form.NumericLabel(99, Form.FT_HEX), - 'iStr1': Form.StringInput(), - 'iColor1': Form.ColorInput(), - 'iFileOpen': Form.FileInput(open=True), - 'iFileSave': Form.FileInput(save=True), - 'iDir': Form.DirInput(), - 'iType': Form.StringInput(tp=Form.FT_TYPE), - 'iSegment': Form.NumericInput(tp=Form.FT_SEG), - 'iRawHex': Form.NumericInput(tp=Form.FT_RAWHEX), - 'iAddr': Form.NumericInput(tp=Form.FT_ADDR), - 'iChar': Form.NumericInput(tp=Form.FT_CHAR), - 'iButton1': Form.ButtonInput(self.OnButton1), - 'iButton2': Form.ButtonInput(self.OnButton2), - 'cGroup1': Form.ChkGroupControl(("rNormal", "rError", "rWarnings")), - 'cGroup2': Form.RadGroupControl(("rRed", "rGreen", "rBlue")), - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) - }) - - - def OnButton1(self, code=0): - print("Button1 pressed") - - - def OnButton2(self, code=0): - print("Button2 pressed") - - - def OnFormChange(self, fid): - if fid == self.iButton1.id: - print("Button1 fchg;inv=%s" % self.invert) - self.SetFocusedField(self.rNormal) - self.EnableField(self.rError, self.invert) - self.invert = not self.invert - elif fid == self.iButton2.id: - g1 = self.GetControlValue(self.cGroup1) - g2 = self.GetControlValue(self.cGroup2) - d = self.GetControlValue(self.iDir) - f = self.GetControlValue(self.iFileOpen) - print("cGroup2:%x;Dir=%s;fopen=%s;cGroup1:%x" % (g1, d, f, g2)) - elif fid == self.cEChooser.id: - l = self.GetControlValue(self.cEChooser) - print("Chooser: %s" % l) - else: - print(">>fid:%d" % fid) - return 1 - - - -# -------------------------------------------------------------------------- -def stdalone_main(): - f = MyForm() - f, args = f.Compile() - print args[0] - print args[1:] - f.rNormal.checked = True - f.rWarnings.checked = True - print hex(f.cGroup1.value) - - f.rGreen.selected = True - print f.cGroup2.value - print "Title: '%s'" % f.title - - f.Free() - -# -------------------------------------------------------------------------- -def ida_main(): - # Create form - global f - f = MyForm() - - # Compile (in order to populate the controls) - f.Compile() - - f.iColor1.value = 0x5bffff - f.iDir.value = os.getcwd() - f.rNormal.checked = True - f.rWarnings.checked = True - f.rGreen.selected = True - f.iStr1.value = "Hello" - f.iFileSave.value = "*.*" - f.iFileOpen.value = "*.*" - # Execute the form - ok = f.Execute() - print("r=%d" % ok) - if ok == 1: - print("f.str1=%s" % f.iStr1.value) - print("f.color1=%x" % f.iColor1.value) - print("f.openfile=%s" % f.iFileOpen.value) - print("f.savefile=%s" % f.iFileSave.value) - print("f.dir=%s" % f.iDir.value) - print("f.type=%s" % f.iType.value) - print("f.seg=%s" % f.iSegment.value) - print("f.rawhex=%x" % f.iRawHex.value) - print("f.char=%x" % f.iChar.value) - print("f.addr=%x" % f.iAddr.value) - print("f.cGroup1=%x" % f.cGroup1.value) - print("f.cGroup2=%x" % f.cGroup2.value) - - sel = f.EChooser.GetEmbSelection() - if sel is None: - print("No selection") - else: - print("Selection: %s" % sel) - - # Dispose the form - f.Free() - -# -------------------------------------------------------------------------- -def ida_main_legacy(): - # Here we simply show how to use the old style form format using Python - - # Sample form from kernwin.hpp - s = """Sample dialog box - - -This is sample dialog box for %A -using address %$ - -<~E~nter value:N:32:16::> -""" - - # Use either StringArgument or NumericArgument to pass values to the function - num = Form.NumericArgument('N', value=123) - ok = idaapi.AskUsingForm(s, - Form.StringArgument("PyAskUsingForm").arg, - Form.NumericArgument('$', 0x401000).arg, - num.arg) - if ok == 1: - print("You entered: %x" % num.value) - -# -------------------------------------------------------------------------- -def test_multilinetext_legacy(): - # Here we text the multi line text control in legacy mode - - # Sample form from kernwin.hpp - s = """Sample dialog box - -This is sample dialog box - -""" - # Use either StringArgument or NumericArgument to pass values to the function - ti = textctrl_info_t("Some initial value") - ok = idaapi.AskUsingForm(s, pointer(c_void_p.from_address(ti.clink_ptr))) - if ok == 1: - print("You entered: %s" % ti.text) - - del ti - -# -------------------------------------------------------------------------- -class MyForm2(Form): - """Simple Form to test multilinetext and combo box controls""" - def __init__(self): - Form.__init__(self, r"""STARTITEM 0 -BUTTON YES* Yeah -BUTTON NO Nope -BUTTON CANCEL NONE -Form Test - -{FormChangeCb} - -""", { - 'txtMultiLineText': Form.MultiLineTextControl(text="Hello"), - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - }) - - - def OnFormChange(self, fid): - if fid == self.txtMultiLineText.id: - pass - elif fid == -2: - ti = self.GetControlValue(self.txtMultiLineText) - print "ti.text = %s" % ti.text - else: - print(">>fid:%d" % fid) - return 1 - -# -------------------------------------------------------------------------- -def test_multilinetext(execute=True): - """Test the multilinetext and combobox controls""" - f = MyForm2() - f, args = f.Compile() - if execute: - ok = f.Execute() - else: - print args[0] - print args[1:] - ok = 0 - - if ok == 1: - assert f.txtMultiLineText.text == f.txtMultiLineText.value - print f.txtMultiLineText.text - - f.Free() - -# -------------------------------------------------------------------------- -class MyForm3(Form): - """Simple Form to test multilinetext and combo box controls""" - def __init__(self): - self.__n = 0 - Form.__init__(self, -r"""BUTTON YES* Yeah -BUTTON NO Nope -BUTTON CANCEL NONE -Dropdown list test - -{FormChangeCb} - - -""", { - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - 'cbReadonly': Form.DropdownListControl( - items=["red", "green", "blue"], - readonly=True, - selval=1), - 'cbEditable': Form.DropdownListControl( - items=["1MB", "2MB", "3MB", "4MB"], - readonly=False, - selval="4MB"), - 'iButtonAddelement': Form.ButtonInput(self.OnButtonNop), - 'iButtonSetIndex': Form.ButtonInput(self.OnButtonNop), - 'iButtonSetString': Form.ButtonInput(self.OnButtonNop), - }) - - - def OnButtonNop(self, code=0): - """Do nothing, we will handle events in the form callback""" - pass - - def OnFormChange(self, fid): - if fid == self.iButtonSetString.id: - s = idc.AskStr("none", "Enter value") - if s: - self.SetControlValue(self.cbEditable, s) - elif fid == self.iButtonSetIndex.id: - s = idc.AskStr("1", "Enter index value:") - if s: - try: - i = int(s) - except: - i = 0 - self.SetControlValue(self.cbReadonly, i) - elif fid == self.iButtonAddelement.id: - # add a value to the string list - self.__n += 1 - self.cbReadonly.add("some text #%d" % self.__n) - # Refresh the control - self.RefreshField(self.cbReadonly) - elif fid == -2: - s = self.GetControlValue(self.cbEditable) - print "user entered: %s" % s - sel_idx = self.GetControlValue(self.cbReadonly) - - return 1 - -# -------------------------------------------------------------------------- -def test_dropdown(execute=True): - """Test the combobox controls, in a modal dialog""" - f = MyForm3() - f, args = f.Compile() - if execute: - ok = f.Execute() - else: - print args[0] - print args[1:] - ok = 0 - - if ok == 1: - print "Editable: %s" % f.cbEditable.value - print "Readonly: %s" % f.cbReadonly.value - - f.Free() - -# -------------------------------------------------------------------------- -tdn_form = None -def test_dropdown_nomodal(): - """Test the combobox controls, in a non-modal form""" - global tdn_form - if tdn_form is None: - tdn_form = MyForm3() - tdn_form.modal = False - tdn_form.openform_flags = idaapi.PluginForm.FORM_TAB - tdn_form, _ = tdn_form.Compile() - tdn_form.Open() - -# - - -# -------------------------------------------------------------------------- -ida_main() \ No newline at end of file +# ----------------------------------------------------------------------- +# This is an example illustrating how to use the Form class +# (c) Hex-Rays +# +from idaapi import Form + +# +# -------------------------------------------------------------------------- +class TestEmbeddedChooserClass(Choose2): + """ + A simple chooser to be used as an embedded chooser + """ + def __init__(self, title, nb = 5, flags=0): + Choose2.__init__(self, + title, + [ ["Address", 10], ["Name", 30] ], + embedded=True, width=30, height=20, flags=flags) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnClose(self): + pass + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + print("getsize -> %d" % n) + return n + +# -------------------------------------------------------------------------- +class MyForm(Form): + def __init__(self): + self.invert = False + self.EChooser = TestEmbeddedChooserClass("E1", flags=Choose2.CH_MULTI) + Form.__init__(self, r"""STARTITEM {id:rNormal} +BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL Nevermind +Form Test + +{FormChangeCb} +This is a string: +{cStr1}+ +This is an address: +{cAddr1}+ + +Escape\{control} +This is a string: '{cStr2}' +This is a number: {cVal1} + +<#Hint1#Enter name:{iStr1}> +<#Hint2#Select color:{iColor1}> +Browse test +<#Select a file to open#Browse to open:{iFileOpen}> +<#Select a file to save#Browse to save:{iFileSave}> +<#Select dir#Browse for dir:{iDir}> +Type +<#Select type#Write a type:{iType}> +Numbers +<##Enter a selector value:{iSegment}> +<##Enter a raw hex:{iRawHex}> +<##Enter a character:{iChar}> +<##Enter an address:{iAddr}> +Button test +<##Button1:{iButton1}> <##Button2:{iButton2}> + +Check boxes: + + +{cGroup1}> + +Radio boxes: + + +{cGroup2}> + +The end! +""", { + 'cStr1': Form.StringLabel("Hello"), + 'cStr2': Form.StringLabel("StringTest"), + 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR), + 'cVal1' : Form.NumericLabel(99, Form.FT_HEX), + 'iStr1': Form.StringInput(), + 'iColor1': Form.ColorInput(), + 'iFileOpen': Form.FileInput(open=True), + 'iFileSave': Form.FileInput(save=True), + 'iDir': Form.DirInput(), + 'iType': Form.StringInput(tp=Form.FT_TYPE), + 'iSegment': Form.NumericInput(tp=Form.FT_SEG), + 'iRawHex': Form.NumericInput(tp=Form.FT_RAWHEX), + 'iAddr': Form.NumericInput(tp=Form.FT_ADDR), + 'iChar': Form.NumericInput(tp=Form.FT_CHAR), + 'iButton1': Form.ButtonInput(self.OnButton1), + 'iButton2': Form.ButtonInput(self.OnButton2), + 'cGroup1': Form.ChkGroupControl(("rNormal", "rError", "rWarnings")), + 'cGroup2': Form.RadGroupControl(("rRed", "rGreen", "rBlue")), + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) + }) + + + def OnButton1(self, code=0): + print("Button1 pressed") + + + def OnButton2(self, code=0): + print("Button2 pressed") + + + def OnFormChange(self, fid): + if fid == self.iButton1.id: + print("Button1 fchg;inv=%s" % self.invert) + self.SetFocusedField(self.rNormal) + self.EnableField(self.rError, self.invert) + self.invert = not self.invert + elif fid == self.iButton2.id: + g1 = self.GetControlValue(self.cGroup1) + g2 = self.GetControlValue(self.cGroup2) + d = self.GetControlValue(self.iDir) + f = self.GetControlValue(self.iFileOpen) + print("cGroup2:%x;Dir=%s;fopen=%s;cGroup1:%x" % (g1, d, f, g2)) + elif fid == self.cEChooser.id: + l = self.GetControlValue(self.cEChooser) + print("Chooser: %s" % l) + else: + print(">>fid:%d" % fid) + return 1 + + + +# -------------------------------------------------------------------------- +def stdalone_main(): + f = MyForm() + f, args = f.Compile() + print args[0] + print args[1:] + f.rNormal.checked = True + f.rWarnings.checked = True + print hex(f.cGroup1.value) + + f.rGreen.selected = True + print f.cGroup2.value + print "Title: '%s'" % f.title + + f.Free() + +# -------------------------------------------------------------------------- +def ida_main(): + # Create form + global f + f = MyForm() + + # Compile (in order to populate the controls) + f.Compile() + + f.iColor1.value = 0x5bffff + f.iDir.value = os.getcwd() + f.rNormal.checked = True + f.rWarnings.checked = True + f.rGreen.selected = True + f.iStr1.value = "Hello" + f.iFileSave.value = "*.*" + f.iFileOpen.value = "*.*" + # Execute the form + ok = f.Execute() + print("r=%d" % ok) + if ok == 1: + print("f.str1=%s" % f.iStr1.value) + print("f.color1=%x" % f.iColor1.value) + print("f.openfile=%s" % f.iFileOpen.value) + print("f.savefile=%s" % f.iFileSave.value) + print("f.dir=%s" % f.iDir.value) + print("f.type=%s" % f.iType.value) + print("f.seg=%s" % f.iSegment.value) + print("f.rawhex=%x" % f.iRawHex.value) + print("f.char=%x" % f.iChar.value) + print("f.addr=%x" % f.iAddr.value) + print("f.cGroup1=%x" % f.cGroup1.value) + print("f.cGroup2=%x" % f.cGroup2.value) + + sel = f.EChooser.GetEmbSelection() + if sel is None: + print("No selection") + else: + print("Selection: %s" % sel) + + # Dispose the form + f.Free() + +# -------------------------------------------------------------------------- +def ida_main_legacy(): + # Here we simply show how to use the old style form format using Python + + # Sample form from kernwin.hpp + s = """Sample dialog box + + +This is sample dialog box for %A +using address %$ + +<~E~nter value:N:32:16::> +""" + + # Use either StringArgument or NumericArgument to pass values to the function + num = Form.NumericArgument('N', value=123) + ok = idaapi.AskUsingForm(s, + Form.StringArgument("PyAskUsingForm").arg, + Form.NumericArgument('$', 0x401000).arg, + num.arg) + if ok == 1: + print("You entered: %x" % num.value) + +# -------------------------------------------------------------------------- +def test_multilinetext_legacy(): + # Here we text the multi line text control in legacy mode + + # Sample form from kernwin.hpp + s = """Sample dialog box + +This is sample dialog box + +""" + # Use either StringArgument or NumericArgument to pass values to the function + ti = textctrl_info_t("Some initial value") + ok = idaapi.AskUsingForm(s, pointer(c_void_p.from_address(ti.clink_ptr))) + if ok == 1: + print("You entered: %s" % ti.text) + + del ti + +# -------------------------------------------------------------------------- +class MyForm2(Form): + """Simple Form to test multilinetext and combo box controls""" + def __init__(self): + Form.__init__(self, r"""STARTITEM 0 +BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL NONE +Form Test + +{FormChangeCb} + +""", { + 'txtMultiLineText': Form.MultiLineTextControl(text="Hello"), + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + }) + + + def OnFormChange(self, fid): + if fid == self.txtMultiLineText.id: + pass + elif fid == -2: + ti = self.GetControlValue(self.txtMultiLineText) + print "ti.text = %s" % ti.text + else: + print(">>fid:%d" % fid) + return 1 + +# -------------------------------------------------------------------------- +def test_multilinetext(execute=True): + """Test the multilinetext and combobox controls""" + f = MyForm2() + f, args = f.Compile() + if execute: + ok = f.Execute() + else: + print args[0] + print args[1:] + ok = 0 + + if ok == 1: + assert f.txtMultiLineText.text == f.txtMultiLineText.value + print f.txtMultiLineText.text + + f.Free() + +# -------------------------------------------------------------------------- +class MyForm3(Form): + """Simple Form to test multilinetext and combo box controls""" + def __init__(self): + self.__n = 0 + Form.__init__(self, +r"""BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL NONE +Dropdown list test + +{FormChangeCb} + + +""", { + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'cbReadonly': Form.DropdownListControl( + items=["red", "green", "blue"], + readonly=True, + selval=1), + 'cbEditable': Form.DropdownListControl( + items=["1MB", "2MB", "3MB", "4MB"], + readonly=False, + selval="4MB"), + 'iButtonAddelement': Form.ButtonInput(self.OnButtonNop), + 'iButtonSetIndex': Form.ButtonInput(self.OnButtonNop), + 'iButtonSetString': Form.ButtonInput(self.OnButtonNop), + }) + + + def OnButtonNop(self, code=0): + """Do nothing, we will handle events in the form callback""" + pass + + def OnFormChange(self, fid): + if fid == self.iButtonSetString.id: + s = idc.AskStr("none", "Enter value") + if s: + self.SetControlValue(self.cbEditable, s) + elif fid == self.iButtonSetIndex.id: + s = idc.AskStr("1", "Enter index value:") + if s: + try: + i = int(s) + except: + i = 0 + self.SetControlValue(self.cbReadonly, i) + elif fid == self.iButtonAddelement.id: + # add a value to the string list + self.__n += 1 + self.cbReadonly.add("some text #%d" % self.__n) + # Refresh the control + self.RefreshField(self.cbReadonly) + elif fid == -2: + s = self.GetControlValue(self.cbEditable) + print "user entered: %s" % s + sel_idx = self.GetControlValue(self.cbReadonly) + + return 1 + +# -------------------------------------------------------------------------- +def test_dropdown(execute=True): + """Test the combobox controls, in a modal dialog""" + f = MyForm3() + f, args = f.Compile() + if execute: + ok = f.Execute() + else: + print args[0] + print args[1:] + ok = 0 + + if ok == 1: + print "Editable: %s" % f.cbEditable.value + print "Readonly: %s" % f.cbReadonly.value + + f.Free() + +# -------------------------------------------------------------------------- +tdn_form = None +def test_dropdown_nomodal(): + """Test the combobox controls, in a non-modal form""" + global tdn_form + if tdn_form is None: + tdn_form = MyForm3() + tdn_form.modal = False + tdn_form.openform_flags = idaapi.PluginForm.FORM_TAB + tdn_form, _ = tdn_form.Compile() + tdn_form.Open() + +# + + +# -------------------------------------------------------------------------- +ida_main() diff --git a/examples/ex_choose2.py b/examples/ex_choose2.py index 00c23495..e91eef1d 100644 --- a/examples/ex_choose2.py +++ b/examples/ex_choose2.py @@ -1,133 +1,133 @@ -import idaapi -from idaapi import Choose2 - -# - - -class chooser_handler_t(idaapi.action_handler_t): - def __init__(self, thing): - idaapi.action_handler_t.__init__(self) - self.thing = thing - - def activate(self, ctx): - sel = [] - for i in xrange(len(ctx.chooser_selection)): - sel.append(str(ctx.chooser_selection.at(i))) - print "command %s selected @ %s" % (self.thing, ", ".join(sel)) - - def update(self, ctx): - return idaapi.AST_ENABLE_FOR_FORM if idaapi.is_chooser_tform(ctx.form_type) else idaapi.AST_DISABLE_FOR_FORM - - -class MyChoose2(Choose2): - - def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False): - Choose2.__init__( - self, - title, - [ ["Address", 10], ["Name", 30] ], - flags = flags, - width = width, - height = height, - embedded = embedded) - self.n = 0 - self.items = [ self.make_item() for x in xrange(0, nb+1) ] - self.icon = 5 - self.selcount = 0 - self.modal = modal - self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"] - - print("created %s" % str(self)) - - def OnClose(self): - print "closed", str(self) - - def OnEditLine(self, n): - self.items[n][1] = self.items[n][1] + "*" - print("editing %d" % n) - - def OnInsertLine(self): - self.items.append(self.make_item()) - print("insert line") - - def OnSelectLine(self, n): - self.selcount += 1 - Warning("[%02d] selectline '%s'" % (self.selcount, n)) - - def OnGetLine(self, n): - print("getline %d" % n) - return self.items[n] - - def OnGetSize(self): - n = len(self.items) - print("getsize -> %d" % n) - return n - - def OnDeleteLine(self, n): - print("del %d " % n) - del self.items[n] - return n - - def OnRefresh(self, n): - print("refresh %d" % n) - return n - - def OnGetIcon(self, n): - r = self.items[n] - t = self.icon + r[1].count("*") - print "geticon", n, t - return t - - def show(self): - return self.Show(self.modal) >= 0 - - def make_item(self): - r = [str(self.n), "func_%04d" % self.n] - self.n += 1 - return r - - def OnGetLineAttr(self, n): - print("getlineattr %d" % n) - if n == 1: - return [0xFF0000, 0] - - -# ----------------------------------------------------------------------- -def test_choose2(modal=False): - global c - c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal) - r = c.show() - form = idaapi.get_current_tform() - for thing in ["A", "B"]: - idaapi.attach_action_to_popup(form, None, "choose2:act%s" % thing) - -# ----------------------------------------------------------------------- -def test_choose2_embedded(): - global c - c = MyChoose2("Choose2 - embedded", nb=12, embedded = True, width=123, height=222) - r = c.Embedded() - if r == 1: - try: - if test_embedded: - o, sel = _idaapi.choose2_get_embedded(c) - print("o=%s, type(o)=%s" % (str(o), type(o))) - test_embedded(o) - finally: - c.Close() - -# ----------------------------------------------------------------------- -if __name__ == '__main__': - - # Register actions - for thing in ["A", "B"]: - actname = "choose2:act%s" % thing - idaapi.register_action( - idaapi.action_desc_t( - actname, - "command %s" % thing, - chooser_handler_t(thing))) - - #test_choose2_embedded() - test_choose2(False) - -# +import idaapi +from idaapi import Choose2 + +# + + +class chooser_handler_t(idaapi.action_handler_t): + def __init__(self, thing): + idaapi.action_handler_t.__init__(self) + self.thing = thing + + def activate(self, ctx): + sel = [] + for i in xrange(len(ctx.chooser_selection)): + sel.append(str(ctx.chooser_selection.at(i))) + print "command %s selected @ %s" % (self.thing, ", ".join(sel)) + + def update(self, ctx): + return idaapi.AST_ENABLE_FOR_FORM if idaapi.is_chooser_tform(ctx.form_type) else idaapi.AST_DISABLE_FOR_FORM + + +class MyChoose2(Choose2): + + def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False): + Choose2.__init__( + self, + title, + [ ["Address", 10], ["Name", 30] ], + flags = flags, + width = width, + height = height, + embedded = embedded) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + self.modal = modal + self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"] + + print("created %s" % str(self)) + + def OnClose(self): + print "closed", str(self) + + def OnEditLine(self, n): + self.items[n][1] = self.items[n][1] + "*" + print("editing %d" % n) + + def OnInsertLine(self): + self.items.append(self.make_item()) + print("insert line") + + def OnSelectLine(self, n): + self.selcount += 1 + Warning("[%02d] selectline '%s'" % (self.selcount, n)) + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + print("getsize -> %d" % n) + return n + + def OnDeleteLine(self, n): + print("del %d " % n) + del self.items[n] + return n + + def OnRefresh(self, n): + print("refresh %d" % n) + return n + + def OnGetIcon(self, n): + r = self.items[n] + t = self.icon + r[1].count("*") + print "geticon", n, t + return t + + def show(self): + return self.Show(self.modal) >= 0 + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnGetLineAttr(self, n): + print("getlineattr %d" % n) + if n == 1: + return [0xFF0000, 0] + + +# ----------------------------------------------------------------------- +def test_choose2(modal=False): + global c + c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal) + r = c.show() + form = idaapi.get_current_tform() + for thing in ["A", "B"]: + idaapi.attach_action_to_popup(form, None, "choose2:act%s" % thing) + +# ----------------------------------------------------------------------- +def test_choose2_embedded(): + global c + c = MyChoose2("Choose2 - embedded", nb=12, embedded = True, width=123, height=222) + r = c.Embedded() + if r == 1: + try: + if test_embedded: + o, sel = _idaapi.choose2_get_embedded(c) + print("o=%s, type(o)=%s" % (str(o), type(o))) + test_embedded(o) + finally: + c.Close() + +# ----------------------------------------------------------------------- +if __name__ == '__main__': + + # Register actions + for thing in ["A", "B"]: + actname = "choose2:act%s" % thing + idaapi.register_action( + idaapi.action_desc_t( + actname, + "command %s" % thing, + chooser_handler_t(thing))) + + #test_choose2_embedded() + test_choose2(False) + +# diff --git a/examples/ex_cli.py b/examples/ex_cli.py index 438d7c0f..b68d58ce 100644 --- a/examples/ex_cli.py +++ b/examples/ex_cli.py @@ -1,100 +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 - -# -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 prefix_start (string) - @param n: completion number (int) - @param line: the current line (string) - @param prefix_start: the index where PREFIX starts in LINE (int) - - @return: None if no completion could be generated otherwise a String with the completion suggestion - """ - print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start) - return None -# - - -# ----------------------------------------------------------------------- -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" - +# ----------------------------------------------------------------------- +# 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 + +# +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 prefix_start (string) + @param n: completion number (int) + @param line: the current line (string) + @param prefix_start: the index where PREFIX starts in LINE (int) + + @return: None if no completion could be generated otherwise a String with the completion suggestion + """ + print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start) + return None +# + + +# ----------------------------------------------------------------------- +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" + diff --git a/examples/ex_custdata.py b/examples/ex_custdata.py index c5e8c70b..b78cbac3 100644 --- a/examples/ex_custdata.py +++ b/examples/ex_custdata.py @@ -1,216 +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 - -# - -# ----------------------------------------------------------------------- -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(),)) - -# - -# ----------------------------------------------------------------------- -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!" +# ----------------------------------------------------------------------- +# 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 + +# + +# ----------------------------------------------------------------------- +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(),)) + +# + +# ----------------------------------------------------------------------- +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!" diff --git a/examples/ex_custview.py b/examples/ex_custview.py index 82bcf355..36455fb7 100644 --- a/examples/ex_custview.py +++ b/examples/ex_custview.py @@ -1,183 +1,183 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to use customview in Python -# (c) Hex-Rays -# -import idaapi -import idc -from idaapi import simplecustviewer_t -# - -class say_something_handler_t(idaapi.action_handler_t): - def __init__(self, thing): - idaapi.action_handler_t.__init__(self) - self.thing = thing - - def activate(self, ctx): - print self.thing - - def update(self, ctx): - return idaapi.AST_ENABLE_ALWAYS - - -# ----------------------------------------------------------------------- -class mycv_t(simplecustviewer_t): - def Create(self, sn=None): - # Form the title - title = "Simple custom view test" - if sn: - title += " %d" % sn - - # Create the customviewer - if not simplecustviewer_t.Create(self, title): - return False - - for i in xrange(0, 100): - self.AddLine("Line %d" % i) - -# self.Jump(0) - - return True - - def OnClick(self, shift): - """ - User clicked in the view - @param shift: Shift flag - @return: Boolean. True if you handled the event - """ - print "OnClick, shift=%d" % shift - return True - - def OnDblClick(self, shift): - """ - User dbl-clicked in the view - @param shift: Shift flag - @return: Boolean. True if you handled the event - """ - word = self.GetCurrentWord() - if not word: word = "" - print "OnDblClick, shift=%d, current word=%s" % (shift, word) - return True - - def OnCursorPosChanged(self): - """ - Cursor position changed. - @return: Nothing - """ - print "OnCurposChanged" - - def OnClose(self): - """ - The view is closing. Use this event to cleanup. - @return: Nothing - """ - print "OnClose " + self.title - - def OnKeydown(self, vkey, shift): - """ - User pressed a key - @param vkey: Virtual key code - @param shift: Shift flag - @return: Boolean. True if you handled the event - """ - print "OnKeydown, vk=%d shift=%d" % (vkey, shift) - # ESCAPE? - if vkey == 27: - self.Close() - # VK_DELETE - elif vkey == 46: - n = self.GetLineNo() - if n is not None: - self.DelLine(n) - self.Refresh() - print "Deleted line %d" % n - # Goto? - elif vkey == ord('G'): - n = self.GetLineNo() - if n is not None: - v = idc.AskLong(self.GetLineNo(), "Where to go?") - if v: - self.Jump(v, 0, 5) - elif vkey == ord('R'): - print "refreshing...." - self.Refresh() - elif vkey == ord('C'): - print "refreshing current line..." - self.RefreshCurrent() - elif vkey == ord('A'): - s = idc.AskStr("NewLine%d" % self.Count(), "Append new line") - self.AddLine(s) - self.Refresh() - elif vkey == ord('X'): - print "Clearing all lines" - self.ClearLines() - self.Refresh() - elif vkey == ord('I'): - n = self.GetLineNo() - s = idc.AskStr("InsertedLine%d" % n, "Insert new line") - self.InsertLine(n, s) - self.Refresh() - elif vkey == ord('E'): - l = self.GetCurrentLine(notags=1) - if not l: - return False - n = self.GetLineNo() - print "curline=<%s>" % l - l = l + idaapi.COLSTR("*", idaapi.SCOLOR_VOIDOP) - self.EditLine(n, l) - self.RefreshCurrent() - print "Edited line %d" % n - else: - return False - return True - - def OnHint(self, lineno): - """ - Hint requested for the given line number. - @param lineno: The line number (zero based) - @return: - - tuple(number of important lines, hint string) - - None: if no hint available - """ - return (1, "OnHint, line=%d" % lineno) - -# ----------------------------------------------------------------------- -try: - # created already? - mycv - print "Already created, will close it..." - mycv.Close() - del mycv -except: - pass - -def show_win(): - x = mycv_t() - if not x.Create(): - print "Failed to create!" - return None - x.Show() - tcc = x.GetTCustomControl() - - # Register actions - for thing in ["Hello", "World"]: - actname = "custview:say_%s" % thing - idaapi.register_action( - idaapi.action_desc_t(actname, "Say %s" % thing, say_something_handler_t(thing))) - idaapi.attach_action_to_popup(tcc, None, actname) - return x - -mycv = show_win() -if not mycv: - del mycv - -def make_many(n): - L = [] - for i in xrange(1, n+1): - v = mycv_t() - if not v.Create(i): - break - v.Show() - L.append(v) - return L - -# \ No newline at end of file +# ----------------------------------------------------------------------- +# This is an example illustrating how to use customview in Python +# (c) Hex-Rays +# +import idaapi +import idc +from idaapi import simplecustviewer_t +# + +class say_something_handler_t(idaapi.action_handler_t): + def __init__(self, thing): + idaapi.action_handler_t.__init__(self) + self.thing = thing + + def activate(self, ctx): + print self.thing + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +# ----------------------------------------------------------------------- +class mycv_t(simplecustviewer_t): + def Create(self, sn=None): + # Form the title + title = "Simple custom view test" + if sn: + title += " %d" % sn + + # Create the customviewer + if not simplecustviewer_t.Create(self, title): + return False + + for i in xrange(0, 100): + self.AddLine("Line %d" % i) + +# self.Jump(0) + + return True + + def OnClick(self, shift): + """ + User clicked in the view + @param shift: Shift flag + @return: Boolean. True if you handled the event + """ + print "OnClick, shift=%d" % shift + return True + + def OnDblClick(self, shift): + """ + User dbl-clicked in the view + @param shift: Shift flag + @return: Boolean. True if you handled the event + """ + word = self.GetCurrentWord() + if not word: word = "" + print "OnDblClick, shift=%d, current word=%s" % (shift, word) + return True + + def OnCursorPosChanged(self): + """ + Cursor position changed. + @return: Nothing + """ + print "OnCurposChanged" + + def OnClose(self): + """ + The view is closing. Use this event to cleanup. + @return: Nothing + """ + print "OnClose " + self.title + + def OnKeydown(self, vkey, shift): + """ + User pressed a key + @param vkey: Virtual key code + @param shift: Shift flag + @return: Boolean. True if you handled the event + """ + print "OnKeydown, vk=%d shift=%d" % (vkey, shift) + # ESCAPE? + if vkey == 27: + self.Close() + # VK_DELETE + elif vkey == 46: + n = self.GetLineNo() + if n is not None: + self.DelLine(n) + self.Refresh() + print "Deleted line %d" % n + # Goto? + elif vkey == ord('G'): + n = self.GetLineNo() + if n is not None: + v = idc.AskLong(self.GetLineNo(), "Where to go?") + if v: + self.Jump(v, 0, 5) + elif vkey == ord('R'): + print "refreshing...." + self.Refresh() + elif vkey == ord('C'): + print "refreshing current line..." + self.RefreshCurrent() + elif vkey == ord('A'): + s = idc.AskStr("NewLine%d" % self.Count(), "Append new line") + self.AddLine(s) + self.Refresh() + elif vkey == ord('X'): + print "Clearing all lines" + self.ClearLines() + self.Refresh() + elif vkey == ord('I'): + n = self.GetLineNo() + s = idc.AskStr("InsertedLine%d" % n, "Insert new line") + self.InsertLine(n, s) + self.Refresh() + elif vkey == ord('E'): + l = self.GetCurrentLine(notags=1) + if not l: + return False + n = self.GetLineNo() + print "curline=<%s>" % l + l = l + idaapi.COLSTR("*", idaapi.SCOLOR_VOIDOP) + self.EditLine(n, l) + self.RefreshCurrent() + print "Edited line %d" % n + else: + return False + return True + + def OnHint(self, lineno): + """ + Hint requested for the given line number. + @param lineno: The line number (zero based) + @return: + - tuple(number of important lines, hint string) + - None: if no hint available + """ + return (1, "OnHint, line=%d" % lineno) + +# ----------------------------------------------------------------------- +try: + # created already? + mycv + print "Already created, will close it..." + mycv.Close() + del mycv +except: + pass + +def show_win(): + x = mycv_t() + if not x.Create(): + print "Failed to create!" + return None + x.Show() + tcc = x.GetTCustomControl() + + # Register actions + for thing in ["Hello", "World"]: + actname = "custview:say_%s" % thing + idaapi.register_action( + idaapi.action_desc_t(actname, "Say %s" % thing, say_something_handler_t(thing))) + idaapi.attach_action_to_popup(tcc, None, actname) + return x + +mycv = show_win() +if not mycv: + del mycv + +def make_many(n): + L = [] + for i in xrange(1, n+1): + v = mycv_t() + if not v.Create(i): + break + v.Show() + L.append(v) + return L + +# diff --git a/examples/ex_dbg.py b/examples/ex_dbg.py index 6f2f8b9a..1c044039 100644 --- a/examples/ex_dbg.py +++ b/examples/ex_dbg.py @@ -1,48 +1,34 @@ -# -# Demonstrates some functions from the "dbg" class -# - -import idaapi -#from idaapi import dbg_write_memory, dbg_read_memory, dbg_get_thread_sreg_base, dbg_get_registers, dbg_get_memory_info - -def dump_meminfo(L): - # startEA, endEA, name, sclass, sbase, bitness, perm - for (startEA, endEA, name, sclass, sbase, bitness, perm) in L: - print "%x: %x name=<%s> sclass=<%s> sbase=%x bitness=%2x perm=%2x" % (startEA, endEA, name, sclass, sbase, bitness, perm) - -def test_getmeminfo(): - L = idaapi.dbg_get_memory_info() - dump_meminfo(L) - -def test_getregs(): - L = idaapi.dbg_get_registers() - # name flags class dtyp bit_strings bit_strings_default_mask - for (name, flags, cls, dtype, bit_strings, bit_strings_default_mask) in L: - print "name=<%s> flags=%x class=%x dtype=%x bit_strings_mask=%x" % (name, flags, cls, dtype, bit_strings_default_mask) - if bit_strings: - for s in bit_strings: - print " %s" % s - -def test_manual_regions(): - L = idaapi.get_manual_regions() - if not L: - print "no manual regions!" - else: - dump_meminfo(L) - -def test_readwrite(): - ea = cpu.Eip - buf = idaapi.dbg_read_memory(ea, 5) - print "read: ", [hex(ord(x)) for x in buf] - idaapi.dbg_write_memory(ea, buf) - -test_manual_regions() - -if idaapi.dbg_can_query(): - print "%x: fs" % (idaapi.dbg_get_thread_sreg_base(idc.GetCurrentThreadId(), cpu.fs)) - test_getmeminfo() - test_getregs() - test_readwrite() - -else: - print "run and suspend the debugger first" \ No newline at end of file +from tempo import *; + +def test_getmeminfo(): + L = tempo.getmeminfo() + out = [] + + # startEA endEA name sclass sbase bitness perm + for (startEA, endEA, name, sclass, sbase, bitness, perm) in L: + out.append("%x: %x name=<%s> sclass=<%s> sbase=%x bitness=%2x perm=%2x" % (startEA, endEA, name, sclass, sbase, bitness, perm)) + + f = file(r"d:\temp\out.log", "w") + f.write("\n".join(out)) + f.close() + + print "dumped meminfo!" + + +def test_getregs(): + # name flags class dtyp bit_strings bit_strings_default_mask + L = tempo.getregs() + out = [] + for (name, flags, cls, dtype, bit_strings, bit_strings_default_mask) in L: + out.append("name=<%s> flags=%x class=%x dtype=%x bit_strings_mask=%x" % (name, flags, cls, dtype, bit_strings_default_mask)) + if bit_strings: + for s in bit_strings: + out.append(" %s" % s) + + f = file(r"d:\temp\out.log", "w") + f.write("\n".join(out)) + f.close() + + print "dumped regs!" + + diff --git a/examples/ex_debug_names.py b/examples/ex_debug_names.py index e6993c22..68cb1b1c 100644 --- a/examples/ex_debug_names.py +++ b/examples/ex_debug_names.py @@ -1,15 +1,15 @@ -import idaapi - -def main(): - if not idaapi.is_debugger_on(): - print "Please run the process first!" - return - if idaapi.get_process_state() != -1: - print "Please suspend the debugger first!" - return - - dn = idaapi.get_debug_names(idaapi.cvar.inf.minEA, idaapi.cvar.inf.maxEA) - for i in dn: - print "%08x: %s" % (i, dn[i]) - -main() +import idaapi + +def main(): + if not idaapi.is_debugger_on(): + print "Please run the process first!" + return + if idaapi.get_process_state() != -1: + print "Please suspend the debugger first!" + return + + dn = idaapi.get_debug_names(idaapi.cvar.inf.minEA, idaapi.cvar.inf.maxEA) + for i in dn: + print "%08x: %s" % (i, dn[i]) + +main() diff --git a/examples/ex_expr.py b/examples/ex_expr.py index 1f97f3c5..765d7463 100644 --- a/examples/ex_expr.py +++ b/examples/ex_expr.py @@ -1,16 +1,16 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to extend IDC from Python -# (c) Hex-Rays -# -from idaapi import set_idc_func_ex - -# -def py_power(n, e): - return n ** e - -ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) -if ok: - print("Now the pow() will be present IDC!") -else: - print("Failed to register pow() IDC function") -# +# ----------------------------------------------------------------------- +# This is an example illustrating how to extend IDC from Python +# (c) Hex-Rays +# +from idaapi import set_idc_func_ex + +# +def py_power(n, e): + return n ** e + +ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) +if ok: + print("Now the pow() will be present IDC!") +else: + print("Failed to register pow() IDC function") +# diff --git a/examples/ex_func_chooser.py b/examples/ex_func_chooser.py index 01aafef9..b489f5f0 100644 --- a/examples/ex_func_chooser.py +++ b/examples/ex_func_chooser.py @@ -1,38 +1,38 @@ -import idaapi -import idautils -import idc - -class MyChoose2(Choose2): - - def __init__(self, title): - Choose2.__init__(self, title, [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ]) - self.n = 0 - self.icon = 41 - self.PopulateItems() - - def PopulateItems(self): - self.items = [ [hex(x), GetFunctionName(x), x] for x in idautils.Functions() ] - - def OnClose(self): - print "closed ", self.title - - def OnSelectLine(self, n): - idc.Jump(self.items[n][2]) - - def OnGetLine(self, n): - return self.items[n] - - def OnGetSize(self): - return len(self.items) - - def OnDeleteLine(self, n): - ea = self.items[n][2] - idc.DelFunction(ea) - return n - - def OnRefresh(self, n): - self.PopulateItems() - return n - -c = MyChoose2("My functions list") +import idaapi +import idautils +import idc + +class MyChoose2(Choose2): + + def __init__(self, title): + Choose2.__init__(self, title, [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ]) + self.n = 0 + self.icon = 41 + self.PopulateItems() + + def PopulateItems(self): + self.items = [ [hex(x), GetFunctionName(x), x] for x in idautils.Functions() ] + + def OnClose(self): + print "closed ", self.title + + def OnSelectLine(self, n): + idc.Jump(self.items[n][2]) + + def OnGetLine(self, n): + return self.items[n] + + def OnGetSize(self): + return len(self.items) + + def OnDeleteLine(self, n): + ea = self.items[n][2] + idc.DelFunction(ea) + return n + + def OnRefresh(self, n): + self.PopulateItems() + return n + +c = MyChoose2("My functions list") c.Show() \ No newline at end of file diff --git a/examples/ex_gdl_qflow_chart.py b/examples/ex_gdl_qflow_chart.py index dae5bd4a..8ee9c8e5 100644 --- a/examples/ex_gdl_qflow_chart.py +++ b/examples/ex_gdl_qflow_chart.py @@ -1,43 +1,43 @@ -import idaapi - -# ----------------------------------------------------------------------- -# Using raw IDAAPI -def raw_main(p=True): - f = idaapi.get_func(here()) - if not f: - return - - q = idaapi.qflow_chart_t("The title", f, 0, 0, idaapi.FC_PREDS) - for n in xrange(0, q.size()): - b = q[n] - if p: - print "%x - %x [%d]:" % (b.startEA, b.endEA, n) - - for ns in xrange(0, q.nsucc(n)): - if p: - print "SUCC: %d->%d" % (n, q.succ(n, ns)) - - for ns in xrange(0, q.npred(n)): - if p: - print "PRED: %d->%d" % (n, q.pred(n, ns)) - -# ----------------------------------------------------------------------- -# Using the class -def cls_main(p=True): - f = idaapi.FlowChart(idaapi.get_func(here())) - for block in f: - if p: - print "%x - %x [%d]:" % (block.startEA, block.endEA, block.id) - for succ_block in block.succs(): - if p: - print " %x - %x [%d]:" % (succ_block.startEA, succ_block.endEA, succ_block.id) - - for pred_block in block.preds(): - if p: - print " %x - %x [%d]:" % (pred_block.startEA, pred_block.endEA, pred_block.id) - -q = None -f = None -raw_main(False) -cls_main(True) - +import idaapi + +# ----------------------------------------------------------------------- +# Using raw IDAAPI +def raw_main(p=True): + f = idaapi.get_func(here()) + if not f: + return + + q = idaapi.qflow_chart_t("The title", f, 0, 0, idaapi.FC_PREDS) + for n in xrange(0, q.size()): + b = q[n] + if p: + print "%x - %x [%d]:" % (b.startEA, b.endEA, n) + + for ns in xrange(0, q.nsucc(n)): + if p: + print "SUCC: %d->%d" % (n, q.succ(n, ns)) + + for ns in xrange(0, q.npred(n)): + if p: + print "PRED: %d->%d" % (n, q.pred(n, ns)) + +# ----------------------------------------------------------------------- +# Using the class +def cls_main(p=True): + f = idaapi.FlowChart(idaapi.get_func(here())) + for block in f: + if p: + print "%x - %x [%d]:" % (block.startEA, block.endEA, block.id) + for succ_block in block.succs(): + if p: + print " %x - %x [%d]:" % (succ_block.startEA, succ_block.endEA, succ_block.id) + + for pred_block in block.preds(): + if p: + print " %x - %x [%d]:" % (pred_block.startEA, pred_block.endEA, pred_block.id) + +q = None +f = None +raw_main(False) +cls_main(True) + diff --git a/examples/ex_graph.py b/examples/ex_graph.py index 38f912d8..62f67a1b 100644 --- a/examples/ex_graph.py +++ b/examples/ex_graph.py @@ -1,70 +1,70 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to use the user graphing functionality -# in Python -# (c) Hex-Rays -# -from idaapi import * - -class GraphCloser(action_handler_t): - def __init__(self, graph): - action_handler_t.__init__(self) - self.graph = graph - - def activate(self, ctx): - self.graph.Close() - - def update(self, ctx): - return AST_ENABLE_ALWAYS - - -class MyGraph(GraphViewer): - def __init__(self, funcname, result): - self.title = "call graph of " + funcname - GraphViewer.__init__(self, self.title) - self.funcname = funcname - self.result = result - - def OnRefresh(self): - self.Clear() - id = self.AddNode(self.funcname) - for x in self.result.keys(): - callee = self.AddNode(x) - self.AddEdge(id, callee) - - return True - - def OnGetText(self, node_id): - return str(self[node_id]) - - def Show(self): - if not GraphViewer.Show(self): - return False - actname = "graph_closer:%s" % self.title - register_action(action_desc_t(actname, "Close %s" % self.title, GraphCloser(self))) - attach_action_to_popup(self.GetTCustomControl(), None, actname) - return True - - -def show_graph(): - f = idaapi.get_func(here()) - if not f: - print "Must be in a function" - return - # Iterate through all function instructions and take only call instructions - result = {} - for x in [x for x in FuncItems(f.startEA) if idaapi.is_call_insn(x)]: - for xref in XrefsFrom(x, idaapi.XREF_FAR): - if not xref.iscode: continue - t = GetFunctionName(xref.to) - if not t: - t = hex(xref.to) - result[t] = True - g = MyGraph(GetFunctionName(f.startEA), result) - if g.Show(): - return g - else: - return None - -g = show_graph() -if g: - print "Graph created and displayed!" \ No newline at end of file +# ----------------------------------------------------------------------- +# This is an example illustrating how to use the user graphing functionality +# in Python +# (c) Hex-Rays +# +from idaapi import * + +class GraphCloser(action_handler_t): + def __init__(self, graph): + action_handler_t.__init__(self) + self.graph = graph + + def activate(self, ctx): + self.graph.Close() + + def update(self, ctx): + return AST_ENABLE_ALWAYS + + +class MyGraph(GraphViewer): + def __init__(self, funcname, result): + self.title = "call graph of " + funcname + GraphViewer.__init__(self, self.title) + self.funcname = funcname + self.result = result + + def OnRefresh(self): + self.Clear() + id = self.AddNode(self.funcname) + for x in self.result.keys(): + callee = self.AddNode(x) + self.AddEdge(id, callee) + + return True + + def OnGetText(self, node_id): + return str(self[node_id]) + + def Show(self): + if not GraphViewer.Show(self): + return False + actname = "graph_closer:%s" % self.title + register_action(action_desc_t(actname, "Close %s" % self.title, GraphCloser(self))) + attach_action_to_popup(self.GetTCustomControl(), None, actname) + return True + + +def show_graph(): + f = idaapi.get_func(here()) + if not f: + print "Must be in a function" + return + # Iterate through all function instructions and take only call instructions + result = {} + for x in [x for x in FuncItems(f.startEA) if idaapi.is_call_insn(x)]: + for xref in XrefsFrom(x, idaapi.XREF_FAR): + if not xref.iscode: continue + t = GetFunctionName(xref.to) + if not t: + t = hex(xref.to) + result[t] = True + g = MyGraph(GetFunctionName(f.startEA), result) + if g.Show(): + return g + else: + return None + +g = show_graph() +if g: + print "Graph created and displayed!" diff --git a/examples/ex_imports.py b/examples/ex_imports.py index 15e21266..660415d4 100644 --- a/examples/ex_imports.py +++ b/examples/ex_imports.py @@ -1,29 +1,29 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to enumerate imports -# (c) Hex-Rays -# -import idaapi - -def imp_cb(ea, name, ord): - if not name: - print "%08x: ord#%d" % (ea, ord) - else: - print "%08x: %s (ord#%d)" % (ea, name, ord) - # True -> Continue enumeration - # False -> Stop enumeration - return True - -nimps = idaapi.get_import_module_qty() - -print "Found %d import(s)..." % nimps - -for i in xrange(0, nimps): - name = idaapi.get_import_module_name(i) - if not name: - print "Failed to get import module name for #%d" % i - continue - - print "Walking-> %s" % name - idaapi.enum_import_names(i, imp_cb) - +# ----------------------------------------------------------------------- +# This is an example illustrating how to enumerate imports +# (c) Hex-Rays +# +import idaapi + +def imp_cb(ea, name, ord): + if not name: + print "%08x: ord#%d" % (ea, ord) + else: + print "%08x: %s (ord#%d)" % (ea, name, ord) + # True -> Continue enumeration + # False -> Stop enumeration + return True + +nimps = idaapi.get_import_module_qty() + +print "Found %d import(s)..." % nimps + +for i in xrange(0, nimps): + name = idaapi.get_import_module_name(i) + if not name: + print "Failed to get import module name for #%d" % i + continue + + print "Walking-> %s" % name + idaapi.enum_import_names(i, imp_cb) + print "All done..." \ No newline at end of file diff --git a/examples/ex_prefix_plugin.py b/examples/ex_prefix_plugin.py index aae8c2b4..4f5993ae 100644 --- a/examples/ex_prefix_plugin.py +++ b/examples/ex_prefix_plugin.py @@ -1,42 +1,42 @@ -import idaapi - -PREFIX = idaapi.SCOLOR_INV + ' ' + idaapi.SCOLOR_INV - -class prefix_plugin_t(idaapi.plugin_t): - flags = 0 - comment = "This is a user defined prefix sample plugin" - help = "This is help" - wanted_name = "user defined prefix" - wanted_hotkey = "" - - - def user_prefix(self, ea, lnnum, indent, line, bufsize): - #print("ea=%x lnnum=%d indent=%d line=%s bufsize=%d" % (ea, lnnum, indent, line, bufsize)) - - if (ea % 2 == 0) and indent == -1: - return PREFIX - else: - return "" - - - def init(self): - self.prefix_installed = idaapi.set_user_defined_prefix(8, self.user_prefix) - if self.prefix_installed: - print("prefix installed") - - return idaapi.PLUGIN_KEEP - - - def run(self, arg): - pass - - - def term(self): - if self.prefix_installed: - idaapi.set_user_defined_prefix(0, None) - print("prefix uninstalled!") - - -def PLUGIN_ENTRY(): - return prefix_plugin_t() - +import idaapi + +PREFIX = idaapi.SCOLOR_INV + ' ' + idaapi.SCOLOR_INV + +class prefix_plugin_t(idaapi.plugin_t): + flags = 0 + comment = "This is a user defined prefix sample plugin" + help = "This is help" + wanted_name = "user defined prefix" + wanted_hotkey = "" + + + def user_prefix(self, ea, lnnum, indent, line, bufsize): + #print("ea=%x lnnum=%d indent=%d line=%s bufsize=%d" % (ea, lnnum, indent, line, bufsize)) + + if (ea % 2 == 0) and indent == -1: + return PREFIX + else: + return "" + + + def init(self): + self.prefix_installed = idaapi.set_user_defined_prefix(8, self.user_prefix) + if self.prefix_installed: + print("prefix installed") + + return idaapi.PLUGIN_KEEP + + + def run(self, arg): + pass + + + def term(self): + if self.prefix_installed: + idaapi.set_user_defined_prefix(0, None) + print("prefix uninstalled!") + + +def PLUGIN_ENTRY(): + return prefix_plugin_t() + diff --git a/examples/ex_pyqt.py b/examples/ex_pyqt.py index eb62f0a2..f1371226 100644 --- a/examples/ex_pyqt.py +++ b/examples/ex_pyqt.py @@ -1,35 +1,35 @@ -from idaapi import PluginForm -from PyQt4 import QtCore, QtGui -import sip - -class MyPluginFormClass(PluginForm): - def OnCreate(self, form): - """ - Called when the plugin form is created - """ - - # Get parent widget - self.parent = self.FormToPyQtWidget(form) - self.PopulateForm() - - - def PopulateForm(self): - # Create layout - layout = QtGui.QVBoxLayout() - - layout.addWidget( - QtGui.QLabel("Hello from PyQt")) - layout.addWidget( - QtGui.QLabel("Hello from IDAPython")) - - self.parent.setLayout(layout) - - - def OnClose(self, form): - """ - Called when the plugin form is closed - """ - pass - -plg = MyPluginFormClass() -plg.Show("PyQt hello world") +from idaapi import PluginForm +from PyQt4 import QtCore, QtGui +import sip + +class MyPluginFormClass(PluginForm): + def OnCreate(self, form): + """ + Called when the plugin form is created + """ + + # Get parent widget + self.parent = self.FormToPyQtWidget(form) + self.PopulateForm() + + + def PopulateForm(self): + # Create layout + layout = QtGui.QVBoxLayout() + + layout.addWidget( + QtGui.QLabel("Hello from PyQt")) + layout.addWidget( + QtGui.QLabel("Hello from IDAPython")) + + self.parent.setLayout(layout) + + + def OnClose(self, form): + """ + Called when the plugin form is closed + """ + pass + +plg = MyPluginFormClass() +plg.Show("PyQt hello world") diff --git a/examples/ex_pyside.py b/examples/ex_pyside.py index 4f9f4335..dba9911f 100644 --- a/examples/ex_pyside.py +++ b/examples/ex_pyside.py @@ -1,34 +1,34 @@ -from idaapi import PluginForm -from PySide import QtGui, QtCore - -class MyPluginFormClass(PluginForm): - def OnCreate(self, form): - """ - Called when the plugin form is created - """ - - # Get parent widget - self.parent = self.FormToPySideWidget(form) - self.PopulateForm() - - - def PopulateForm(self): - # Create layout - layout = QtGui.QVBoxLayout() - - layout.addWidget( - QtGui.QLabel("Hello from PySide")) - layout.addWidget( - QtGui.QLabel("Hello from IDAPython")) - - self.parent.setLayout(layout) - - - def OnClose(self, form): - """ - Called when the plugin form is closed - """ - pass - -plg = MyPluginFormClass() -plg.Show("PySide hello world") +from idaapi import PluginForm +from PySide import QtGui, QtCore + +class MyPluginFormClass(PluginForm): + def OnCreate(self, form): + """ + Called when the plugin form is created + """ + + # Get parent widget + self.parent = self.FormToPySideWidget(form) + self.PopulateForm() + + + def PopulateForm(self): + # Create layout + layout = QtGui.QVBoxLayout() + + layout.addWidget( + QtGui.QLabel("Hello from PySide")) + layout.addWidget( + QtGui.QLabel("Hello from IDAPython")) + + self.parent.setLayout(layout) + + + def OnClose(self, form): + """ + Called when the plugin form is closed + """ + pass + +plg = MyPluginFormClass() +plg.Show("PySide hello world") diff --git a/examples/hotkey.py b/examples/hotkey.py index 1524404f..fdc8b5a9 100644 --- a/examples/hotkey.py +++ b/examples/hotkey.py @@ -1,24 +1,24 @@ -#--------------------------------------------------------------------- -# This script demonstrates the usage of hotkeys. -# -# Note: Hotkeys only work with the GUI version of IDA and not in -# text mode. -# -# Author: Gergely Erdelyi -#--------------------------------------------------------------------- -import idaapi - -def foo(): - print "Hotkey activated!" - -# IDA binds hotkeys to IDC functions so a trampoline IDC function -# must be created -idaapi.CompileLine('static key_2() { RunPythonStatement("foo()"); }') -# Add the hotkey -AddHotkey("2", 'key_2') - -# Press 2 to activate foo() - -# The hotkey can be removed with -# DelHotkey('2') - +#--------------------------------------------------------------------- +# This script demonstrates the usage of hotkeys. +# +# Note: Hotkeys only work with the GUI version of IDA and not in +# text mode. +# +# Author: Gergely Erdelyi +#--------------------------------------------------------------------- +import idaapi + +def foo(): + print "Hotkey activated!" + +# IDA binds hotkeys to IDC functions so a trampoline IDC function +# must be created +idaapi.CompileLine('static key_2() { RunPythonStatement("foo()"); }') +# Add the hotkey +AddHotkey("2", 'key_2') + +# Press 2 to activate foo() + +# The hotkey can be removed with +# DelHotkey('2') + diff --git a/examples/structure.py b/examples/structure.py index 7e550ac5..1ebedd33 100644 --- a/examples/structure.py +++ b/examples/structure.py @@ -1,49 +1,49 @@ -#--------------------------------------------------------------------- -# Structure test -# -# This script demonstrates how to create structures and populate them -# with members of different types. -# -# Author: Gergely Erdelyi -#--------------------------------------------------------------------- -from idaapi import stroffflag, offflag - -sid = GetStrucIdByName("mystr1") -if sid != -1: - DelStruc(sid) -sid = AddStrucEx(-1, "mystr1", 0) -print "%x" % sid - -# Test simple data types -simple_types = [ FF_BYTE, FF_WORD, FF_DWRD, FF_QWRD, FF_TBYT, FF_OWRD, FF_FLOAT, FF_DOUBLE, FF_PACKREAL ] -simple_sizes = [ 1, 2, 4, 8, 10, 16, 4, 8, 10 ] - -i = 0 -for t,nsize in zip(simple_types, simple_sizes): - print "t%x:"% ((t|FF_DATA)&0xFFFFFFFF), AddStrucMember(sid, "t%02d"%i, BADADDR, (t|FF_DATA )&0xFFFFFFFF, -1, nsize) - i+=1 - -# Test ASCII type -print "ASCII:", AddStrucMember(sid, "tascii", -1, FF_ASCI|FF_DATA, ASCSTR_C, 8) - -# Test enum type - Add a defined enum name or load MACRO_WMI from a type library. -#eid = GetEnum("MACRO_WMI") -#print "Enum:", AddStrucMember(sid, "tenum", BADADDR, FF_0ENUM|FF_DATA|FF_DWRD, eid, 4) - -# Test struc member type -msid = GetStrucIdByName("mystr2") -if msid != -1: - DelStruc(msid) -msid = AddStrucEx(-1, "mystr2", 0) -print AddStrucMember(msid, "member1", -1, (FF_DWRD|FF_DATA )&0xFFFFFFFF, -1, 4) -print AddStrucMember(msid, "member2", -1, (FF_DWRD|FF_DATA )&0xFFFFFFFF, -1, 4) - -msize = GetStrucSize(msid) -print "Struct:", AddStrucMember(sid, "tstruct", -1, FF_STRU|FF_DATA, msid, msize) -print "Stroff:", AddStrucMember(sid, "tstroff", -1, stroffflag()|FF_DWRD, msid, 4) - -# Test offset types -print "Offset:", AddStrucMember(sid, "toffset", -1, offflag()|FF_DATA|FF_DWRD, 0, 4) -print "Offset:", SetMemberType(sid, 0, offflag()|FF_DATA|FF_DWRD, 0, 4) - -print "Done" +#--------------------------------------------------------------------- +# Structure test +# +# This script demonstrates how to create structures and populate them +# with members of different types. +# +# Author: Gergely Erdelyi +#--------------------------------------------------------------------- +from idaapi import stroffflag, offflag + +sid = GetStrucIdByName("mystr1") +if sid != -1: + DelStruc(sid) +sid = AddStrucEx(-1, "mystr1", 0) +print "%x" % sid + +# Test simple data types +simple_types = [ FF_BYTE, FF_WORD, FF_DWRD, FF_QWRD, FF_TBYT, FF_OWRD, FF_FLOAT, FF_DOUBLE, FF_PACKREAL ] +simple_sizes = [ 1, 2, 4, 8, 10, 16, 4, 8, 10 ] + +i = 0 +for t,nsize in zip(simple_types, simple_sizes): + print "t%x:"% ((t|FF_DATA)&0xFFFFFFFF), AddStrucMember(sid, "t%02d"%i, BADADDR, (t|FF_DATA )&0xFFFFFFFF, -1, nsize) + i+=1 + +# Test ASCII type +print "ASCII:", AddStrucMember(sid, "tascii", -1, FF_ASCI|FF_DATA, ASCSTR_C, 8) + +# Test enum type - Add a defined enum name or load MACRO_WMI from a type library. +#eid = GetEnum("MACRO_WMI") +#print "Enum:", AddStrucMember(sid, "tenum", BADADDR, FF_0ENUM|FF_DATA|FF_DWRD, eid, 4) + +# Test struc member type +msid = GetStrucIdByName("mystr2") +if msid != -1: + DelStruc(msid) +msid = AddStrucEx(-1, "mystr2", 0) +print AddStrucMember(msid, "member1", -1, (FF_DWRD|FF_DATA )&0xFFFFFFFF, -1, 4) +print AddStrucMember(msid, "member2", -1, (FF_DWRD|FF_DATA )&0xFFFFFFFF, -1, 4) + +msize = GetStrucSize(msid) +print "Struct:", AddStrucMember(sid, "tstruct", -1, FF_STRU|FF_DATA, msid, msize) +print "Stroff:", AddStrucMember(sid, "tstroff", -1, stroffflag()|FF_DWRD, msid, 4) + +# Test offset types +print "Offset:", AddStrucMember(sid, "toffset", -1, offflag()|FF_DATA|FF_DWRD, 0, 4) +print "Offset:", SetMemberType(sid, 0, offflag()|FF_DATA|FF_DWRD, 0, 4) + +print "Done" diff --git a/idapython.sln b/idapython.sln index eadc0d6b..863f73e9 100644 --- a/idapython.sln +++ b/idapython.sln @@ -1,26 +1,26 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "idapython", "idapython.vcxproj", "{F43D6BB8-B7D6-486A-82E5-BABBA9848525}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug64|Win32 = Debug64|Win32 - Release|Win32 = Release|Win32 - SemiDebug|Win32 = SemiDebug|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.ActiveCfg = Debug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.Build.0 = Debug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug64|Win32.ActiveCfg = Debug64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug64|Win32.Build.0 = Debug64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.ActiveCfg = Release|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.Build.0 = Release|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.ActiveCfg = SemiDebug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.Build.0 = SemiDebug|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "idapython", "idapython.vcxproj", "{F43D6BB8-B7D6-486A-82E5-BABBA9848525}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug64|Win32 = Debug64|Win32 + Release|Win32 = Release|Win32 + SemiDebug|Win32 = SemiDebug|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.ActiveCfg = Debug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.Build.0 = Debug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug64|Win32.ActiveCfg = Debug64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug64|Win32.Build.0 = Debug64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.ActiveCfg = Release|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.Build.0 = Release|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.ActiveCfg = SemiDebug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.Build.0 = SemiDebug|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/idapython.vcxproj b/idapython.vcxproj index 1c9c1073..bfb6177b 100644 --- a/idapython.vcxproj +++ b/idapython.vcxproj @@ -1,453 +1,453 @@ - - - - - Debug64 - Win32 - - - Debug - Win32 - - - Release - Win32 - - - SemiDebug - Win32 - - - - {F43D6BB8-B7D6-486A-82E5-BABBA9848525} - idapython - - - - DynamicLibrary - false - MultiByte - v120 - - - DynamicLibrary - false - MultiByte - v120 - - - DynamicLibrary - false - MultiByte - v120 - - - DynamicLibrary - false - MultiByte - v120 - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - .\Debug\ - .\Debug64\ - .\Debug\ - .\Debug64\ - true - true - .\Release\ - .\Release\ - false - false - $(Configuration)\ - $(Configuration)\ - true - false - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/idapython.tlb - - - - - Disabled - .\pywraps;..\..\include;c:\python27\include;%(AdditionalIncludeDirectories) - WITH_HEXRAYS;NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=5;VER_PATCH=3;PLUGINFIX;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\Debug/idapython.pch - .\Debug/ - .\Debug/ - .\Debug/ - Level3 - true - EditAndContinue - Cdecl - 4102;4804;4800;4018;4005;%(DisableSpecificWarnings) - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;%(AdditionalDependencies) - c:\temp\ida\plugins\python.plw - true - \Python27\libs;..\..\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) - true - .\Debug/idapython.pdb - - - - - .\Debug/idapython.lib - MachineX86 - - - true - .\Debug/idapython.bsc - - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/idapython.tlb - - - - - Disabled - .\pywraps;..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) - NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=5;VER_PATCH=0;PLUGINFIX;%(PreprocessorDefinitions);__EA64__ - true - EnableFastChecks - MultiThreadedDebug - .\Debug/idapython.pch - .\Debug/ - .\Debug/ - .\Debug/ - Level3 - true - EditAndContinue - Cdecl - 4804;4800;4018;4005;%(DisableSpecificWarnings) - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;%(AdditionalDependencies) - ..\..\bin\x86_win_vc\plugins\python.p64 - true - C:\Python26\libs;..\..\lib\x86_win_vc_64;%(AdditionalLibraryDirectories) - true - .\Debug/idapython.pdb - - - - - .\Debug/idapython.lib - MachineX86 - - - true - .\Debug/idapython.bsc - - - copy ..\..\bin\x86_win_vc\plugins\python.p64 ..\..\bin\x86_win_bcc\plugins\python.p64 - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/idapython.tlb - - - - - Disabled - OnlyExplicitInline - .\pywraps;..\..\include;c:\python27\include;..\;%(AdditionalIncludeDirectories) - NO_OBSOLETE_FUNCS;NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=3;VER_PATCH=7;PLUGINFIX;4804;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/idapython.pch - .\Release/ - .\Release/ - .\Release/ - Level3 - true - ProgramDatabase - Cdecl - 4102;4005;4804;4018;4800;%(DisableSpecificWarnings) - - - NDEBUG;%(PreprocessorDefinitions) - 0x0419 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;%(AdditionalDependencies) - C:\temp\ida\plugins\python.plw - true - \Python27\libs;..\..\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) - true - .\Release/idapython.pdb - - - - - .\Release/idapython.lib - MachineX86 - - - true - .\Release/idapython.bsc - - - copy ..\..\bin\x86_win_vc\plugins\python.plw ..\..\bin\x86_win_bcc\plugins\python.plw - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/idapython.tlb - - - - - Disabled - .\pywraps;..\..\include;c:\python27\include;%(AdditionalIncludeDirectories) - NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=3;VER_PATCH=7;PLUGINFIX;%(PreprocessorDefinitions) - true - true - EnableFastChecks - MultiThreaded - false - .\Debug/idapython.pch - .\Debug/ - .\Debug/ - .\Debug/ - Level3 - true - EditAndContinue - Cdecl - 4102;4804;4800;4018;%(DisableSpecificWarnings) - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;%(AdditionalDependencies) - ../../bin/x86_win_vc/plugins/python.plw - true - C:\Python27\libs;..\..\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) - true - .\Debug/idapython.pdb - - - - - .\Debug/idapython.lib - MachineX86 - - - true - .\Debug/idapython.bsc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document - true - true - true - true - - - Document - true - true - true - true - - - Document - true - true - true - - - Document - true - true - - - Document - true - true - true - true - - - Document - true - true - true - - - - - + + + + + Debug64 + Win32 + + + Debug + Win32 + + + Release + Win32 + + + SemiDebug + Win32 + + + + {F43D6BB8-B7D6-486A-82E5-BABBA9848525} + idapython + + + + DynamicLibrary + false + MultiByte + v120 + + + DynamicLibrary + false + MultiByte + v120 + + + DynamicLibrary + false + MultiByte + v120 + + + DynamicLibrary + false + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\Debug\ + .\Debug64\ + .\Debug\ + .\Debug64\ + true + true + .\Release\ + .\Release\ + false + false + $(Configuration)\ + $(Configuration)\ + true + false + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/idapython.tlb + + + + + Disabled + .\pywraps;..\..\include;c:\python27\include;%(AdditionalIncludeDirectories) + WITH_HEXRAYS;NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=5;VER_PATCH=3;PLUGINFIX;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/idapython.pch + .\Debug/ + .\Debug/ + .\Debug/ + Level3 + true + EditAndContinue + Cdecl + 4102;4804;4800;4018;4005;%(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;%(AdditionalDependencies) + c:\temp\ida\plugins\python.plw + true + \Python27\libs;..\..\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) + true + .\Debug/idapython.pdb + + + + + .\Debug/idapython.lib + MachineX86 + + + true + .\Debug/idapython.bsc + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/idapython.tlb + + + + + Disabled + .\pywraps;..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) + NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=5;VER_PATCH=0;PLUGINFIX;%(PreprocessorDefinitions);__EA64__ + true + EnableFastChecks + MultiThreadedDebug + .\Debug/idapython.pch + .\Debug/ + .\Debug/ + .\Debug/ + Level3 + true + EditAndContinue + Cdecl + 4804;4800;4018;4005;%(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;%(AdditionalDependencies) + ..\..\bin\x86_win_vc\plugins\python.p64 + true + C:\Python26\libs;..\..\lib\x86_win_vc_64;%(AdditionalLibraryDirectories) + true + .\Debug/idapython.pdb + + + + + .\Debug/idapython.lib + MachineX86 + + + true + .\Debug/idapython.bsc + + + copy ..\..\bin\x86_win_vc\plugins\python.p64 ..\..\bin\x86_win_bcc\plugins\python.p64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/idapython.tlb + + + + + Disabled + OnlyExplicitInline + .\pywraps;..\..\include;c:\python27\include;..\;%(AdditionalIncludeDirectories) + NO_OBSOLETE_FUNCS;NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=3;VER_PATCH=7;PLUGINFIX;4804;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/idapython.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + ProgramDatabase + Cdecl + 4102;4005;4804;4018;4800;%(DisableSpecificWarnings) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0419 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;%(AdditionalDependencies) + C:\temp\ida\plugins\python.plw + true + \Python27\libs;..\..\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) + true + .\Release/idapython.pdb + + + + + .\Release/idapython.lib + MachineX86 + + + true + .\Release/idapython.bsc + + + copy ..\..\bin\x86_win_vc\plugins\python.plw ..\..\bin\x86_win_bcc\plugins\python.plw + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/idapython.tlb + + + + + Disabled + .\pywraps;..\..\include;c:\python27\include;%(AdditionalIncludeDirectories) + NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=3;VER_PATCH=7;PLUGINFIX;%(PreprocessorDefinitions) + true + true + EnableFastChecks + MultiThreaded + false + .\Debug/idapython.pch + .\Debug/ + .\Debug/ + .\Debug/ + Level3 + true + EditAndContinue + Cdecl + 4102;4804;4800;4018;%(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;%(AdditionalDependencies) + ../../bin/x86_win_vc/plugins/python.plw + true + C:\Python27\libs;..\..\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) + true + .\Debug/idapython.pdb + + + + + .\Debug/idapython.lib + MachineX86 + + + true + .\Debug/idapython.bsc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + true + true + true + + + Document + true + true + true + true + + + Document + true + true + true + + + Document + true + true + + + Document + true + true + true + true + + + Document + true + true + true + + + + + \ No newline at end of file diff --git a/idapython.vcxproj.filters b/idapython.vcxproj.filters index 881971af..9da6bb5b 100644 --- a/idapython.vcxproj.filters +++ b/idapython.vcxproj.filters @@ -1,320 +1,320 @@ - - - - - - autogen - - - - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - autogen - - - - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - autogen - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - py - - - py - - - py - - - py - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - pywraps - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - swig_i - - - TEXT - - - TEXT - - - pywraps - - - TEXT - - - TEXT - - - swig_i - - - py - - - - - {16b55e12-2b1e-4d6d-a1bf-df3400f06d21} - - - {f733d65b-1c25-4587-8566-7000875727ff} - - - {e2ec193c-4803-45b0-96c8-bfdc173c14ff} - - - {01459f9f-5d55-4797-aab4-81876a9163d3} - - - {b581ad45-b3f6-4591-baf0-306dab4e0590} - - + + + + + + autogen + + + + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + autogen + + + + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + autogen + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + py + + + py + + + py + + + py + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + pywraps + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + swig_i + + + TEXT + + + TEXT + + + pywraps + + + TEXT + + + TEXT + + + swig_i + + + py + + + + + {16b55e12-2b1e-4d6d-a1bf-df3400f06d21} + + + {f733d65b-1c25-4587-8566-7000875727ff} + + + {e2ec193c-4803-45b0-96c8-bfdc173c14ff} + + + {01459f9f-5d55-4797-aab4-81876a9163d3} + + + {b581ad45-b3f6-4591-baf0-306dab4e0590} + + \ No newline at end of file diff --git a/patch_directors_cc.py b/patch_directors_cc.py index eaa38d81..07450188 100644 --- a/patch_directors_cc.py +++ b/patch_directors_cc.py @@ -13,6 +13,9 @@ sys.exit(1) patches = [ + # misc + "virtual int idaapi print", + # user_lvar_visitor_t "virtual int idaapi handle_retrieved_info", "virtual int idaapi handle_retrieved_mapping", diff --git a/python.cfg b/python.cfg index 1ab7d582..d71cccc5 100644 --- a/python.cfg +++ b/python.cfg @@ -8,7 +8,3 @@ REMOVE_CWD_SYS_PATH = 1 // Script timeout (in seconds) // (A value of 0 disables the timeout) SCRIPT_TIMEOUT = 3 - -// Use a local Python library -// If enabled, the "lib" directory tree with modules must be present in IDADIR/python -USE_LOCAL_PYTHON = 0 diff --git a/python.cpp b/python.cpp index a43e6f18..56e200d1 100644 --- a/python.cpp +++ b/python.cpp @@ -1,1784 +1,1830 @@ -//--------------------------------------------------------------------- -// IDAPython - Python plugin for Interactive Disassembler -// -// Copyright (c) The IDAPython Team -// -// All rights reserved. -// -// For detailed copyright information see the file COPYING in -// the root of the distribution archive. -//--------------------------------------------------------------------- -// python.cpp - Main plugin code -//--------------------------------------------------------------------- -#include - -//------------------------------------------------------------------------- -// This define fixes the redefinition of ssize_t -#ifdef HAVE_SSIZE_T -#define _SSIZE_T_DEFINED 1 -#endif - -#ifdef __LINUX__ -#include -#endif -#ifdef __MAC__ -#include -#endif -#include -#include -#include -#include -#include -#include - -#ifdef WITH_HEXRAYS -#include -hexdsp_t *hexdsp = NULL; -#endif - -#include "pywraps.hpp" - -//------------------------------------------------------------------------- -// Defines and constants - -// Python-style version tuple comes from the makefile -// Only the serial and status is set here -#define VER_SERIAL 0 -#define VER_STATUS "final" -#define IDAPYTHON_RUNSTATEMENT 0 -#define IDAPYTHON_ENABLE_EXTLANG 3 -#define IDAPYTHON_DISABLE_EXTLANG 4 -#define PYTHON_DIR_NAME "python" -#define S_IDAPYTHON "IDAPython" -#define S_INIT_PY "init.py" -static const char S_IDC_ARGS_VARNAME[] = "ARGV"; -static const char S_MAIN[] = "__main__"; -static const char S_IDC_RUNPYTHON_STATEMENT[] = "RunPythonStatement"; -static const char S_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; - -//------------------------------------------------------------------------- -// Types - -// -enum script_run_when -{ - run_on_db_open = 0, // run script after opening database (default) - run_on_ui_ready = 1, // run script when UI is ready - run_on_init = 2, // run script immediately on plugin load (shortly after IDA starts) -}; - -//------------------------------------------------------------------------- -// Global variables -static bool g_instance_initialized = false; // This instance of the plugin is the one - // that initialized the python interpreter. -static int g_run_when = -1; -static char g_run_script[QMAXPATH]; -static char g_idapython_dir[QMAXPATH]; - -//------------------------------------------------------------------------- -// Prototypes and forward declarations - -// Alias to SWIG_Init -//lint -esym(526,init_idaapi) not defined -extern "C" void init_idaapi(void); - -// Plugin run() callback -void idaapi run(int arg); - -//------------------------------------------------------------------------- -// This is a simple tracing code for debugging purposes. -// It might evolve into a tracing facility for user scripts. - -//#define ENABLE_PYTHON_PROFILING -#ifdef ENABLE_PYTHON_PROFILING -#include "compile.h" -#include "frameobject.h" - -int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) -{ - PyObject *str; - - /* Catch line change events. */ - /* Print the filename and line number */ - if ( what == PyTrace_LINE ) - { - str = PyObject_Str(frame->f_code->co_filename); - if ( str ) - { - msg("PROFILING: %s:%d\n", PyString_AsString(str), frame->f_lineno); - Py_DECREF(str); - } - } - return 0; -} -#endif - -//------------------------------------------------------------------------- -// Helper routines to make Python script execution breakable from IDA -static int ninsns = 0; // number of times trace function was called -static bool box_displayed; // has the wait box been displayed? -static time_t start_time; // the start time of the execution -static int script_timeout = 2; -static bool g_ui_ready = false; -static bool g_alert_auto_scripts = true; -static bool g_remove_cwd_sys_path = false; -static bool g_use_local_python = false; - -static void end_execution(void); -static void begin_execution(void); - -//------------------------------------------------------------------------ -// This callback is called on various interpreter events -static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) -{ - if ( wasBreak() ) - { - // User pressed Cancel in the waitbox; send KeyboardInterrupt exception - PyErr_SetString(PyExc_KeyboardInterrupt, "User interrupted"); - return -1; - } - else if ( !box_displayed && ++ninsns > 10 ) - { - // We check the timer once every 10 calls - ninsns = 0; - - // Timeout disabled or elapsed? - if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) - { - box_displayed = true; - show_wait_box("Running Python script"); - } - } -#ifdef ENABLE_PYTHON_PROFILING - return tracefunc(obj, frame, what, arg); -#else - qnotused(obj); - qnotused(frame); - qnotused(what); - qnotused(arg); - return 0; -#endif -} - -//------------------------------------------------------------------------ -static void reset_execution_time() -{ - start_time = time(NULL); - ninsns = 0; -} - -//------------------------------------------------------------------------ -// Prepare for Python execution -static void begin_execution() -{ - if ( !g_ui_ready || script_timeout == 0 ) - return; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - end_execution(); - reset_execution_time(); - PyEval_SetTrace(break_check, NULL); -} - -//--------------------------------------------------------------------------- -static void hide_script_waitbox() -{ - if ( box_displayed ) - { - hide_wait_box(); - box_displayed = false; - } -} - -//------------------------------------------------------------------------ -// Called after Python execution finishes -static void end_execution() -{ - hide_script_waitbox(); - PYW_GIL_CHECK_LOCKED_SCOPE(); -#ifdef ENABLE_PYTHON_PROFILING - PyEval_SetTrace(tracefunc, NULL); -#else - PyEval_SetTrace(NULL, NULL); -#endif -} - -//------------------------------------------------------------------------- -//lint -esym(714,disable_script_timeout) Symbol not referenced -void disable_script_timeout() -{ - // Clear timeout - script_timeout = 0; - - // Uninstall the trace function and hide the waitbox (if it was shown) - end_execution(); -} - -//------------------------------------------------------------------------- -//lint -esym(714,set_script_timeout) Symbol not referenced -int set_script_timeout(int timeout) -{ - // Update the timeout - qswap(timeout, script_timeout); - - // Reset the execution time and hide the waitbox (so it is shown again after timeout elapses) - reset_execution_time(); - hide_script_waitbox(); - - return timeout; -} - -//------------------------------------------------------------------------ -// Return a formatted error or just print it to the console -static void handle_python_error( - char *errbuf, - size_t errbufsize, - bool clear_error = true) -{ - if ( errbufsize > 0 ) - errbuf[0] = '\0'; - - // No exception? - if ( !PyErr_Occurred() ) - return; - - qstring s; - if ( PyW_GetError(&s, clear_error) ) - qstrncpy(errbuf, s.c_str(), errbufsize); -} - -//------------------------------------------------------------------------ -// Helper function to get globals for the __main__ module -// Note: The references are borrowed. No need to free them. -static PyObject *GetMainGlobals() -{ - PyObject *module = PyImport_AddModule(S_MAIN); - return module == NULL ? NULL : PyModule_GetDict(module); -} - -//------------------------------------------------------------------------ -static void PythonEvalOrExec( - const char *str, - const char *filename = "") -{ - // Compile as an expression - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyCompilerFlags cf = {0}; - newref_t py_code(Py_CompileStringFlags(str, filename, Py_eval_input, &cf)); - if ( py_code == NULL || PyErr_Occurred() ) - { - // Not an expression? - PyErr_Clear(); - - // Run as a string - PyRun_SimpleString(str); - } - else - { - PyObject *py_globals = GetMainGlobals(); - newref_t py_result( - PyEval_EvalCode( - (PyCodeObject *) py_code.o, - py_globals, - py_globals)); - - if ( py_result == NULL || PyErr_Occurred() ) - { - PyErr_Print(); - } - else - { - if ( py_result.o != Py_None ) - { - bool ok = false; - if ( PyUnicode_Check(py_result.o) ) - { - newref_t py_result_utf8(PyUnicode_AsUTF8String(py_result.o)); - ok = py_result_utf8 != NULL; - if ( ok ) - umsg("%s\n", PyString_AS_STRING(py_result_utf8.o)); - } - else - { - qstring result_str; - ok = PyW_ObjectToString(py_result.o, &result_str); - if ( ok ) - msg("%s\n", result_str.c_str()); - } - - if ( !ok ) - msg("*** IDAPython: Couldn't convert evaluation result\n"); - } - } - } -} - -//------------------------------------------------------------------------ -// Executes a simple string -static bool idaapi IDAPython_extlang_run_statements( - const char *str, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - PyObject *globals = GetMainGlobals(); - bool ok; - if ( globals == NULL ) - { - ok = false; - } - else - { - errbuf[0] = '\0'; - PyErr_Clear(); - begin_execution(); - newref_t result(PyRun_String( - str, - Py_file_input, - globals, - globals)); - end_execution(); - ok = result != NULL && !PyErr_Occurred(); - if ( !ok ) - handle_python_error(errbuf, errbufsize); - } - if ( !ok && errbuf[0] == '\0' ) - qstrncpy(errbuf, "internal error", errbufsize); - return ok; -} - -//------------------------------------------------------------------------ -// Simple Python statement runner function for IDC -static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; -static error_t idaapi idc_runpythonstatement( - idc_value_t *argv, - idc_value_t *res) -{ - char errbuf[MAXSTR]; - bool ok = IDAPython_extlang_run_statements(argv[0].c_str(), errbuf, sizeof(errbuf)); - - if ( ok ) - res->set_long(0); - else - res->set_string(errbuf); - - return eOk; -} - -//-------------------------------------------------------------------------- -const char *idaapi set_python_options( - const char *keyword, - int value_type, - const void *value) -{ - do - { - if ( value_type == IDPOPT_NUM ) - { - if ( qstrcmp(keyword, "SCRIPT_TIMEOUT") == 0 ) - { - script_timeout = int(*(uval_t *)value); - break; - } - else if ( qstrcmp(keyword, "ALERT_AUTO_SCRIPTS") == 0 ) - { - g_alert_auto_scripts = *(uval_t *)value != 0; - break; - } - else if ( qstrcmp(keyword, "REMOVE_CWD_SYS_PATH") == 0 ) - { - g_remove_cwd_sys_path = *(uval_t *)value != 0; - break; - } - else if ( qstrcmp(keyword, "USE_LOCAL_PYTHON") == 0 ) - { - g_use_local_python = *(uval_t *)value != 0; - break; - } - } - return IDPOPT_BADKEY; - } while (false); - return IDPOPT_OK; -} - -//------------------------------------------------------------------------- -// Check for the presence of a file in IDADIR/python and complain on error -bool CheckScriptFiles() -{ - static const char *const script_files[] = - { - S_IDC_MODNAME ".py", - S_INIT_PY, - "idaapi.py", - "idautils.py" - }; - for ( size_t i=0; i 0 ) - errbuf[0] = '\0'; - - if ( py_result == NULL ) - { - handle_python_error(errbuf, errbufsize); - return false; - } - - int cvt = CIP_OK; - if ( idc_result != NULL ) - { - idc_result->clear(); - cvt = pyvar_to_idcvar(py_result, idc_result); - if ( cvt < CIP_OK ) - qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); - } - - return cvt >= CIP_OK; -} - -//------------------------------------------------------------------------- -// This function will call the Python function 'idaapi.IDAPython_ExecFile' -// It does not use 'import', thus the executed script will not yield a new module name -// It returns the exception and traceback information. -// We use the Python function to execute the script because it knows how to deal with -// module reloading. -static bool IDAPython_ExecFile( - const char *FileName, - char *errbuf, - size_t errbufsz, - const char *idaapi_script = S_IDAAPI_EXECSCRIPT, - idc_value_t *second_res = NULL, - bool want_tuple = false) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_execscript(get_idaapi_attr(idaapi_script)); - if ( py_execscript == NULL ) - { - qsnprintf(errbuf, errbufsz, "Could not find idaapi.%s ?!", idaapi_script); - return false; - } - - char script[MAXSTR]; - qstrncpy(script, FileName, sizeof(script)); - strrpl(script, '\\', '/'); - - newref_t py_script(PyString_FromString(script)); - newref_t py_ret(PyObject_CallFunctionObjArgs( - py_execscript.o, - py_script.o, - GetMainGlobals(), - NULL)); - - // Failure at this point means the script was interrupted - bool interrupted = false; - qstring err; - if ( PyW_GetError(&err) || py_ret == NULL ) - { - PyErr_Clear(); - if ( err.empty() ) - qstrncpy(errbuf, "Script interrupted", errbufsz); - else - qstrncpy(errbuf, err.c_str(), errbufsz); - interrupted = true; - } - - bool ok = false; - if ( !interrupted ) - { - PyObject *ret_o; - if ( want_tuple ) - { - if ( second_res != NULL - && PyTuple_Check(py_ret.o) - && PyTuple_Size(py_ret.o) == 2 ) - { - ret_o = PyTuple_GetItem(py_ret.o, 0); // Borrowed reference - } - else - { - INTERR(30444); - } - } - else - { - ret_o = py_ret.o; - } - - if ( ret_o == Py_None ) - { - if ( want_tuple ) - { - borref_t ret2_o(PyTuple_GetItem(py_ret.o, 1)); - ok = return_python_result(second_res, ret2_o, errbuf, errbufsz); - } - else - { - ok = true; - } - } - else if ( PyString_Check(ret_o) ) - { - qstrncpy(errbuf, PyString_AsString(ret_o), errbufsz); - } - else - { - INTERR(30154); - } - } - return ok; -} - -//------------------------------------------------------------------------- -// Execute the Python script from the plugin -static bool RunScript(const char *script) -{ - begin_execution(); - - char errbuf[MAXSTR]; - bool ok = IDAPython_ExecFile(script, errbuf, sizeof(errbuf)); - if ( !ok ) - warning("IDAPython: error executing '%s':\n%s", script, errbuf); - - end_execution(); - return ok; -} - -//------------------------------------------------------------------------- -// This function parses a name into two different components (if it applies). -// Example: -// parse_py_modname("modname.attrname", mod_buf, attr_buf) -// It splits the full name into two parts. -static bool parse_py_modname( - const char *full_name, - char *modname, - char *attrname, - size_t sz, - const char *defmod = S_IDAAPI_MODNAME) -{ - const char *p = strchr(full_name, '.'); - if ( p == NULL ) - { - qstrncpy(modname, defmod, sz); - qstrncpy(attrname, full_name, sz); - } - else - { - qstrncpy(modname, full_name, p - full_name + 1); - qstrncpy(attrname, p + 1, sz); - } - return p != NULL; -} - -//------------------------------------------------------------------------- -// Run callback for Python external language evaluator -bool idaapi IDAPython_extlang_run( - const char *name, - int nargs, - const idc_value_t args[], - idc_value_t *result, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - // Try to extract module name (if any) from the funcname - char modname[MAXSTR] = {0}; - char funcname[MAXSTR] = {0}; - bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); - - bool ok = true; - PyObject *module = NULL; - ref_vec_t pargs; - do - { - // Convert arguments to python - ok = pyw_convert_idc_args(args, nargs, pargs, false, errbuf, errbufsize); - if ( !ok ) - break; - - if ( imported_module ) - { - module = PyImport_ImportModule(modname); - } - else - { - module = PyImport_AddModule(S_MAIN); - QASSERT(30156, module != NULL); - } - - PyObject *globals = PyModule_GetDict(module); - QASSERT(30157, globals != NULL); - - PyObject *func = PyDict_GetItemString(globals, funcname); - if ( func == NULL ) - { - qsnprintf(errbuf, errbufsize, "undefined function %s", name); - ok = false; - break; - } - - borref_t code(PyFunction_GetCode(func)); - qvector pargs_ptrs; - pargs.to_pyobject_pointers(&pargs_ptrs); - newref_t py_res(PyEval_EvalCodeEx( - (PyCodeObject*) code.o, - globals, NULL, - pargs_ptrs.begin(), - nargs, - NULL, 0, NULL, 0, NULL)); - ok = return_python_result(result, py_res, errbuf, errbufsize); - } while ( false ); - - if ( imported_module ) - Py_XDECREF(module); - return ok; -} - -//------------------------------------------------------------------------- -static void wrap_in_function(qstring *out, const qstring &body, const char *name) -{ - out->sprnt("def %s():\n", name); - // dont copy trailing whitespace - int i = body.length()-1; - while ( i >= 0 && qisspace(body.at(i)) ) - i--; - out->append(body.substr(0, i+1)); - out->replace("\n", "\n "); -} - -//------------------------------------------------------------------------- -// Compile callback for Python external language evaluator -bool idaapi IDAPython_extlang_compile( - const char *name, - ea_t /*current_ea*/, - const char *expr, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - PyObject *globals = GetMainGlobals(); - bool is_func = false; - - PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); - if ( code == NULL ) - { - // try compiling as a list of statements - // wrap them into a function - handle_python_error(errbuf, errbufsize); - qstring func; - wrap_in_function(&func, expr, name); - code = (PyCodeObject *)Py_CompileString(func.c_str(), "", Py_file_input); - if ( code == NULL ) - { - handle_python_error(errbuf, errbufsize); - return false; - } - is_func = true; - } - - // Set the desired function name - Py_XDECREF(code->co_name); - code->co_name = PyString_FromString(name); - - // Create a function out of code - PyObject *func = PyFunction_New((PyObject *)code, globals); - - if ( func == NULL ) - { -ERR: - handle_python_error(errbuf, errbufsize); - Py_XDECREF(code); - return false; - } - - int err = PyDict_SetItemString(globals, name, func); - Py_XDECREF(func); - - if ( err ) - goto ERR; - - if ( is_func ) - { - const idc_value_t args; - idc_value_t result; - return IDAPython_extlang_run(name, 0, &args, &result, errbuf, errbufsize); - } - return true; -} - -//------------------------------------------------------------------------- -// Compile callback for Python external language evaluator -bool idaapi IDAPython_extlang_compile_file( - const char *filename, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - begin_execution(); - bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize); - end_execution(); - return ok; -} - -//------------------------------------------------------------------------- -// Load processor module callback for Python external language evaluator -static bool idaapi IDAPython_extlang_loadprocmod( - const char *filename, - idc_value_t *procobj, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - begin_execution(); - bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_LOADPROCMOD, procobj, true); - if ( ok && procobj->is_zero() ) - { - errbuf[0] = '\0'; - ok = false; - } - end_execution(); - return ok; -} - -//------------------------------------------------------------------------- -// Unload processor module callback for Python external language evaluator -static bool idaapi IDAPython_extlang_unloadprocmod( - const char *filename, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - begin_execution(); - bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_UNLOADPROCMOD); - end_execution(); - return ok; -} - -//------------------------------------------------------------------------- -// Create an object instance -bool idaapi IDAPython_extlang_create_object( - const char *name, // in: object class name - int nargs, // in: number of input arguments - const idc_value_t args[], // in: input arguments - idc_value_t *result, // out: created object or exception - char *errbuf, // out: error message if evaluation fails - size_t errbufsize) // in: size of the error buffer -{ - PYW_GIL_GET; - bool ok = false; - ref_vec_t pargs; - do - { - // Parse the object name (to get the module and class name) - char modname[MAXSTR] = {0}; - char clsname[MAXSTR] = {0}; - parse_py_modname(name, modname, clsname, MAXSTR); - - // Get a reference to the module - ref_t py_mod(PyW_TryImportModule(modname)); - if ( py_mod == NULL ) - { - qsnprintf(errbuf, errbufsize, "Could not import module '%s'!", modname); - break; - } - - // Get the class reference - ref_t py_cls(PyW_TryGetAttrString(py_mod.o, clsname)); - if ( py_cls == NULL ) - { - qsnprintf(errbuf, errbufsize, "Could not find class type '%s'!", clsname); - break; - } - - // Error during conversion? - ok = pyw_convert_idc_args(args, nargs, pargs, true, errbuf, errbufsize); - if ( !ok ) - break; - - // Call the constructor - newref_t py_res(PyObject_CallObject(py_cls.o, pargs.empty() ? NULL : pargs[0].o)); - ok = return_python_result(result, py_res, errbuf, errbufsize); - } while ( false ); - - return ok; -} - -//------------------------------------------------------------------------- -// Returns the attribute value of a given object from the global scope -bool idaapi IDAPython_extlang_get_attr( - const idc_value_t *obj, // in: object (may be NULL) - const char *attr, // in: attribute name - idc_value_t *result) -{ - PYW_GIL_GET; - int cvt = CIP_FAILED; - do - { - // Get a reference to the module - ref_t py_mod(PyW_TryImportModule(S_MAIN)); - if ( py_mod == NULL ) - break; - - // Object specified: - // - (1) string contain attribute name in the main module - // - (2) opaque object (we use it as is) - ref_t py_obj; - if ( obj != NULL ) - { - // (1) Get attribute from main module - if ( obj->vtype == VT_STR2 ) - { - py_obj = PyW_TryGetAttrString(py_mod.o, obj->c_str()); - } - // (2) see if opaque object - else - { - // Convert object (expecting opaque object) - cvt = idcvar_to_pyvar(*obj, &py_obj); - if ( cvt != CIP_OK_OPAQUE ) // Only opaque objects are accepted - { - py_obj = ref_t(); - cvt = CIP_FAILED; - break; - } - } - // Get the attribute reference - if ( py_obj == NULL ) - break; - } - // No object specified: - else - { - // ...then work with main module - py_obj = py_mod; - } - // Special case: if attribute not passed then retrieve the class - // name associated associated with the passed object - if ( attr == NULL || attr[0] == '\0' ) - { - cvt = CIP_FAILED; - // Get the class - newref_t cls(PyObject_GetAttrString(py_obj.o, "__class__")); - if ( cls == NULL ) - break; - - // Get its name - newref_t name(PyObject_GetAttrString(cls.o, "__name__")); - if ( name == NULL ) - break; - - // Convert name object to string object - newref_t string(PyObject_Str(name.o)); - if ( string == NULL ) - break; - - // Convert name python string to a C string - const char *clsname = PyString_AsString(string.o); - if ( clsname == NULL ) - break; - - result->set_string(clsname); - cvt = CIP_OK; //lint !e838 - break; - } - - ref_t py_attr(PyW_TryGetAttrString(py_obj.o, attr)); - // No attribute? - if ( py_attr == NULL ) - { - cvt = CIP_FAILED; - break; - } - // Don't store result - if ( result == NULL ) - { - cvt = CIP_OK; - // Decrement attribute (because of GetAttrString) - } - else - { - cvt = pyvar_to_idcvar(py_attr, result); - // // Conversion succeeded and opaque object was passed: - // // Since the object will be passed to IDC, it is likely that IDC value will be - // // destroyed and also destroying the opaque object with it. That is an undesired effect. - // // We increment the reference of the object so that even if the IDC value dies - // // the opaque object remains. So by not decrement reference after GetAttrString() call - // // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. - // if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) - // { - // // Decrement the reference (that was incremented by GetAttrString()) - // py_attr.decref(); - // } - } - } while ( false ); - return cvt >= CIP_OK; -} - -//------------------------------------------------------------------------- -// Returns the attribute value of a given object from the global scope -//lint -e{818} -bool idaapi IDAPython_extlang_set_attr( - idc_value_t *obj, // in: object name (may be NULL) - const char *attr, // in: attribute name - idc_value_t *value) -{ - PYW_GIL_GET; - bool ok = false; - do - { - // Get a reference to the module - ref_t py_mod(PyW_TryImportModule(S_MAIN)); - if ( py_mod == NULL ) - break; - ref_t py_obj; - if ( obj != NULL ) - { - // Get the attribute reference (from just a name) - if ( obj->vtype == VT_STR2 ) - { - py_obj = PyW_TryGetAttrString(py_mod.o, obj->c_str()); - } - else - { - int cvt = idcvar_to_pyvar(*obj, &py_obj); - if ( cvt != CIP_OK_OPAQUE ) // Only opaque objects are accepted - py_obj = ref_t(); - } - // No object to set_attr on? - if ( py_obj == NULL ) - break; - } - else - { - // set_attr on the main module - py_obj = py_mod; - } - // Convert the value - ref_t py_var; - int cvt = idcvar_to_pyvar(*value, &py_var); - if ( cvt >= CIP_OK ) - { - ok = PyObject_SetAttrString(py_obj.o, attr, py_var.o) != -1; - // if ( cvt != CIP_OK_NODECREF ) - // Py_XDECREF(py_var); - } - } while ( false ); - return ok; -} - -//------------------------------------------------------------------------- -// Calculator callback for Python external language evaluator -//lint -e{818} -bool idaapi IDAPython_extlang_calcexpr( - ea_t /*current_ea*/, - const char *expr, - idc_value_t *rv, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - PyObject *globals = GetMainGlobals(); - bool ok = globals != NULL; - ref_t result; - if ( ok ) - { - begin_execution(); - result = newref_t(PyRun_String(expr, Py_eval_input, globals, globals)); - end_execution(); - ok = return_python_result(rv, result, errbuf, errbufsize); - } - return ok; -} - -//------------------------------------------------------------------------- -bool idaapi IDAPython_extlang_call_method( - const idc_value_t *idc_obj, - const char *method_name, - int nargs, - const idc_value_t args[], - idc_value_t *result, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_GET; - // Check for unsupported usage of call_method. - // Mainly a method call requires an object and a method. - if ( (idc_obj == NULL && method_name == NULL) || (idc_obj != NULL && method_name == NULL) ) - { - qstrncpy(errbuf, "call_method does not support this operation", errbufsize); - return false; - } - // Behave like run() - else if ( idc_obj == NULL && method_name != NULL ) - { - return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); - } - - // Holds conversion status of input object - int obj_cvt; - bool ok = false; - ref_vec_t pargs; - do - { - // Convert input object - ref_t py_obj; - obj_cvt = idcvar_to_pyvar(*idc_obj, &py_obj); - if ( obj_cvt < CIP_OK ) - { - qstrncpy(errbuf, "Failed to convert input object to Python value", errbufsize); - break; - } - - ref_t py_method(PyW_TryGetAttrString(py_obj.o, method_name)); - if ( py_method == NULL || !PyCallable_Check(py_method.o) ) - { - qsnprintf(errbuf, errbufsize, "The input object does not have a callable method called '%s'", method_name); - break; - } - - // Convert arguments to python objects - ok = pyw_convert_idc_args(args, nargs, pargs, true, errbuf, errbufsize); - if ( !ok ) - break; - - newref_t py_res(PyObject_CallObject(py_method.o, pargs.empty() ? NULL : pargs[0].o)); - ok = return_python_result(result, py_res, errbuf, errbufsize); - } while ( false ); - - return ok; -} - -//------------------------------------------------------------------------- -const extlang_t extlang_python = -{ - sizeof(extlang_t), - 0, - "Python", - IDAPython_extlang_compile, - IDAPython_extlang_run, - IDAPython_extlang_calcexpr, - IDAPython_extlang_compile_file, - "py", - IDAPython_extlang_create_object, - IDAPython_extlang_get_attr, - IDAPython_extlang_set_attr, - IDAPython_extlang_call_method, - IDAPython_extlang_run_statements, - IDAPython_extlang_loadprocmod, - IDAPython_extlang_unloadprocmod, -}; - -//------------------------------------------------------------------------- -void enable_extlang_python(bool enable) -{ - if ( enable ) - select_extlang(&extlang_python); - else - select_extlang(NULL); -} - -//------------------------------------------------------------------------- -// Execute a line in the Python CLI -bool idaapi IDAPython_cli_execute_line(const char *line) -{ - PYW_GIL_GET; - - // Do not process empty lines - if ( line[0] == '\0' ) - return true; - - const char *last_line = strrchr(line, '\n'); - if ( last_line == NULL ) - last_line = line; - else - last_line += 1; - - // Skip empty lines - if ( last_line[0] != '\0' ) - { - // Line ends with ":" or begins with a space character? - bool more = last_line[qstrlen(last_line)-1] == ':' || qisspace(last_line[0]); - if ( more ) - return false; - } - - // - // Pseudo commands - // - qstring s; - do - { - // Help command? - if ( line[0] == '?' ) - s.sprnt("help(%s)", line+1); - // Shell command? - else if ( line[0] == '!' ) - s.sprnt("idaapi.IDAPython_ExecSystem(r'%s')", line+1); - else - break; - // Patch the command line pointer - line = s.c_str(); - } while (false); - - begin_execution(); - PythonEvalOrExec(line); - end_execution(); - - return true; -} - -//------------------------------------------------------------------------- -bool idaapi IDAPYthon_cli_complete_line( - qstring *completion, - const char *prefix, - int n, - const char *line, - int x) -{ - PYW_GIL_GET; - - ref_t py_complete(get_idaapi_attr(S_IDAAPI_COMPLETION)); - if ( py_complete == NULL ) - return false; - - newref_t py_ret(PyObject_CallFunction(py_complete.o, "sisi", prefix, n, line, x)); //lint !e605 - - // Swallow the error - PyW_GetError(completion); - - bool ok = py_ret != NULL && PyString_Check(py_ret.o) != 0; - if ( ok ) - *completion = PyString_AS_STRING(py_ret.o); - return ok; -} - -//------------------------------------------------------------------------- -static const cli_t cli_python = -{ - sizeof(cli_t), - 0, - "Python", - "Python - IDAPython plugin", - "Enter any Python expression", - IDAPython_cli_execute_line, - IDAPYthon_cli_complete_line, - NULL -}; - -//------------------------------------------------------------------------- -// Control the Python CLI status -void enable_python_cli(bool enable) -{ - if ( enable ) - install_command_interpreter(&cli_python); - else - remove_command_interpreter(&cli_python); -} - -//------------------------------------------------------------------------- -// Prints the IDAPython copyright banner -void py_print_banner() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyRun_SimpleString("print_banner()"); -} - -//------------------------------------------------------------------------ -// Parse plugin options -void parse_plugin_options() -{ - // Get options from IDA - const char *options = get_plugin_options(S_IDAPYTHON); - - // No options? - if ( options == NULL ) - return; - - // User specified 'when' parameter? - const char *p = strchr(options, ';'); - if ( p == NULL ) - { - g_run_when = run_on_db_open; - p = options; - } - else - { - g_run_when = atoi(options); - ++p; - } - qstrncpy(g_run_script, p, sizeof(g_run_script)); -} - -//------------------------------------------------------------------------ -// Converts the global IDC variable "ARGV" into a Python variable. -// The arguments will then be accessible via 'idc' module / 'ARGV' variable. -void convert_idc_args() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_args(PyList_New(0)); - - idc_value_t *idc_args = find_idc_gvar(S_IDC_ARGS_VARNAME); - if ( idc_args != NULL ) - { - idc_value_t attr; - char attr_name[20] = {"0"}; - for ( int i=1; VarGetAttr(idc_args, attr_name, &attr) == eOk; i++ ) - { - PyList_Insert(py_args.o, i, PyString_FromString(attr.c_str())); - qsnprintf(attr_name, sizeof(attr_name), "%d", i); - } - } - - // Get reference to the IDC module (it is imported by init.py) - ref_t py_mod(PyW_TryImportModule(S_IDC_MODNAME)); - if ( py_mod != NULL ) - PyObject_SetAttrString(py_mod.o, S_IDC_ARGS_VARNAME, py_args.o); -} - -#ifdef WITH_HEXRAYS -//------------------------------------------------------------------------- -static bool is_hexrays_plugin(const plugin_info_t *pinfo) -{ - bool is_hx = false; - if ( pinfo != NULL && pinfo->entry != NULL ) - { - const plugin_t *p = pinfo->entry; - if ( streq(p->wanted_name, "Hex-Rays Decompiler") ) - is_hx = true; - } - return is_hx; -} -#endif - - -//------------------------------------------------------------------------ -//lint -esym(715,va) Symbol not referenced -static int idaapi on_ui_notification(void *, int code, va_list va) -{ -#ifdef WITH_HEXRAYS - qnotused(va); -#endif - switch ( code ) - { - case ui_ready_to_run: - { - PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. - g_ui_ready = true; - py_print_banner(); - - if ( g_run_when == run_on_ui_ready ) - RunScript(g_run_script); - } - break; - - case ui_database_inited: - { - PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. - convert_idc_args(); - if ( g_run_when == run_on_db_open ) - RunScript(g_run_script); - } - break; - -#ifdef WITH_HEXRAYS - case ui_plugin_loaded: - if ( hexdsp == NULL ) - { - if ( is_hexrays_plugin(va_arg(va, plugin_info_t *)) ) - { - init_hexrays_plugin(0); - if ( hexdsp != NULL ) - msg("IDAPython Hex-Rays bindings initialized.\n"); - } - } - break; - - case ui_plugin_unloading: - { - if ( hexdsp != NULL ) - { - // Hex-Rays will close. Make sure all the refcounted cfunc_t objects - // are cleared right away. - if ( is_hexrays_plugin(va_arg(va, plugin_info_t *)) ) - { - hexrays_clear_python_cfuncptr_t_references(); - hexdsp = NULL; - } - } - } - break; -#endif - - default: - break; - } - return 0; -} - -//------------------------------------------------------------------------- -//lint -esym(526,til_clear_python_tinfo_t_instances) not defined -extern void til_clear_python_tinfo_t_instances(void); -static int idaapi on_idp_notification(void *, int code, va_list) -{ - switch ( code ) - { - case processor_t::closebase: - // The til machinery is about to garbage-collect: We must go - // through all the tinfo_t objects that are embedded in SWIG wrappers, - // (i.e., that were created from Python) and clear those. - til_clear_python_tinfo_t_instances(); - break; - } - return 0; -} - -#ifdef _DEBUG -//------------------------------------------------------------------------ -// extern int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag); -extern PyThreadState *_PyThreadState_Current; -static int idaapi ui_debug_handler_cb(void *, int code, va_list) -{ - // This hook gets called from the kernel, but its very point is to - // make sure that we don't hold the GIL. Thus: No PYW_GIL_GET here! - switch ( code ) - { - case debug_assert_thread_waitready: - // We will *always* be in a non-main thread when this is called. - if ( _PyThreadState_Current != NULL ) - { - PyThreadState *tcur = PyGILState_GetThisThreadState(); - if ( tcur == _PyThreadState_Current ) - { - // This thread is the '_PyThreadState_Current'; i.e., it holds the lock. - // We're likely to end up in a deadlock. - BPT; - } - } - break; - default: - break; - } - return 0; -} -#endif - -//------------------------------------------------------------------------- -// remove current directory (empty entry) from the sys.path -static void sanitize_path() -{ - char buf[QMAXPATH]; - qstrncpy(buf, Py_GetPath(), sizeof(buf)); - char *ctx; - qstring newpath; - for ( char *d0 = qstrtok(buf, DELIMITER, &ctx); - d0 != NULL; - d0 = qstrtok(NULL, DELIMITER, &ctx) ) - { - if ( d0[0] == '\0' ) - // skip empty entry - continue; - - if ( !newpath.empty() ) - newpath.append(DELIMITER); - newpath.append(d0); - } - PySys_SetPath(newpath.begin()); -} - - -//------------------------------------------------------------------------- -// we have to do it ourselves because Python 2.7 calls exit() if importing site fails -static bool initsite(void) -{ - PyObject *m; - m = PyImport_ImportModule("site"); - if ( m == NULL ) - { - PyErr_Print(); - Py_Finalize(); - return false; - } - else - { - Py_DECREF(m); - } - return true; -} - -//------------------------------------------------------------------------- -// Initialize the Python environment -bool IDAPython_Init(void) -{ - if ( Py_IsInitialized() != 0 ) - return true; - - // Form the absolute path to IDA\python folder - qstrncpy(g_idapython_dir, idadir(PYTHON_DIR_NAME), sizeof(g_idapython_dir)); - - // Check for the presence of essential files - if ( !CheckScriptFiles() ) - return false; - - char tmp[QMAXPATH]; -#ifdef __LINUX__ - // Export symbols from libpython to resolve imported module deps - // use the standard soname: libpython2.7.so.1.0 -#define PYLIB "libpython" QSTRINGIZE(PY_MAJOR_VERSION) "." QSTRINGIZE(PY_MINOR_VERSION) ".so.1.0" - if ( !dlopen(PYLIB, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY) ) - { - warning("IDAPython dlopen(" PYLIB ") error: %s", dlerror()); - return false; - } -#endif - -#ifdef __MAC__ - // We should set python home to the module's path, otherwise it can pick up stray modules from $PATH - NSModule pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_InitializeEx")); - // Use dylib functions to find out where the framework was loaded from - const char *buf = (char *)NSLibraryNameForModule(pythonModule); - if ( buf != NULL ) - { - // The path will be something like: - // /System/Library/Frameworks/Python.framework/Versions/2.5/Python - // We need to strip the last part - // use static buffer because Py_SetPythonHome() just stores a pointer - static char pyhomepath[MAXSTR]; - qstrncpy(pyhomepath, buf, MAXSTR); - char * lastslash = strrchr(pyhomepath, '/'); - if ( lastslash != NULL ) - { - *lastslash = 0; - Py_SetPythonHome(pyhomepath); - } - } -#endif - - // Read configuration value - read_user_config_file("python.cfg", set_python_options, NULL); - if ( g_alert_auto_scripts ) - { - if ( pywraps_check_autoscripts(tmp, sizeof(tmp)) - && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n" - "Do you want to continue loading IDAPython?", tmp) <= 0 ) - { - return false; - } - } - - if ( g_use_local_python ) - Py_SetPythonHome(g_idapython_dir); - - // don't import "site" right now - Py_NoSiteFlag = 1; - - // Start the interpreter - Py_InitializeEx(0 /* Don't catch SIGPIPE, SIGXFZ, SIGXFSZ & SIGINT signals */); - - if ( !Py_IsInitialized() ) - { - warning("IDAPython: Py_InitializeEx() failed"); - return false; - } - - // remove current directory - sanitize_path(); - - // import "site" - if ( !g_use_local_python && !initsite() ) - { - warning("IDAPython: importing \"site\" failed"); - return false; - } - - // Enable multi-threading support - if ( !PyEval_ThreadsInitialized() ) - PyEval_InitThreads(); - - // Init the SWIG wrapper - init_idaapi(); - -#ifdef Py_DEBUG - msg("HexraysPython: Python compiled with DEBUG enabled.\n"); -#endif - - // Set IDAPYTHON_VERSION in Python - qsnprintf( - tmp, - sizeof(tmp), - "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" - "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", - VER_MAJOR, - VER_MINOR, - VER_PATCH, - VER_STATUS, - VER_SERIAL, - g_remove_cwd_sys_path ? "True" : "False"); - PyRun_SimpleString(tmp); - - // Install extlang. Needs to be done before running init.py - // in case it's calling idaapi.enable_extlang_python(1) - install_extlang(&extlang_python); - - // Execute init.py (for Python side initialization) - qmakepath(tmp, MAXSTR, g_idapython_dir, S_INIT_PY, NULL); - if ( !PyRunFile(tmp) ) - { - // Try to fetch a one line error string. We must do it before printing - // the traceback information. Make sure that the exception is not cleared - handle_python_error(tmp, sizeof(tmp), false); - - // Print the exception traceback - PyRun_SimpleString("import traceback;traceback.print_exc();"); - - warning("IDAPython: error executing " S_INIT_PY ":\n" - "%s\n" - "\n" - "Refer to the message window to see the full error log.", tmp); - remove_extlang(&extlang_python); - return false; - } - - // Init pywraps and notify_when - if ( !init_pywraps() || !pywraps_nw_init() ) - { - warning("IDAPython: init_pywraps() failed!"); - remove_extlang(&extlang_python); - return false; - } - -#ifdef ENABLE_PYTHON_PROFILING - PyEval_SetTrace(tracefunc, NULL); -#endif - - // Batch-mode operation: - parse_plugin_options(); - - // Register a RunPythonStatement() function for IDC - set_idc_func_ex( - S_IDC_RUNPYTHON_STATEMENT, - idc_runpythonstatement, - idc_runpythonstatement_args, - 0); - - // A script specified on the command line is run - if ( g_run_when == run_on_init ) - RunScript(g_run_script); - -#ifdef _DEBUG - hook_to_notification_point(HT_UI, ui_debug_handler_cb, NULL); -#endif - hook_to_notification_point(HT_UI, on_ui_notification, NULL); - hook_to_notification_point(HT_IDP, on_idp_notification, NULL); - - // Enable the CLI by default - enable_python_cli(true); - - pywraps_nw_notify(NW_INITIDA_SLOT); - - PyEval_ReleaseThread(PyThreadState_Get()); - - g_instance_initialized = true; - return true; -} - -//------------------------------------------------------------------------- -// Cleaning up Python -void IDAPython_Term(void) -{ - if ( !g_instance_initialized || Py_IsInitialized() == 0 ) - return; - - if ( PyGILState_GetThisThreadState() ) - { - // Note: No 'PYW_GIL_GET' here, as it would try to release - // the state after 'Py_Finalize()' has been called. - // ...nor is it a good idea to try to put it in its own scope, - // as it will PyGILState_Release() the current thread & GIL, and - // Py_Finalize() itself wouldn't be happy then. - PyGILState_Ensure(); - } - - unhook_from_notification_point(HT_IDP, on_idp_notification, NULL); - unhook_from_notification_point(HT_UI, on_ui_notification, NULL); -#ifdef _DEBUG - unhook_from_notification_point(HT_UI, ui_debug_handler_cb, NULL); -#endif - - // Notify about IDA closing - pywraps_nw_notify(NW_TERMIDA_SLOT); - - // De-init notify_when - pywraps_nw_term(); - - // Remove the CLI - enable_python_cli(false); - - // Remove the extlang - remove_extlang(&extlang_python); - - // De-init pywraps - deinit_pywraps(); - - // Uninstall IDC function - set_idc_func_ex(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL, 0); - - // Shut the interpreter down - Py_Finalize(); - g_instance_initialized = false; -} - -//------------------------------------------------------------------------- -// Plugin init routine -int idaapi init(void) -{ - if ( IDAPython_Init() ) - return PLUGIN_KEEP; - else - return PLUGIN_SKIP; -} - -//------------------------------------------------------------------------- -// Plugin term routine -void idaapi term(void) -{ - IDAPython_Term(); -} - -//------------------------------------------------------------------------- -// Plugin hotkey entry point -void idaapi run(int arg) -{ - try - { - switch ( arg ) - { - case IDAPYTHON_RUNSTATEMENT: - IDAPython_RunStatement(); - break; - case IDAPYTHON_ENABLE_EXTLANG: - enable_extlang_python(true); - break; - case IDAPYTHON_DISABLE_EXTLANG: - enable_extlang_python(false); - break; - default: - warning("IDAPython: unknown plugin argument %d", arg); - break; - } - } - catch(...) - { - warning("Exception in Python interpreter. Reloading..."); - IDAPython_Term(); - IDAPython_Init(); - } -} - -//------------------------------------------------------------------------- -// PLUGIN DESCRIPTION BLOCK -//------------------------------------------------------------------------- -plugin_t PLUGIN = -{ - IDP_INTERFACE_VERSION, - PLUGIN_FIX | PLUGIN_HIDE, // plugin flags - init, // initialize - term, // terminate. this pointer may be NULL. - run, // invoke plugin - S_IDAPYTHON, // long comment about the plugin - // it could appear in the status line - // or as a hint - // multiline help about the plugin - "IDA Python Plugin\n", - // the preferred short name of the plugin - S_IDAPYTHON, - // the preferred hotkey to run the plugin - NULL -}; +//--------------------------------------------------------------------- +// IDAPython - Python plugin for Interactive Disassembler +// +// Copyright (c) The IDAPython Team +// +// All rights reserved. +// +// For detailed copyright information see the file COPYING in +// the root of the distribution archive. +//--------------------------------------------------------------------- +// python.cpp - Main plugin code +//--------------------------------------------------------------------- +#include + +//------------------------------------------------------------------------- +// This define fixes the redefinition of ssize_t +#ifdef HAVE_SSIZE_T +#define _SSIZE_T_DEFINED 1 +#endif + +#ifdef __LINUX__ +#include +#endif +#ifdef __MAC__ +#include +#endif +#include +#include +#include +#include +#include +#include + +#ifdef WITH_HEXRAYS +#include +hexdsp_t *hexdsp = NULL; +#endif + +#include "pywraps.hpp" + +//------------------------------------------------------------------------- +// Defines and constants + +// Python-style version tuple comes from the makefile +// Only the serial and status is set here +#define VER_SERIAL 0 +#define VER_STATUS "final" +#define IDAPYTHON_RUNSTATEMENT 0 +#define IDAPYTHON_ENABLE_EXTLANG 3 +#define IDAPYTHON_DISABLE_EXTLANG 4 +#define PYTHON_DIR_NAME "python" +#define S_IDAPYTHON "IDAPython" +#define S_INIT_PY "init.py" +static const char S_IDC_ARGS_VARNAME[] = "ARGV"; +static const char S_MAIN[] = "__main__"; +static const char S_IDC_RUNPYTHON_STATEMENT[] = "RunPythonStatement"; +static const char S_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; + +//------------------------------------------------------------------------- +// Types + +// +enum script_run_when +{ + run_on_db_open = 0, // run script after opening database (default) + run_on_ui_ready = 1, // run script when UI is ready + run_on_init = 2, // run script immediately on plugin load (shortly after IDA starts) +}; + +//------------------------------------------------------------------------- +// Global variables +static bool g_instance_initialized = false; // This instance of the plugin is the one + // that initialized the python interpreter. +static int g_run_when = -1; +static char g_run_script[QMAXPATH]; +static char g_idapython_dir[QMAXPATH]; + +//------------------------------------------------------------------------- +// Prototypes and forward declarations + +// Alias to SWIG_Init +//lint -esym(526,init_idaapi) not defined +extern "C" void init_idaapi(void); + +// Plugin run() callback +void idaapi run(int arg); + +//------------------------------------------------------------------------- +// This is a simple tracing code for debugging purposes. +// It might evolve into a tracing facility for user scripts. + +//#define ENABLE_PYTHON_PROFILING +#ifdef ENABLE_PYTHON_PROFILING +#include "compile.h" +#include "frameobject.h" + +int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) +{ + PyObject *str; + + /* Catch line change events. */ + /* Print the filename and line number */ + if ( what == PyTrace_LINE ) + { + str = PyObject_Str(frame->f_code->co_filename); + if ( str ) + { + msg("PROFILING: %s:%d\n", PyString_AsString(str), frame->f_lineno); + Py_DECREF(str); + } + } + return 0; +} +#endif + +//------------------------------------------------------------------------- +// Helper routines to make Python script execution breakable from IDA +static int ninsns = 0; // number of times trace function was called +static bool box_displayed; // has the wait box been displayed? +static time_t start_time; // the start time of the execution +static int script_timeout = 2; +static bool g_ui_ready = false; +static bool g_alert_auto_scripts = true; +static bool g_remove_cwd_sys_path = false; +static bool g_use_local_python = false; + +static void end_execution(void); +static void begin_execution(void); + +//------------------------------------------------------------------------ +// This callback is called on various interpreter events +static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) +{ + if ( wasBreak() ) + { + // User pressed Cancel in the waitbox; send KeyboardInterrupt exception + PyErr_SetString(PyExc_KeyboardInterrupt, "User interrupted"); + return -1; + } + else if ( !box_displayed && ++ninsns > 10 ) + { + // We check the timer once every 10 calls + ninsns = 0; + + // Timeout disabled or elapsed? + if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) + { + box_displayed = true; + show_wait_box("Running Python script"); + } + } +#ifdef ENABLE_PYTHON_PROFILING + return tracefunc(obj, frame, what, arg); +#else + qnotused(obj); + qnotused(frame); + qnotused(what); + qnotused(arg); + return 0; +#endif +} + +//------------------------------------------------------------------------ +static void reset_execution_time() +{ + start_time = time(NULL); + ninsns = 0; +} + +//------------------------------------------------------------------------ +// Prepare for Python execution +static void begin_execution() +{ + if ( !g_ui_ready || script_timeout == 0 ) + return; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + end_execution(); + reset_execution_time(); + PyEval_SetTrace(break_check, NULL); +} + +//--------------------------------------------------------------------------- +static void hide_script_waitbox() +{ + if ( box_displayed ) + { + hide_wait_box(); + box_displayed = false; + } +} + +//------------------------------------------------------------------------ +// Called after Python execution finishes +static void end_execution() +{ + hide_script_waitbox(); + PYW_GIL_CHECK_LOCKED_SCOPE(); +#ifdef ENABLE_PYTHON_PROFILING + PyEval_SetTrace(tracefunc, NULL); +#else + PyEval_SetTrace(NULL, NULL); +#endif +} + +//------------------------------------------------------------------------- +pycall_res_t::pycall_res_t(PyObject *pyo) + : result(pyo) +{ + PYGLOG("return code: %p\n", result.o); +} + +//------------------------------------------------------------------------- +pycall_res_t::~pycall_res_t() +{ + if (PyErr_Occurred()) + PyErr_Print(); +} + +//------------------------------------------------------------------------- +//lint -esym(714,disable_script_timeout) Symbol not referenced +void disable_script_timeout() +{ + // Clear timeout + script_timeout = 0; + + // Uninstall the trace function and hide the waitbox (if it was shown) + end_execution(); +} + +//------------------------------------------------------------------------- +//lint -esym(714,set_script_timeout) Symbol not referenced +int set_script_timeout(int timeout) +{ + // Update the timeout + qswap(timeout, script_timeout); + + // Reset the execution time and hide the waitbox (so it is shown again after timeout elapses) + reset_execution_time(); + hide_script_waitbox(); + + return timeout; +} + +//------------------------------------------------------------------------ +// Return a formatted error or just print it to the console +static void handle_python_error( + char *errbuf, + size_t errbufsize, + bool clear_error = true) +{ + if ( errbufsize > 0 && errbuf != NULL ) + errbuf[0] = '\0'; + + // No exception? + if ( !PyErr_Occurred() ) + return; + + qstring s; + if ( PyW_GetError(&s, clear_error) && errbuf != NULL ) + qstrncpy(errbuf, s.c_str(), errbufsize); +} + +//------------------------------------------------------------------------ +// Helper function to get globals for the __main__ module +// Note: The references are borrowed. No need to free them. +static PyObject *GetMainGlobals() +{ + PyObject *module = PyImport_AddModule(S_MAIN); + return module == NULL ? NULL : PyModule_GetDict(module); +} + +//------------------------------------------------------------------------ +static void PythonEvalOrExec( + const char *str, + const char *filename = "") +{ + // Compile as an expression + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyCompilerFlags cf = {0}; + newref_t py_code(Py_CompileStringFlags(str, filename, Py_eval_input, &cf)); + if ( py_code == NULL || PyErr_Occurred() ) + { + // Not an expression? + PyErr_Clear(); + + // Run as a string + PyRun_SimpleString(str); + } + else + { + PyObject *py_globals = GetMainGlobals(); + newref_t py_result( + PyEval_EvalCode( + (PyCodeObject *) py_code.o, + py_globals, + py_globals)); + + if ( py_result == NULL || PyErr_Occurred() ) + { + PyErr_Print(); + } + else + { + if ( py_result.o != Py_None ) + { + bool ok = false; + if ( PyUnicode_Check(py_result.o) ) + { + newref_t py_result_utf8(PyUnicode_AsUTF8String(py_result.o)); + ok = py_result_utf8 != NULL; + if ( ok ) + umsg("%s\n", PyString_AS_STRING(py_result_utf8.o)); + } + else + { + qstring result_str; + ok = PyW_ObjectToString(py_result.o, &result_str); + if ( ok ) + msg("%s\n", result_str.c_str()); + } + + if ( !ok ) + msg("*** IDAPython: Couldn't convert evaluation result\n"); + } + } + } +} + +//------------------------------------------------------------------------ +// Executes a simple string +static bool idaapi IDAPython_extlang_run_statements( + const char *str, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + PyObject *globals = GetMainGlobals(); + bool ok; + if ( globals == NULL ) + { + ok = false; + } + else + { + errbuf[0] = '\0'; + PyErr_Clear(); + begin_execution(); + newref_t result(PyRun_String( + str, + Py_file_input, + globals, + globals)); + end_execution(); + ok = result != NULL && !PyErr_Occurred(); + if ( !ok ) + handle_python_error(errbuf, errbufsize); + } + if ( !ok && errbuf[0] == '\0' ) + qstrncpy(errbuf, "internal error", errbufsize); + return ok; +} + +//------------------------------------------------------------------------ +// Simple Python statement runner function for IDC +static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; +static error_t idaapi idc_runpythonstatement( + idc_value_t *argv, + idc_value_t *res) +{ + char errbuf[MAXSTR]; + bool ok = IDAPython_extlang_run_statements(argv[0].c_str(), errbuf, sizeof(errbuf)); + + if ( ok ) + res->set_long(0); + else + res->set_string(errbuf); + + return eOk; +} + +//-------------------------------------------------------------------------- +const char *idaapi set_python_options( + const char *keyword, + int value_type, + const void *value) +{ + do + { + if ( value_type == IDPOPT_NUM ) + { + if ( qstrcmp(keyword, "SCRIPT_TIMEOUT") == 0 ) + { + script_timeout = int(*(uval_t *)value); + break; + } + else if ( qstrcmp(keyword, "ALERT_AUTO_SCRIPTS") == 0 ) + { + g_alert_auto_scripts = *(uval_t *)value != 0; + break; + } + else if ( qstrcmp(keyword, "REMOVE_CWD_SYS_PATH") == 0 ) + { + g_remove_cwd_sys_path = *(uval_t *)value != 0; + break; + } + else if ( qstrcmp(keyword, "USE_LOCAL_PYTHON") == 0 ) + { + msg("\"USE_LOCAL_PYTHON\" is deprecated. IDA will always use " + "its own Python libraries if directory python/lib/ exists.\n"); + break; + } + } + return IDPOPT_BADKEY; + } while (false); + return IDPOPT_OK; +} + +//------------------------------------------------------------------------- +// Check for the presence of a file in IDADIR/python and complain on error +static bool check_python_dir() +{ + static const char *const script_files[] = + { + S_IDC_MODNAME ".py", + S_INIT_PY, + "idaapi.py", + "idautils.py" + }; + char filepath[QMAXPATH]; + for ( size_t i=0; i 0 ) + errbuf[0] = '\0'; + + if ( py_result == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + + int cvt = CIP_OK; + if ( idc_result != NULL ) + { + idc_result->clear(); + cvt = pyvar_to_idcvar(py_result, idc_result); + if ( cvt < CIP_OK ) + qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); + } + + return cvt >= CIP_OK; +} + +//------------------------------------------------------------------------- +// This function will call the Python function 'idaapi.IDAPython_ExecFile' +// It does not use 'import', thus the executed script will not yield a new module name +// It returns the exception and traceback information. +// We use the Python function to execute the script because it knows how to deal with +// module reloading. +static bool IDAPython_ExecFile( + const char *FileName, + char *errbuf, + size_t errbufsz, + const char *idaapi_script = S_IDAAPI_EXECSCRIPT, + idc_value_t *second_res = NULL, + bool want_tuple = false) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_execscript(get_idaapi_attr(idaapi_script)); + if ( py_execscript == NULL ) + { + qsnprintf(errbuf, errbufsz, "Could not find idaapi.%s ?!", idaapi_script); + return false; + } + + char script[MAXSTR]; + qstrncpy(script, FileName, sizeof(script)); + strrpl(script, '\\', '/'); + + newref_t py_script(PyString_FromString(script)); + newref_t py_ret(PyObject_CallFunctionObjArgs( + py_execscript.o, + py_script.o, + GetMainGlobals(), + NULL)); + + // Failure at this point means the script was interrupted + bool interrupted = false; + qstring err; + if ( PyW_GetError(&err) || py_ret == NULL ) + { + PyErr_Clear(); + if ( err.empty() ) + qstrncpy(errbuf, "Script interrupted", errbufsz); + else + qstrncpy(errbuf, err.c_str(), errbufsz); + interrupted = true; + } + + bool ok = false; + if ( !interrupted ) + { + PyObject *ret_o; + if ( want_tuple ) + { + if ( second_res != NULL + && PyTuple_Check(py_ret.o) + && PyTuple_Size(py_ret.o) == 2 ) + { + ret_o = PyTuple_GetItem(py_ret.o, 0); // Borrowed reference + } + else + { + INTERR(30444); + } + } + else + { + ret_o = py_ret.o; + } + + if ( ret_o == Py_None ) + { + if ( want_tuple ) + { + borref_t ret2_o(PyTuple_GetItem(py_ret.o, 1)); + ok = return_python_result(second_res, ret2_o, errbuf, errbufsz); + } + else + { + ok = true; + } + } + else if ( PyString_Check(ret_o) ) + { + qstrncpy(errbuf, PyString_AsString(ret_o), errbufsz); + } + else + { + INTERR(30154); + } + } + return ok; +} + +//------------------------------------------------------------------------- +// Execute the Python script from the plugin +static bool RunScript(const char *script) +{ + begin_execution(); + + char errbuf[MAXSTR]; + bool ok = IDAPython_ExecFile(script, errbuf, sizeof(errbuf)); + if ( !ok ) + warning("IDAPython: error executing '%s':\n%s", script, errbuf); + + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// This function parses a name into two different components (if it applies). +// Example: +// parse_py_modname("modname.attrname", mod_buf, attr_buf) +// It splits the full name into two parts. +static bool parse_py_modname( + const char *full_name, + char *modname, + char *attrname, + size_t sz, + const char *defmod = S_IDAAPI_MODNAME) +{ + const char *p = strchr(full_name, '.'); + if ( p == NULL ) + { + qstrncpy(modname, defmod, sz); + qstrncpy(attrname, full_name, sz); + } + else + { + qstrncpy(modname, full_name, p - full_name + 1); + qstrncpy(attrname, p + 1, sz); + } + return p != NULL; +} + +//------------------------------------------------------------------------- +// Run callback for Python external language evaluator +bool idaapi IDAPython_extlang_run( + const char *name, + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + // Try to extract module name (if any) from the funcname + char modname[MAXSTR] = {0}; + char funcname[MAXSTR] = {0}; + bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); + + bool ok = true; + PyObject *module = NULL; + ref_vec_t pargs; + do + { + // Convert arguments to python + ok = pyw_convert_idc_args(args, nargs, pargs, false, errbuf, errbufsize); + if ( !ok ) + break; + + if ( imported_module ) + { + module = PyImport_ImportModule(modname); + } + else + { + module = PyImport_AddModule(S_MAIN); + QASSERT(30156, module != NULL); + } + + PyObject *globals = PyModule_GetDict(module); + QASSERT(30157, globals != NULL); + + PyObject *func = PyDict_GetItemString(globals, funcname); + if ( func == NULL ) + { + qsnprintf(errbuf, errbufsize, "undefined function %s", name); + ok = false; + break; + } + + borref_t code(PyFunction_GetCode(func)); + qvector pargs_ptrs; + pargs.to_pyobject_pointers(&pargs_ptrs); + newref_t py_res(PyEval_EvalCodeEx( + (PyCodeObject*) code.o, + globals, NULL, + pargs_ptrs.begin(), + nargs, + NULL, 0, NULL, 0, NULL)); + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + if ( imported_module ) + Py_XDECREF(module); + return ok; +} + +//------------------------------------------------------------------------- +static void wrap_in_function(qstring *out, const qstring &body, const char *name) +{ + out->sprnt("def %s():\n", name); + // dont copy trailing whitespace + int i = body.length()-1; + while ( i >= 0 && qisspace(body.at(i)) ) + i--; + out->append(body.substr(0, i+1)); + out->replace("\n", "\n "); +} + +//------------------------------------------------------------------------- +// Compile callback for Python external language evaluator +bool idaapi IDAPython_extlang_compile( + const char *name, + ea_t /*current_ea*/, + const char *expr, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + PyObject *globals = GetMainGlobals(); + bool is_func = false; + + PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); + if ( code == NULL ) + { + // try compiling as a list of statements + // wrap them into a function + handle_python_error(errbuf, errbufsize); + qstring func; + wrap_in_function(&func, expr, name); + code = (PyCodeObject *)Py_CompileString(func.c_str(), "", Py_file_input); + if ( code == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + is_func = true; + } + + // Set the desired function name + Py_XDECREF(code->co_name); + code->co_name = PyString_FromString(name); + + // Create a function out of code + PyObject *func = PyFunction_New((PyObject *)code, globals); + + if ( func == NULL ) + { +ERR: + handle_python_error(errbuf, errbufsize); + Py_XDECREF(code); + return false; + } + + int err = PyDict_SetItemString(globals, name, func); + Py_XDECREF(func); + + if ( err ) + goto ERR; + + if ( is_func ) + { + const idc_value_t args; + idc_value_t result; + return IDAPython_extlang_run(name, 0, &args, &result, errbuf, errbufsize); + } + return true; +} + +//------------------------------------------------------------------------- +// Compile callback for Python external language evaluator +bool idaapi IDAPython_extlang_compile_file( + const char *filename, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize); + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// Load processor module callback for Python external language evaluator +static bool idaapi IDAPython_extlang_loadprocmod( + const char *filename, + idc_value_t *procobj, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_LOADPROCMOD, procobj, true); + if ( ok && procobj->is_zero() ) + { + errbuf[0] = '\0'; + ok = false; + } + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// Unload processor module callback for Python external language evaluator +static bool idaapi IDAPython_extlang_unloadprocmod( + const char *filename, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_UNLOADPROCMOD); + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// Create an object instance +bool idaapi IDAPython_extlang_create_object( + const char *name, // in: object class name + int nargs, // in: number of input arguments + const idc_value_t args[], // in: input arguments + idc_value_t *result, // out: created object or exception + char *errbuf, // out: error message if evaluation fails + size_t errbufsize) // in: size of the error buffer +{ + PYW_GIL_GET; + bool ok = false; + ref_vec_t pargs; + do + { + // Parse the object name (to get the module and class name) + char modname[MAXSTR] = {0}; + char clsname[MAXSTR] = {0}; + parse_py_modname(name, modname, clsname, MAXSTR); + + // Get a reference to the module + ref_t py_mod(PyW_TryImportModule(modname)); + if ( py_mod == NULL ) + { + qsnprintf(errbuf, errbufsize, "Could not import module '%s'!", modname); + break; + } + + // Get the class reference + ref_t py_cls(PyW_TryGetAttrString(py_mod.o, clsname)); + if ( py_cls == NULL ) + { + qsnprintf(errbuf, errbufsize, "Could not find class type '%s'!", clsname); + break; + } + + // Error during conversion? + ok = pyw_convert_idc_args(args, nargs, pargs, true, errbuf, errbufsize); + if ( !ok ) + break; + + // Call the constructor + newref_t py_res(PyObject_CallObject(py_cls.o, pargs.empty() ? NULL : pargs[0].o)); + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + return ok; +} + +//------------------------------------------------------------------------- +// Returns the attribute value of a given object from the global scope +bool idaapi IDAPython_extlang_get_attr( + const idc_value_t *obj, // in: object (may be NULL) + const char *attr, // in: attribute name + idc_value_t *result) +{ + PYW_GIL_GET; + int cvt = CIP_FAILED; + do + { + // Get a reference to the module + ref_t py_mod(PyW_TryImportModule(S_MAIN)); + if ( py_mod == NULL ) + break; + + // Object specified: + // - (1) string contain attribute name in the main module + // - (2) opaque object (we use it as is) + ref_t py_obj; + if ( obj != NULL ) + { + // (1) Get attribute from main module + if ( obj->vtype == VT_STR2 ) + { + py_obj = PyW_TryGetAttrString(py_mod.o, obj->c_str()); + } + // (2) see if opaque object + else + { + // Convert object (expecting opaque object) + cvt = idcvar_to_pyvar(*obj, &py_obj); + if ( cvt != CIP_OK_OPAQUE ) // Only opaque objects are accepted + { + py_obj = ref_t(); + cvt = CIP_FAILED; + break; + } + } + // Get the attribute reference + if ( py_obj == NULL ) + break; + } + // No object specified: + else + { + // ...then work with main module + py_obj = py_mod; + } + // Special case: if attribute not passed then retrieve the class + // name associated associated with the passed object + if ( attr == NULL || attr[0] == '\0' ) + { + cvt = CIP_FAILED; + // Get the class + newref_t cls(PyObject_GetAttrString(py_obj.o, "__class__")); + if ( cls == NULL ) + break; + + // Get its name + newref_t name(PyObject_GetAttrString(cls.o, "__name__")); + if ( name == NULL ) + break; + + // Convert name object to string object + newref_t string(PyObject_Str(name.o)); + if ( string == NULL ) + break; + + // Convert name python string to a C string + const char *clsname = PyString_AsString(string.o); + if ( clsname == NULL ) + break; + + result->set_string(clsname); + cvt = CIP_OK; //lint !e838 + break; + } + + ref_t py_attr(PyW_TryGetAttrString(py_obj.o, attr)); + // No attribute? + if ( py_attr == NULL ) + { + cvt = CIP_FAILED; + break; + } + // Don't store result + if ( result == NULL ) + { + cvt = CIP_OK; + // Decrement attribute (because of GetAttrString) + } + else + { + cvt = pyvar_to_idcvar(py_attr, result); + // // Conversion succeeded and opaque object was passed: + // // Since the object will be passed to IDC, it is likely that IDC value will be + // // destroyed and also destroying the opaque object with it. That is an undesired effect. + // // We increment the reference of the object so that even if the IDC value dies + // // the opaque object remains. So by not decrement reference after GetAttrString() call + // // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. + // if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) + // { + // // Decrement the reference (that was incremented by GetAttrString()) + // py_attr.decref(); + // } + } + } while ( false ); + return cvt >= CIP_OK; +} + +//------------------------------------------------------------------------- +// Returns the attribute value of a given object from the global scope +//lint -e{818} +bool idaapi IDAPython_extlang_set_attr( + idc_value_t *obj, // in: object name (may be NULL) + const char *attr, // in: attribute name + idc_value_t *value) +{ + PYW_GIL_GET; + bool ok = false; + do + { + // Get a reference to the module + ref_t py_mod(PyW_TryImportModule(S_MAIN)); + if ( py_mod == NULL ) + break; + ref_t py_obj; + if ( obj != NULL ) + { + // Get the attribute reference (from just a name) + if ( obj->vtype == VT_STR2 ) + { + py_obj = PyW_TryGetAttrString(py_mod.o, obj->c_str()); + } + else + { + int cvt = idcvar_to_pyvar(*obj, &py_obj); + if ( cvt != CIP_OK_OPAQUE ) // Only opaque objects are accepted + py_obj = ref_t(); + } + // No object to set_attr on? + if ( py_obj == NULL ) + break; + } + else + { + // set_attr on the main module + py_obj = py_mod; + } + // Convert the value + ref_t py_var; + int cvt = idcvar_to_pyvar(*value, &py_var); + if ( cvt >= CIP_OK ) + { + ok = PyObject_SetAttrString(py_obj.o, attr, py_var.o) != -1; + // if ( cvt != CIP_OK_NODECREF ) + // Py_XDECREF(py_var); + } + } while ( false ); + return ok; +} + +//------------------------------------------------------------------------- +// Calculator callback for Python external language evaluator +//lint -e{818} +bool idaapi IDAPython_extlang_calcexpr( + ea_t /*current_ea*/, + const char *expr, + idc_value_t *rv, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + PyObject *globals = GetMainGlobals(); + bool ok = globals != NULL; + ref_t result; + if ( ok ) + { + begin_execution(); + result = newref_t(PyRun_String(expr, Py_eval_input, globals, globals)); + end_execution(); + ok = return_python_result(rv, result, errbuf, errbufsize); + } + return ok; +} + +//------------------------------------------------------------------------- +bool idaapi IDAPython_extlang_call_method( + const idc_value_t *idc_obj, + const char *method_name, + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + // Check for unsupported usage of call_method. + // Mainly a method call requires an object and a method. + if ( (idc_obj == NULL && method_name == NULL) || (idc_obj != NULL && method_name == NULL) ) + { + qstrncpy(errbuf, "call_method does not support this operation", errbufsize); + return false; + } + // Behave like run() + else if ( idc_obj == NULL && method_name != NULL ) + { + return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); + } + + // Holds conversion status of input object + int obj_cvt; + bool ok = false; + ref_vec_t pargs; + do + { + // Convert input object + ref_t py_obj; + obj_cvt = idcvar_to_pyvar(*idc_obj, &py_obj); + if ( obj_cvt < CIP_OK ) + { + qstrncpy(errbuf, "Failed to convert input object to Python value", errbufsize); + break; + } + + ref_t py_method(PyW_TryGetAttrString(py_obj.o, method_name)); + if ( py_method == NULL || !PyCallable_Check(py_method.o) ) + { + qsnprintf(errbuf, errbufsize, "The input object does not have a callable method called '%s'", method_name); + break; + } + + // Convert arguments to python objects + ok = pyw_convert_idc_args(args, nargs, pargs, true, errbuf, errbufsize); + if ( !ok ) + break; + + newref_t py_res(PyObject_CallObject(py_method.o, pargs.empty() ? NULL : pargs[0].o)); + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + return ok; +} + +//------------------------------------------------------------------------- +const extlang_t extlang_python = +{ + sizeof(extlang_t), + 0, + "Python", + IDAPython_extlang_compile, + IDAPython_extlang_run, + IDAPython_extlang_calcexpr, + IDAPython_extlang_compile_file, + "py", + IDAPython_extlang_create_object, + IDAPython_extlang_get_attr, + IDAPython_extlang_set_attr, + IDAPython_extlang_call_method, + IDAPython_extlang_run_statements, + IDAPython_extlang_loadprocmod, + IDAPython_extlang_unloadprocmod, +}; + +//------------------------------------------------------------------------- +void enable_extlang_python(bool enable) +{ + if ( enable ) + select_extlang(&extlang_python); + else + select_extlang(NULL); +} + +//------------------------------------------------------------------------- +// Execute a line in the Python CLI +bool idaapi IDAPython_cli_execute_line(const char *line) +{ + PYW_GIL_GET; + + // Do not process empty lines + if ( line[0] == '\0' ) + return true; + + const char *last_line = strrchr(line, '\n'); + if ( last_line == NULL ) + last_line = line; + else + last_line += 1; + + // Skip empty lines + if ( last_line[0] != '\0' ) + { + // Line ends with ":" or begins with a space character? + bool more = last_line[qstrlen(last_line)-1] == ':' || qisspace(last_line[0]); + if ( more ) + return false; + } + + // + // Pseudo commands + // + qstring s; + do + { + // Help command? + if ( line[0] == '?' ) + s.sprnt("help(%s)", line+1); + // Shell command? + else if ( line[0] == '!' ) + s.sprnt("idaapi.IDAPython_ExecSystem(r'%s')", line+1); + else + break; + // Patch the command line pointer + line = s.c_str(); + } while (false); + + begin_execution(); + PythonEvalOrExec(line); + end_execution(); + + return true; +} + +//------------------------------------------------------------------------- +bool idaapi IDAPYthon_cli_complete_line( + qstring *completion, + const char *prefix, + int n, + const char *line, + int x) +{ + PYW_GIL_GET; + + ref_t py_complete(get_idaapi_attr(S_IDAAPI_COMPLETION)); + if ( py_complete == NULL ) + return false; + + newref_t py_ret(PyObject_CallFunction(py_complete.o, "sisi", prefix, n, line, x)); //lint !e605 + + // Swallow the error + PyW_GetError(completion); + + bool ok = py_ret != NULL && PyString_Check(py_ret.o); + if ( ok ) + *completion = PyString_AS_STRING(py_ret.o); + return ok; +} + +//------------------------------------------------------------------------- +static const cli_t cli_python = +{ + sizeof(cli_t), + 0, + "Python", + "Python - IDAPython plugin", + "Enter any Python expression", + IDAPython_cli_execute_line, + IDAPYthon_cli_complete_line, + NULL +}; + +//------------------------------------------------------------------------- +// Control the Python CLI status +void enable_python_cli(bool enable) +{ + if ( enable ) + install_command_interpreter(&cli_python); + else + remove_command_interpreter(&cli_python); +} + +//------------------------------------------------------------------------- +// Prints the IDAPython copyright banner +void py_print_banner() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyRun_SimpleString("print_banner()"); +} + +//------------------------------------------------------------------------ +// Parse plugin options +void parse_plugin_options() +{ + // Get options from IDA + const char *options = get_plugin_options(S_IDAPYTHON); + + // No options? + if ( options == NULL ) + return; + + // User specified 'when' parameter? + const char *p = strchr(options, ';'); + if ( p == NULL ) + { + g_run_when = run_on_db_open; + p = options; + } + else + { + g_run_when = atoi(options); + ++p; + } + qstrncpy(g_run_script, p, sizeof(g_run_script)); +} + +//------------------------------------------------------------------------ +// Converts the global IDC variable "ARGV" into a Python variable. +// The arguments will then be accessible via 'idc' module / 'ARGV' variable. +void convert_idc_args() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_args(PyList_New(0)); + + idc_value_t *idc_args = find_idc_gvar(S_IDC_ARGS_VARNAME); + if ( idc_args != NULL ) + { + idc_value_t attr; + char attr_name[20] = {"0"}; + for ( int i=1; VarGetAttr(idc_args, attr_name, &attr) == eOk; i++ ) + { + PyList_Insert(py_args.o, i, PyString_FromString(attr.c_str())); + qsnprintf(attr_name, sizeof(attr_name), "%d", i); + } + } + + // Get reference to the IDC module (it is imported by init.py) + ref_t py_mod(PyW_TryImportModule(S_IDC_MODNAME)); + if ( py_mod != NULL ) + PyObject_SetAttrString(py_mod.o, S_IDC_ARGS_VARNAME, py_args.o); +} + +#ifdef WITH_HEXRAYS +//------------------------------------------------------------------------- +static bool is_hexrays_plugin(const plugin_info_t *pinfo) +{ + bool is_hx = false; + if ( pinfo != NULL && pinfo->entry != NULL ) + { + const plugin_t *p = pinfo->entry; + if ( streq(p->wanted_name, "Hex-Rays Decompiler") ) + is_hx = true; + } + return is_hx; +} +#endif + + +//------------------------------------------------------------------------ +//lint -esym(715,va) Symbol not referenced +static int idaapi on_ui_notification(void *, int code, va_list va) +{ +#ifdef WITH_HEXRAYS + qnotused(va); +#endif + switch ( code ) + { + case ui_ready_to_run: + { + PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. + g_ui_ready = true; + py_print_banner(); + + if ( g_run_when == run_on_ui_ready ) + RunScript(g_run_script); + } + break; + + case ui_database_inited: + { + PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. + convert_idc_args(); + if ( g_run_when == run_on_db_open ) + RunScript(g_run_script); + } + break; + + case ui_term: + { + PYW_GIL_GET; + // Let's make sure there are no non-Free()d forms. + free_compiled_form_instances(); + } + break; + +#ifdef WITH_HEXRAYS + case ui_plugin_loaded: + if ( hexdsp == NULL ) + { + if ( is_hexrays_plugin(va_arg(va, plugin_info_t *)) ) + { + init_hexrays_plugin(0); + if ( hexdsp != NULL ) + msg("IDAPython Hex-Rays bindings initialized.\n"); + } + } + break; + + case ui_plugin_unloading: + { + if ( hexdsp != NULL ) + { + // Hex-Rays will close. Make sure all the refcounted cfunc_t objects + // are cleared right away. + if ( is_hexrays_plugin(va_arg(va, plugin_info_t *)) ) + { + hexrays_clear_python_cfuncptr_t_references(); + hexdsp = NULL; + } + } + } + break; +#endif + + default: + break; + } + return 0; +} + +//------------------------------------------------------------------------- +//lint -esym(526,til_clear_python_tinfo_t_instances) not defined +extern void til_clear_python_tinfo_t_instances(void); +static int idaapi on_idp_notification(void *, int code, va_list) +{ + switch ( code ) + { + case processor_t::closebase: + // The til machinery is about to garbage-collect: We must go + // through all the tinfo_t objects that are embedded in SWIG wrappers, + // (i.e., that were created from Python) and clear those. + til_clear_python_tinfo_t_instances(); + break; + } + return 0; +} + +#ifdef _DEBUG +//------------------------------------------------------------------------ +// extern int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag); +extern PyThreadState *_PyThreadState_Current; +static int idaapi ui_debug_handler_cb(void *, int code, va_list) +{ + // This hook gets called from the kernel, but its very point is to + // make sure that we don't hold the GIL. Thus: No PYW_GIL_GET here! + switch ( code ) + { + case debug_assert_thread_waitready: + // We will *always* be in a non-main thread when this is called. + if ( _PyThreadState_Current != NULL ) + { + PyThreadState *tcur = PyGILState_GetThisThreadState(); + if ( tcur == _PyThreadState_Current ) + { + // This thread is the '_PyThreadState_Current'; i.e., it holds the lock. + // We're likely to end up in a deadlock. + BPT; + } + } + break; + default: + break; + } + return 0; +} +#endif + +//------------------------------------------------------------------------- +// remove current directory (empty entry) from the sys.path +static void sanitize_path() +{ + char buf[QMAXPATH]; + qstrncpy(buf, Py_GetPath(), sizeof(buf)); + char *ctx; + qstring newpath; + for ( char *d0 = qstrtok(buf, DELIMITER, &ctx); + d0 != NULL; + d0 = qstrtok(NULL, DELIMITER, &ctx) ) + { + if ( d0[0] == '\0' ) + // skip empty entry + continue; + + if ( !newpath.empty() ) + newpath.append(DELIMITER); + newpath.append(d0); + } + PySys_SetPath(newpath.begin()); +} + + +//------------------------------------------------------------------------- +// we have to do it ourselves because Python 2.7 calls exit() if importing site fails +static bool initsite(void) +{ + PyObject *m; + m = PyImport_ImportModule("site"); + if ( m == NULL ) + { + PyErr_Print(); + Py_Finalize(); + return false; + } + else + { + Py_DECREF(m); + } + return true; +} + +//------------------------------------------------------------------------- +// Initialize the Python environment +bool IDAPython_Init(void) +{ + if ( Py_IsInitialized() != 0 ) + return true; + + // Form the absolute path to IDA\python folder + qstrncpy(g_idapython_dir, idadir(PYTHON_DIR_NAME), sizeof(g_idapython_dir)); + + // Check for the presence of essential files + if ( !check_python_dir() ) + return false; + + char tmp[QMAXPATH]; +#ifdef __LINUX__ + // Export symbols from libpython to resolve imported module deps + // use the standard soname: libpython2.7.so.1.0 +#define PYLIB "libpython" QSTRINGIZE(PY_MAJOR_VERSION) "." QSTRINGIZE(PY_MINOR_VERSION) ".so.1.0" + if ( !dlopen(PYLIB, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY) ) + { + warning("IDAPython dlopen(" PYLIB ") error: %s", dlerror()); + return false; + } +#endif + +#ifdef __MAC__ + // We should set python home to the module's path, otherwise it can pick up stray modules from $PATH + NSModule pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_InitializeEx")); + // Use dylib functions to find out where the framework was loaded from + const char *buf = (char *)NSLibraryNameForModule(pythonModule); + if ( buf != NULL ) + { + // The path will be something like: + // /System/Library/Frameworks/Python.framework/Versions/2.5/Python + // We need to strip the last part + // use static buffer because Py_SetPythonHome() just stores a pointer + static char pyhomepath[MAXSTR]; + qstrncpy(pyhomepath, buf, MAXSTR); + char * lastslash = strrchr(pyhomepath, '/'); + if ( lastslash != NULL ) + { + *lastslash = 0; + Py_SetPythonHome(pyhomepath); + } + } +#endif + + // Read configuration value + read_user_config_file("python.cfg", set_python_options, NULL); + if ( g_alert_auto_scripts ) + { + if ( pywraps_check_autoscripts(tmp, sizeof(tmp)) + && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n" + "Do you want to continue loading IDAPython?", tmp) <= 0 ) + { + return false; + } + } + + if ( g_use_local_python ) + { + // Set the program name: + // "This is used by Py_GetPath() and some other functions below to find the + // Python run-time libraries relative to the interpreter executable". + // + // + // Note: + // "The argument should point to a zero-terminated character string + // in static storage whose contents will not change for the duration + // of the program's execution" + static qstring pname = idadir(""); + Py_SetProgramName(pname.begin()); + Py_SetPythonHome(g_idapython_dir); + } + + // don't import "site" right now + Py_NoSiteFlag = 1; + + // Start the interpreter + Py_InitializeEx(0 /* Don't catch SIGPIPE, SIGXFZ, SIGXFSZ & SIGINT signals */); + + if ( !Py_IsInitialized() ) + { + warning("IDAPython: Py_InitializeEx() failed"); + return false; + } + + // remove current directory + sanitize_path(); + + // import "site" + if ( !g_use_local_python && !initsite() ) + { + warning("IDAPython: importing \"site\" failed"); + return false; + } + + // Enable multi-threading support + if ( !PyEval_ThreadsInitialized() ) + PyEval_InitThreads(); + + // Init the SWIG wrapper + init_idaapi(); + +#ifdef Py_DEBUG + msg("HexraysPython: Python compiled with DEBUG enabled.\n"); +#endif + + // Set IDAPYTHON_VERSION in Python + qsnprintf( + tmp, + sizeof(tmp), + "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" + "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", + VER_MAJOR, + VER_MINOR, + VER_PATCH, + VER_STATUS, + VER_SERIAL, + g_remove_cwd_sys_path ? "True" : "False"); + PyRun_SimpleString(tmp); + + // Install extlang. Needs to be done before running init.py + // in case it's calling idaapi.enable_extlang_python(1) + install_extlang(&extlang_python); + + // Execute init.py (for Python side initialization) + qmakepath(tmp, MAXSTR, g_idapython_dir, S_INIT_PY, NULL); + if ( !PyRunFile(tmp) ) + { + // Try to fetch a one line error string. We must do it before printing + // the traceback information. Make sure that the exception is not cleared + handle_python_error(tmp, sizeof(tmp), false); + + // Print the exception traceback + PyRun_SimpleString("import traceback;traceback.print_exc();"); + + warning("IDAPython: error executing " S_INIT_PY ":\n" + "%s\n" + "\n" + "Refer to the message window to see the full error log.", tmp); + remove_extlang(&extlang_python); + return false; + } + + // Init pywraps and notify_when + if ( !init_pywraps() || !pywraps_nw_init() ) + { + warning("IDAPython: init_pywraps() failed!"); + remove_extlang(&extlang_python); + return false; + } + +#ifdef ENABLE_PYTHON_PROFILING + PyEval_SetTrace(tracefunc, NULL); +#endif + + // Batch-mode operation: + parse_plugin_options(); + + // Register a RunPythonStatement() function for IDC + set_idc_func_ex( + S_IDC_RUNPYTHON_STATEMENT, + idc_runpythonstatement, + idc_runpythonstatement_args, + 0); + + // A script specified on the command line is run + if ( g_run_when == run_on_init ) + RunScript(g_run_script); + +#ifdef _DEBUG + hook_to_notification_point(HT_UI, ui_debug_handler_cb, NULL); +#endif + hook_to_notification_point(HT_UI, on_ui_notification, NULL); + hook_to_notification_point(HT_IDP, on_idp_notification, NULL); + + // Enable the CLI by default + enable_python_cli(true); + + pywraps_nw_notify(NW_INITIDA_SLOT); + + PyEval_ReleaseThread(PyThreadState_Get()); + + g_instance_initialized = true; + return true; +} + +//------------------------------------------------------------------------- +// Cleaning up Python +void IDAPython_Term(void) +{ + if ( !g_instance_initialized || Py_IsInitialized() == 0 ) + return; + + if ( PyGILState_GetThisThreadState() ) + { + // Note: No 'PYW_GIL_GET' here, as it would try to release + // the state after 'Py_Finalize()' has been called. + // ...nor is it a good idea to try to put it in its own scope, + // as it will PyGILState_Release() the current thread & GIL, and + // Py_Finalize() itself wouldn't be happy then. + PyGILState_Ensure(); + } + + unhook_from_notification_point(HT_IDP, on_idp_notification, NULL); + unhook_from_notification_point(HT_UI, on_ui_notification, NULL); +#ifdef _DEBUG + unhook_from_notification_point(HT_UI, ui_debug_handler_cb, NULL); +#endif + + // Notify about IDA closing + pywraps_nw_notify(NW_TERMIDA_SLOT); + + // De-init notify_when + pywraps_nw_term(); + + // Remove the CLI + enable_python_cli(false); + + // Remove the extlang + remove_extlang(&extlang_python); + + // De-init pywraps + deinit_pywraps(); + + // Uninstall IDC function + set_idc_func_ex(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL, 0); + + // Shut the interpreter down + Py_Finalize(); + g_instance_initialized = false; +} + +//------------------------------------------------------------------------- +// Plugin init routine +int idaapi init(void) +{ + if ( IDAPython_Init() ) + return PLUGIN_KEEP; + else + return PLUGIN_SKIP; +} + +//------------------------------------------------------------------------- +// Plugin term routine +void idaapi term(void) +{ + IDAPython_Term(); +} + +//------------------------------------------------------------------------- +// Plugin hotkey entry point +void idaapi run(int arg) +{ + try + { + switch ( arg ) + { + case IDAPYTHON_RUNSTATEMENT: + IDAPython_RunStatement(); + break; + case IDAPYTHON_ENABLE_EXTLANG: + enable_extlang_python(true); + break; + case IDAPYTHON_DISABLE_EXTLANG: + enable_extlang_python(false); + break; + default: + warning("IDAPython: unknown plugin argument %d", arg); + break; + } + } + catch(...) + { + warning("Exception in Python interpreter. Reloading..."); + IDAPython_Term(); + IDAPython_Init(); + } +} + +//------------------------------------------------------------------------- +// PLUGIN DESCRIPTION BLOCK +//------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_FIX | PLUGIN_HIDE, // plugin flags + init, // initialize + term, // terminate. this pointer may be NULL. + run, // invoke plugin + S_IDAPYTHON, // long comment about the plugin + // it could appear in the status line + // or as a hint + // multiline help about the plugin + "IDA Python Plugin\n", + // the preferred short name of the plugin + S_IDAPYTHON, + // the preferred hotkey to run the plugin + NULL +}; diff --git a/python/idautils.py b/python/idautils.py index c5748ba4..65baa5f3 100644 --- a/python/idautils.py +++ b/python/idautils.py @@ -1,830 +1,830 @@ -#--------------------------------------------------------------------- -# IDAPython - Python plugin for Interactive Disassembler -# -# Copyright (c) 2004-2010 Gergely Erdelyi -# -# All rights reserved. -# -# For detailed copyright information see the file COPYING in -# the root of the distribution archive. -#--------------------------------------------------------------------- -""" -idautils.py - High level utility functions for IDA -""" -import idaapi -import idc -import types -import os - - -def refs(ea, funcfirst, funcnext): - """ - Generic reference collector - INTERNAL USE ONLY. - """ - ref = funcfirst(ea) - while ref != idaapi.BADADDR: - yield ref - ref = funcnext(ea, ref) - - -def CodeRefsTo(ea, flow): - """ - Get a list of code references to 'ea' - - @param ea: Target address - @param flow: Follow normal code flow or not - @type flow: Boolean (0/1, False/True) - - @return: list of references (may be empty list) - - Example:: - - for ref in CodeRefsTo(ScreenEA(), 1): - print ref - """ - if flow == 1: - return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to) - else: - return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to) - - -def CodeRefsFrom(ea, flow): - """ - Get a list of code references from 'ea' - - @param ea: Target address - @param flow: Follow normal code flow or not - @type flow: Boolean (0/1, False/True) - - @return: list of references (may be empty list) - - Example:: - - for ref in CodeRefsFrom(ScreenEA(), 1): - print ref - """ - if flow == 1: - return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from) - else: - return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from) - - -def DataRefsTo(ea): - """ - Get a list of data references to 'ea' - - @param ea: Target address - - @return: list of references (may be empty list) - - Example:: - - for ref in DataRefsTo(ScreenEA()): - print ref - """ - return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to) - - -def DataRefsFrom(ea): - """ - Get a list of data references from 'ea' - - @param ea: Target address - - @return: list of references (may be empty list) - - Example:: - - for ref in DataRefsFrom(ScreenEA()): - print ref - """ - return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from) - - -def XrefTypeName(typecode): - """ - Convert cross-reference type codes to readable names - - @param typecode: cross-reference type code - """ - ref_types = { - 0 : 'Data_Unknown', - 1 : 'Data_Offset', - 2 : 'Data_Write', - 3 : 'Data_Read', - 4 : 'Data_Text', - 5 : 'Data_Informational', - 16 : 'Code_Far_Call', - 17 : 'Code_Near_Call', - 18 : 'Code_Far_Jump', - 19 : 'Code_Near_Jump', - 20 : 'Code_User', - 21 : 'Ordinary_Flow' - } - assert typecode in ref_types, "unknown reference type %d" % typecode - return ref_types[typecode] - - -def _copy_xref(xref): - """ Make a private copy of the xref class to preserve its contents """ - class _xref(object): - pass - - xr = _xref() - for attr in [ 'frm', 'to', 'iscode', 'type', 'user' ]: - setattr(xr, attr, getattr(xref, attr)) - return xr - - -def XrefsFrom(ea, flags=0): - """ - Return all references from address 'ea' - - @param ea: Reference address - @param flags: any of idaapi.XREF_* flags - - Example:: - for xref in XrefsFrom(here(), 0): - print xref.type, XrefTypeName(xref.type), \ - 'from', hex(xref.frm), 'to', hex(xref.to) - """ - xref = idaapi.xrefblk_t() - if xref.first_from(ea, flags): - yield _copy_xref(xref) - while xref.next_from(): - yield _copy_xref(xref) - - -def XrefsTo(ea, flags=0): - """ - Return all references to address 'ea' - - @param ea: Reference address - @param flags: any of idaapi.XREF_* flags - - Example:: - for xref in XrefsTo(here(), 0): - print xref.type, XrefTypeName(xref.type), \ - 'from', hex(xref.frm), 'to', hex(xref.to) - """ - xref = idaapi.xrefblk_t() - if xref.first_to(ea, flags): - yield _copy_xref(xref) - while xref.next_to(): - yield _copy_xref(xref) - - -def Threads(): - """Returns all thread IDs""" - for i in xrange(0, idc.GetThreadQty()): - yield idc.GetThreadId(i) - - -def Heads(start=None, end=None): - """ - Get a list of heads (instructions or data) - - @param start: start address (default: inf.minEA) - @param end: end address (default: inf.maxEA) - - @return: list of heads between start and end - """ - if not start: start = idaapi.cvar.inf.minEA - if not end: end = idaapi.cvar.inf.maxEA - - ea = start - if not idc.isHead(idc.GetFlags(ea)): - ea = idaapi.next_head(ea, end) - while ea != idaapi.BADADDR: - yield ea - ea = idaapi.next_head(ea, end) - - -def Functions(start=None, end=None): - """ - Get a list of functions - - @param start: start address (default: inf.minEA) - @param end: end address (default: inf.maxEA) - - @return: list of heads between start and end - - @note: The last function that starts before 'end' is included even - if it extends beyond 'end'. Any function that has its chunks scattered - in multiple segments will be reported multiple times, once in each segment - as they are listed. - """ - if not start: start = idaapi.cvar.inf.minEA - if not end: end = idaapi.cvar.inf.maxEA - - # find first function head chunk in the range - chunk = idaapi.get_fchunk(start) - if not chunk: - chunk = idaapi.get_next_fchunk(start) - while chunk and chunk.startEA < end and (chunk.flags & idaapi.FUNC_TAIL) != 0: - chunk = idaapi.get_next_fchunk(chunk.startEA) - func = chunk - - while func and func.startEA < end: - startea = func.startEA - yield startea - func = idaapi.get_next_func(startea) - - -def Chunks(start): - """ - Get a list of function chunks - - @param start: address of the function - - @return: list of funcion chunks (tuples of the form (start_ea, end_ea)) - belonging to the function - """ - func_iter = idaapi.func_tail_iterator_t( idaapi.get_func( start ) ) - status = func_iter.main() - while status: - chunk = func_iter.chunk() - yield (chunk.startEA, chunk.endEA) - status = func_iter.next() - - -def Modules(): - """ - Returns a list of module objects with name,size,base and the rebase_to attributes - """ - mod = idaapi.module_info_t() - result = idaapi.get_first_module(mod) - while result: - yield idaapi.object_t(name=mod.name, size=mod.size, base=mod.base, rebase_to=mod.rebase_to) - result = idaapi.get_next_module(mod) - - -def Names(): - """ - Returns a list of names - - @return: List of tuples (ea, name) - """ - for i in xrange(idaapi.get_nlist_size()): - ea = idaapi.get_nlist_ea(i) - name = idaapi.get_nlist_name(i) - yield (ea, name) - - -def Segments(): - """ - Get list of segments (sections) in the binary image - - @return: List of segment start addresses. - """ - for n in xrange(idaapi.get_segm_qty()): - seg = idaapi.getnseg(n) - if seg: - yield seg.startEA - - -def Entries(): - """ - Returns a list of entry points - - @return: List of tuples (index, ordinal, ea, name) - """ - n = idaapi.get_entry_qty() - for i in xrange(0, n): - ordinal = idaapi.get_entry_ordinal(i) - ea = idaapi.get_entry(ordinal) - name = idaapi.get_entry_name(ordinal) - yield (i, ordinal, ea, name) - - -def FuncItems(start): - """ - Get a list of function items - - @param start: address of the function - - @return: ea of each item in the function - """ - func = idaapi.get_func(start) - if not func: - return - fii = idaapi.func_item_iterator_t() - ok = fii.set(func) - while ok: - yield fii.current() - ok = fii.next_code() - - -def Structs(): - """ - Get a list of structures - - @return: List of tuples (idx, sid, name) - """ - idx = idc.GetFirstStrucIdx() - while idx != idaapi.BADADDR: - sid = idc.GetStrucId(idx) - yield (idx, sid, idc.GetStrucName(sid)) - idx = idc.GetNextStrucIdx(idx) - - -def StructMembers(sid): - """ - Get a list of structure members information (or stack vars if given a frame). - - @param sid: ID of the structure. - - @return: List of tuples (offset, name, size) - - @note: If 'sid' does not refer to a valid structure, - an exception will be raised. - @note: This will not return 'holes' in structures/stack frames; - it only returns defined structure members. - """ - m = idc.GetFirstMember(sid) - if m == -1: - raise Exception("No structure with ID: 0x%x" % sid) - while (m != idaapi.BADADDR): - name = idc.GetMemberName(sid, m) - if name: - yield (m, name, idc.GetMemberSize(sid, m)) - m = idc.GetStrucNextOff(sid, m) - - -def DecodePrecedingInstruction(ea): - """ - Decode preceding instruction in the execution flow. - - @param ea: address to decode - @return: (None or the decode instruction, farref) - farref will contain 'true' if followed an xref, false otherwise - """ - prev_addr, farref = idaapi.decode_preceding_insn(ea) - if prev_addr == idaapi.BADADDR: - return (None, False) - else: - return (idaapi.cmd.copy(), farref) - - - -def DecodePreviousInstruction(ea): - """ - Decodes the previous instruction and returns an insn_t like class - - @param ea: address to decode - @return: None or a new insn_t instance - """ - prev_addr = idaapi.decode_prev_insn(ea) - if prev_addr == idaapi.BADADDR: - return None - - return idaapi.cmd.copy() - - -def DecodeInstruction(ea): - """ - Decodes an instruction and returns an insn_t like class - - @param ea: address to decode - @return: None or a new insn_t instance - """ - inslen = idaapi.decode_insn(ea) - if inslen == 0: - return None - - return idaapi.cmd.copy() - - -def GetDataList(ea, count, itemsize=1): - """ - Get data list - INTERNAL USE ONLY - """ - if itemsize == 1: - getdata = idaapi.get_byte - elif itemsize == 2: - getdata = idaapi.get_word - elif itemsize == 4: - getdata = idaapi.get_long - elif itemsize == 8: - getdata = idaapi.get_qword - else: - raise ValueError, "Invalid data size! Must be 1, 2, 4 or 8" - - endea = ea + itemsize * count - curea = ea - while curea < endea: - yield getdata(curea) - curea += itemsize - - -def PutDataList(ea, datalist, itemsize=1): - """ - Put data list - INTERNAL USE ONLY - """ - putdata = None - - if itemsize == 1: - putdata = idaapi.patch_byte - if itemsize == 2: - putdata = idaapi.patch_word - if itemsize == 4: - putdata = idaapi.patch_long - - assert putdata, "Invalid data size! Must be 1, 2 or 4" - - for val in datalist: - putdata(ea, val) - ea = ea + itemsize - - -def MapDataList(ea, length, func, wordsize=1): - """ - Map through a list of data words in the database - - @param ea: start address - @param length: number of words to map - @param func: mapping function - @param wordsize: size of words to map [default: 1 byte] - - @return: None - """ - PutDataList(ea, map(func, GetDataList(ea, length, wordsize)), wordsize) - - -def GetInputFileMD5(): - """ - Return the MD5 hash of the input binary file - - @return: MD5 string or None on error - """ - return idc.GetInputMD5() - - -class Strings(object): - """ - Allows iterating over the string list. The set of strings will not be modified. - , unless asked explicitly at setup()-time.. - - Example: - s = Strings() - - for i in s: - print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i)) - - """ - class StringItem(object): - """ - Class representing each string item. - """ - def __init__(self, si): - self.ea = si.ea - """String ea""" - self.type = si.type - """string type (ASCSTR_xxxxx)""" - self.length = si.length - """string length""" - - def is_1_byte_encoding(self): - return not self.is_2_bytes_encoding() and not self.is_4_bytes_encoding() - - def is_2_bytes_encoding(self): - return (self.type & 7) in [idaapi.ASCSTR_UTF16, idaapi.ASCSTR_ULEN2, idaapi.ASCSTR_ULEN4] - - def is_4_bytes_encoding(self): - return (self.type & 7) == idaapi.ASCSTR_UTF32 - - def _toseq(self, as_unicode): - if self.is_2_bytes_encoding(): - conv = idaapi.ACFOPT_UTF16 - pyenc = "utf-16" - elif self.is_4_bytes_encoding(): - conv = idaapi.ACFOPT_UTF8 - pyenc = "utf-8" - else: - conv = idaapi.ACFOPT_ASCII - pyenc = 'ascii' - strbytes = idaapi.get_ascii_contents2(self.ea, self.length, self.type, conv) - return unicode(strbytes, pyenc, 'replace') if as_unicode else strbytes - - def __str__(self): - return self._toseq(False) - - def __unicode__(self): - return self._toseq(True) - - - STR_C = 0x0001 - """C-style ASCII string""" - STR_PASCAL = 0x0002 - """Pascal-style ASCII string (length byte)""" - STR_LEN2 = 0x0004 - """Pascal-style, length is 2 bytes""" - STR_UNICODE = 0x0008 - """Unicode string""" - STR_LEN4 = 0x0010 - """Pascal-style, length is 4 bytes""" - STR_ULEN2 = 0x0020 - """Pascal-style Unicode, length is 2 bytes""" - STR_ULEN4 = 0x0040 - """Pascal-style Unicode, length is 4 bytes""" - - def clear_cache(self): - """Clears the strings list cache""" - self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache - - def __init__(self, default_setup = False): - """ - Initializes the Strings enumeration helper class - - @param default_setup: Set to True to use default setup (C strings, min len 5, ...) - """ - self.size = 0 - if default_setup: - self.setup() - else: - self.refresh() - - self._si = idaapi.string_info_t() - - def refresh(self, ea1=None, ea2=None): - """Refreshes the strings list""" - if ea1 is None: - ea1 = idaapi.cvar.inf.minEA - if ea2 is None: - ea2 = idaapi.cvar.inf.maxEA - - idaapi.refresh_strlist(ea1, ea2) - self.size = idaapi.get_strlist_qty() - - - def setup(self, - strtypes = STR_C, - minlen = 5, - only_7bit = True, - ignore_instructions = False, - ea1 = None, - ea2 = None, - display_only_existing_strings = False): - - if ea1 is None: - ea1 = idaapi.cvar.inf.minEA - - if ea2 is None: - ea2 = idaapi.cvar.inf.maxEA - - t = idaapi.strwinsetup_t() - t.strtypes = strtypes - t.minlen = minlen - t.only_7bit = only_7bit - t.ea1 = ea1 - t.ea2 = ea2 - t.display_only_existing_strings = display_only_existing_strings - idaapi.set_strlist_options(t) - - # Automatically refreshes - self.refresh() - - - def _get_item(self, index): - if not idaapi.get_strlist_item(index, self._si): - return None - else: - return Strings.StringItem(self._si) - - - def __iter__(self): - return (self._get_item(index) for index in xrange(0, self.size)) - - - def __getitem__(self, index): - """Returns a string item or None""" - if index >= self.size: - raise KeyError - else: - return self._get_item(index) - -# ----------------------------------------------------------------------- -def GetIdbDir(): - """ - Get IDB directory - - This function returns directory path of the current IDB database - """ - return os.path.dirname(idaapi.cvar.database_idb) + os.sep - -# ----------------------------------------------------------------------- -def GetRegisterList(): - """Returns the register list""" - return idaapi.ph_get_regnames() - -# ----------------------------------------------------------------------- -def GetInstructionList(): - """Returns the instruction list of the current processor module""" - return [i[0] for i in idaapi.ph_get_instruc() if i[0]] - -# ----------------------------------------------------------------------- -def _Assemble(ea, line): - """ - Please refer to Assemble() - INTERNAL USE ONLY - """ - if type(line) == types.StringType: - lines = [line] - else: - lines = line - ret = [] - for line in lines: - seg = idaapi.getseg(ea) - if not seg: - return (False, "No segment at ea") - ip = ea - (idaapi.ask_selector(seg.sel) << 4) - buf = idaapi.AssembleLine(ea, seg.sel, ip, seg.bitness, line) - if not buf: - return (False, "Assembler failed: " + line) - ea += len(buf) - ret.append(buf) - - if len(ret) == 1: - ret = ret[0] - return (True, ret) - - -def Assemble(ea, line): - """ - Assembles one or more lines (does not display an message dialogs) - If line is a list then this function will attempt to assemble all the lines - This function will turn on batch mode temporarily so that no messages are displayed on the screen - - @param ea: start address - @return: (False, "Error message") or (True, asm_buf) or (True, [asm_buf1, asm_buf2, asm_buf3]) - """ - old_batch = idc.Batch(1) - ret = _Assemble(ea, line) - idc.Batch(old_batch) - return ret - -def _copy_obj(src, dest, skip_list = None): - """ - Copy non private/non callable attributes from a class instance to another - @param src: Source class to copy from - @param dest: If it is a string then it designates the new class type that will be created and copied to. - Otherwise dest should be an instance of another class - @return: A new instance or "dest" - """ - if type(dest) == types.StringType: - # instantiate a new destination class of the specified type name? - dest = new.classobj(dest, (), {}) - for x in dir(src): - # skip special and private fields - if x.startswith("__") and x.endswith("__"): - continue - # skip items in the skip list - if skip_list and x in skip_list: - continue - t = getattr(src, x) - # skip callable - if callable(t): - continue - setattr(dest, x, t) - return dest - -# ----------------------------------------------------------------------- -class _reg_dtyp_t(object): - """ - INTERNAL - This class describes a register's number and dtyp. - The equal operator is overloaded so that two instances can be tested for equality - """ - def __init__(self, reg, dtyp): - self.reg = reg - self.dtyp = dtyp - - def __eq__(self, other): - return (self.reg == other.reg) and (self.dtyp == other.dtyp) - -# ----------------------------------------------------------------------- -class _procregs(object): - """Utility class allowing the users to identify registers in a decoded instruction""" - def __getattr__(self, attr): - ri = idaapi.reg_info_t() - if not idaapi.parse_reg_name(attr, ri): - raise AttributeError() - r = _reg_dtyp_t(ri.reg, ord(idaapi.get_dtyp_by_size(ri.size))) - self.__dict__[attr] = r - return r - - def __setattr__(self, attr, value): - raise AttributeError(attr) - - -# ----------------------------------------------------------------------- -class _cpu(object): - "Simple wrapper around GetRegValue/SetRegValue" - def __getattr__(self, name): - #print "cpu.get(%s)" % name - return idc.GetRegValue(name) - - def __setattr__(self, name, value): - #print "cpu.set(%s)" % name - return idc.SetRegValue(value, name) - - -# -------------------------------------------------------------------------- -class __process_ui_actions_helper(object): - def __init__(self, actions, flags = 0): - """Expect a list or a string with a list of actions""" - if isinstance(actions, str): - lst = actions.split(";") - elif isinstance(actions, (list, tuple)): - lst = actions - else: - raise ValueError, "Must pass a string, list or a tuple" - - # Remember the action list and the flags - self.__action_list = lst - self.__flags = flags - - # Reset action index - self.__idx = 0 - - def __len__(self): - return len(self.__action_list) - - def __call__(self): - if self.__idx >= len(self.__action_list): - return False - - # Execute one action - idaapi.process_ui_action( - self.__action_list[self.__idx], - self.__flags) - - # Move to next action - self.__idx += 1 - - # Reschedule - return True - - -# -------------------------------------------------------------------------- -def ProcessUiActions(actions, flags=0): - """ - @param actions: A string containing a list of actions separated by semicolon, a list or a tuple - @param flags: flags to be passed to process_ui_action() - @return: Boolean. Returns False if the action list was empty or execute_ui_requests() failed. - """ - - # Instantiate a helper - helper = __process_ui_actions_helper(actions, flags) - return False if len(helper) < 1 else idaapi.execute_ui_requests((helper,)) - - -# ----------------------------------------------------------------------- -class peutils_t(object): - """ - PE utility class. Retrieves PE information from the database. - - Constants from pe.h - """ - PE_NODE = "$ PE header" # netnode name for PE header - PE_ALT_DBG_FPOS = idaapi.BADADDR & -1 # altval() -> translated fpos of debuginfo - PE_ALT_IMAGEBASE = idaapi.BADADDR & -2 # altval() -> loading address (usually pe.imagebase) - PE_ALT_PEHDR_OFF = idaapi.BADADDR & -3 # altval() -> offset of PE header - PE_ALT_NEFLAGS = idaapi.BADADDR & -4 # altval() -> neflags - PE_ALT_TDS_LOADED = idaapi.BADADDR & -5 # altval() -> tds already loaded(1) or invalid(-1) - PE_ALT_PSXDLL = idaapi.BADADDR & -6 # altval() -> if POSIX(x86) imports from PSXDLL netnode - - def __init__(self): - self.__penode = idaapi.netnode() - self.__penode.create(peutils_t.PE_NODE) - - imagebase = property( - lambda self: self.__penode.altval(peutils_t.PE_ALT_IMAGEBASE) - ) - - header = property( - lambda self: self.__penode.altval(peutils_t.PE_ALT_PEHDR_OFF) - ) - - def __str__(self): - return "peutils_t(imagebase=%s, header=%s)" % (hex(self.imagebase), hex(self.header)) - - def header(self): - """ - Returns the complete PE header as an instance of peheader_t (defined in the SDK). - """ - return self.__penode.valobj() - -# ----------------------------------------------------------------------- -cpu = _cpu() -"""This is a special class instance used to access the registers as if they were attributes of this object. -For example to access the EAX register: - print "%x" % cpu.Eax -""" - -procregs = _procregs() -"""This object is used to access the processor registers. It is useful when decoding instructions and you want to see which instruction is which. -For example: - x = idautils.DecodeInstruction(here()) - if x[0] == procregs.Esp: - print "This operand is the register ESP -""" \ No newline at end of file +#--------------------------------------------------------------------- +# IDAPython - Python plugin for Interactive Disassembler +# +# Copyright (c) 2004-2010 Gergely Erdelyi +# +# All rights reserved. +# +# For detailed copyright information see the file COPYING in +# the root of the distribution archive. +#--------------------------------------------------------------------- +""" +idautils.py - High level utility functions for IDA +""" +import idaapi +import idc +import types +import os + + +def refs(ea, funcfirst, funcnext): + """ + Generic reference collector - INTERNAL USE ONLY. + """ + ref = funcfirst(ea) + while ref != idaapi.BADADDR: + yield ref + ref = funcnext(ea, ref) + + +def CodeRefsTo(ea, flow): + """ + Get a list of code references to 'ea' + + @param ea: Target address + @param flow: Follow normal code flow or not + @type flow: Boolean (0/1, False/True) + + @return: list of references (may be empty list) + + Example:: + + for ref in CodeRefsTo(ScreenEA(), 1): + print ref + """ + if flow == 1: + return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to) + else: + return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to) + + +def CodeRefsFrom(ea, flow): + """ + Get a list of code references from 'ea' + + @param ea: Target address + @param flow: Follow normal code flow or not + @type flow: Boolean (0/1, False/True) + + @return: list of references (may be empty list) + + Example:: + + for ref in CodeRefsFrom(ScreenEA(), 1): + print ref + """ + if flow == 1: + return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from) + else: + return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from) + + +def DataRefsTo(ea): + """ + Get a list of data references to 'ea' + + @param ea: Target address + + @return: list of references (may be empty list) + + Example:: + + for ref in DataRefsTo(ScreenEA()): + print ref + """ + return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to) + + +def DataRefsFrom(ea): + """ + Get a list of data references from 'ea' + + @param ea: Target address + + @return: list of references (may be empty list) + + Example:: + + for ref in DataRefsFrom(ScreenEA()): + print ref + """ + return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from) + + +def XrefTypeName(typecode): + """ + Convert cross-reference type codes to readable names + + @param typecode: cross-reference type code + """ + ref_types = { + 0 : 'Data_Unknown', + 1 : 'Data_Offset', + 2 : 'Data_Write', + 3 : 'Data_Read', + 4 : 'Data_Text', + 5 : 'Data_Informational', + 16 : 'Code_Far_Call', + 17 : 'Code_Near_Call', + 18 : 'Code_Far_Jump', + 19 : 'Code_Near_Jump', + 20 : 'Code_User', + 21 : 'Ordinary_Flow' + } + assert typecode in ref_types, "unknown reference type %d" % typecode + return ref_types[typecode] + + +def _copy_xref(xref): + """ Make a private copy of the xref class to preserve its contents """ + class _xref(object): + pass + + xr = _xref() + for attr in [ 'frm', 'to', 'iscode', 'type', 'user' ]: + setattr(xr, attr, getattr(xref, attr)) + return xr + + +def XrefsFrom(ea, flags=0): + """ + Return all references from address 'ea' + + @param ea: Reference address + @param flags: any of idaapi.XREF_* flags + + Example:: + for xref in XrefsFrom(here(), 0): + print xref.type, XrefTypeName(xref.type), \ + 'from', hex(xref.frm), 'to', hex(xref.to) + """ + xref = idaapi.xrefblk_t() + if xref.first_from(ea, flags): + yield _copy_xref(xref) + while xref.next_from(): + yield _copy_xref(xref) + + +def XrefsTo(ea, flags=0): + """ + Return all references to address 'ea' + + @param ea: Reference address + @param flags: any of idaapi.XREF_* flags + + Example:: + for xref in XrefsTo(here(), 0): + print xref.type, XrefTypeName(xref.type), \ + 'from', hex(xref.frm), 'to', hex(xref.to) + """ + xref = idaapi.xrefblk_t() + if xref.first_to(ea, flags): + yield _copy_xref(xref) + while xref.next_to(): + yield _copy_xref(xref) + + +def Threads(): + """Returns all thread IDs""" + for i in xrange(0, idc.GetThreadQty()): + yield idc.GetThreadId(i) + + +def Heads(start=None, end=None): + """ + Get a list of heads (instructions or data) + + @param start: start address (default: inf.minEA) + @param end: end address (default: inf.maxEA) + + @return: list of heads between start and end + """ + if not start: start = idaapi.cvar.inf.minEA + if not end: end = idaapi.cvar.inf.maxEA + + ea = start + if not idc.isHead(idc.GetFlags(ea)): + ea = idaapi.next_head(ea, end) + while ea != idaapi.BADADDR: + yield ea + ea = idaapi.next_head(ea, end) + + +def Functions(start=None, end=None): + """ + Get a list of functions + + @param start: start address (default: inf.minEA) + @param end: end address (default: inf.maxEA) + + @return: list of heads between start and end + + @note: The last function that starts before 'end' is included even + if it extends beyond 'end'. Any function that has its chunks scattered + in multiple segments will be reported multiple times, once in each segment + as they are listed. + """ + if not start: start = idaapi.cvar.inf.minEA + if not end: end = idaapi.cvar.inf.maxEA + + # find first function head chunk in the range + chunk = idaapi.get_fchunk(start) + if not chunk: + chunk = idaapi.get_next_fchunk(start) + while chunk and chunk.startEA < end and (chunk.flags & idaapi.FUNC_TAIL) != 0: + chunk = idaapi.get_next_fchunk(chunk.startEA) + func = chunk + + while func and func.startEA < end: + startea = func.startEA + yield startea + func = idaapi.get_next_func(startea) + + +def Chunks(start): + """ + Get a list of function chunks + + @param start: address of the function + + @return: list of funcion chunks (tuples of the form (start_ea, end_ea)) + belonging to the function + """ + func_iter = idaapi.func_tail_iterator_t( idaapi.get_func( start ) ) + status = func_iter.main() + while status: + chunk = func_iter.chunk() + yield (chunk.startEA, chunk.endEA) + status = func_iter.next() + + +def Modules(): + """ + Returns a list of module objects with name,size,base and the rebase_to attributes + """ + mod = idaapi.module_info_t() + result = idaapi.get_first_module(mod) + while result: + yield idaapi.object_t(name=mod.name, size=mod.size, base=mod.base, rebase_to=mod.rebase_to) + result = idaapi.get_next_module(mod) + + +def Names(): + """ + Returns a list of names + + @return: List of tuples (ea, name) + """ + for i in xrange(idaapi.get_nlist_size()): + ea = idaapi.get_nlist_ea(i) + name = idaapi.get_nlist_name(i) + yield (ea, name) + + +def Segments(): + """ + Get list of segments (sections) in the binary image + + @return: List of segment start addresses. + """ + for n in xrange(idaapi.get_segm_qty()): + seg = idaapi.getnseg(n) + if seg: + yield seg.startEA + + +def Entries(): + """ + Returns a list of entry points + + @return: List of tuples (index, ordinal, ea, name) + """ + n = idaapi.get_entry_qty() + for i in xrange(0, n): + ordinal = idaapi.get_entry_ordinal(i) + ea = idaapi.get_entry(ordinal) + name = idaapi.get_entry_name(ordinal) + yield (i, ordinal, ea, name) + + +def FuncItems(start): + """ + Get a list of function items + + @param start: address of the function + + @return: ea of each item in the function + """ + func = idaapi.get_func(start) + if not func: + return + fii = idaapi.func_item_iterator_t() + ok = fii.set(func) + while ok: + yield fii.current() + ok = fii.next_code() + + +def Structs(): + """ + Get a list of structures + + @return: List of tuples (idx, sid, name) + """ + idx = idc.GetFirstStrucIdx() + while idx != idaapi.BADADDR: + sid = idc.GetStrucId(idx) + yield (idx, sid, idc.GetStrucName(sid)) + idx = idc.GetNextStrucIdx(idx) + + +def StructMembers(sid): + """ + Get a list of structure members information (or stack vars if given a frame). + + @param sid: ID of the structure. + + @return: List of tuples (offset, name, size) + + @note: If 'sid' does not refer to a valid structure, + an exception will be raised. + @note: This will not return 'holes' in structures/stack frames; + it only returns defined structure members. + """ + m = idc.GetFirstMember(sid) + if m == -1: + raise Exception("No structure with ID: 0x%x" % sid) + while (m != idaapi.BADADDR): + name = idc.GetMemberName(sid, m) + if name: + yield (m, name, idc.GetMemberSize(sid, m)) + m = idc.GetStrucNextOff(sid, m) + + +def DecodePrecedingInstruction(ea): + """ + Decode preceding instruction in the execution flow. + + @param ea: address to decode + @return: (None or the decode instruction, farref) + farref will contain 'true' if followed an xref, false otherwise + """ + prev_addr, farref = idaapi.decode_preceding_insn(ea) + if prev_addr == idaapi.BADADDR: + return (None, False) + else: + return (idaapi.cmd.copy(), farref) + + + +def DecodePreviousInstruction(ea): + """ + Decodes the previous instruction and returns an insn_t like class + + @param ea: address to decode + @return: None or a new insn_t instance + """ + prev_addr = idaapi.decode_prev_insn(ea) + if prev_addr == idaapi.BADADDR: + return None + + return idaapi.cmd.copy() + + +def DecodeInstruction(ea): + """ + Decodes an instruction and returns an insn_t like class + + @param ea: address to decode + @return: None or a new insn_t instance + """ + inslen = idaapi.decode_insn(ea) + if inslen == 0: + return None + + return idaapi.cmd.copy() + + +def GetDataList(ea, count, itemsize=1): + """ + Get data list - INTERNAL USE ONLY + """ + if itemsize == 1: + getdata = idaapi.get_byte + elif itemsize == 2: + getdata = idaapi.get_word + elif itemsize == 4: + getdata = idaapi.get_long + elif itemsize == 8: + getdata = idaapi.get_qword + else: + raise ValueError, "Invalid data size! Must be 1, 2, 4 or 8" + + endea = ea + itemsize * count + curea = ea + while curea < endea: + yield getdata(curea) + curea += itemsize + + +def PutDataList(ea, datalist, itemsize=1): + """ + Put data list - INTERNAL USE ONLY + """ + putdata = None + + if itemsize == 1: + putdata = idaapi.patch_byte + if itemsize == 2: + putdata = idaapi.patch_word + if itemsize == 4: + putdata = idaapi.patch_long + + assert putdata, "Invalid data size! Must be 1, 2 or 4" + + for val in datalist: + putdata(ea, val) + ea = ea + itemsize + + +def MapDataList(ea, length, func, wordsize=1): + """ + Map through a list of data words in the database + + @param ea: start address + @param length: number of words to map + @param func: mapping function + @param wordsize: size of words to map [default: 1 byte] + + @return: None + """ + PutDataList(ea, map(func, GetDataList(ea, length, wordsize)), wordsize) + + +def GetInputFileMD5(): + """ + Return the MD5 hash of the input binary file + + @return: MD5 string or None on error + """ + return idc.GetInputMD5() + + +class Strings(object): + """ + Allows iterating over the string list. The set of strings will not be modified. + , unless asked explicitly at setup()-time.. + + Example: + s = Strings() + + for i in s: + print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i)) + + """ + class StringItem(object): + """ + Class representing each string item. + """ + def __init__(self, si): + self.ea = si.ea + """String ea""" + self.type = si.type + """string type (ASCSTR_xxxxx)""" + self.length = si.length + """string length""" + + def is_1_byte_encoding(self): + return not self.is_2_bytes_encoding() and not self.is_4_bytes_encoding() + + def is_2_bytes_encoding(self): + return (self.type & 7) in [idaapi.ASCSTR_UTF16, idaapi.ASCSTR_ULEN2, idaapi.ASCSTR_ULEN4] + + def is_4_bytes_encoding(self): + return (self.type & 7) == idaapi.ASCSTR_UTF32 + + def _toseq(self, as_unicode): + if self.is_2_bytes_encoding(): + conv = idaapi.ACFOPT_UTF16 + pyenc = "utf-16" + elif self.is_4_bytes_encoding(): + conv = idaapi.ACFOPT_UTF8 + pyenc = "utf-8" + else: + conv = idaapi.ACFOPT_ASCII + pyenc = 'ascii' + strbytes = idaapi.get_ascii_contents2(self.ea, self.length, self.type, conv) + return unicode(strbytes, pyenc, 'replace') if as_unicode else strbytes + + def __str__(self): + return self._toseq(False) + + def __unicode__(self): + return self._toseq(True) + + + STR_C = 0x0001 + """C-style ASCII string""" + STR_PASCAL = 0x0002 + """Pascal-style ASCII string (length byte)""" + STR_LEN2 = 0x0004 + """Pascal-style, length is 2 bytes""" + STR_UNICODE = 0x0008 + """Unicode string""" + STR_LEN4 = 0x0010 + """Pascal-style, length is 4 bytes""" + STR_ULEN2 = 0x0020 + """Pascal-style Unicode, length is 2 bytes""" + STR_ULEN4 = 0x0040 + """Pascal-style Unicode, length is 4 bytes""" + + def clear_cache(self): + """Clears the strings list cache""" + self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache + + def __init__(self, default_setup = False): + """ + Initializes the Strings enumeration helper class + + @param default_setup: Set to True to use default setup (C strings, min len 5, ...) + """ + self.size = 0 + if default_setup: + self.setup() + else: + self.refresh() + + self._si = idaapi.string_info_t() + + def refresh(self, ea1=None, ea2=None): + """Refreshes the strings list""" + if ea1 is None: + ea1 = idaapi.cvar.inf.minEA + if ea2 is None: + ea2 = idaapi.cvar.inf.maxEA + + idaapi.refresh_strlist(ea1, ea2) + self.size = idaapi.get_strlist_qty() + + + def setup(self, + strtypes = STR_C, + minlen = 5, + only_7bit = True, + ignore_instructions = False, + ea1 = None, + ea2 = None, + display_only_existing_strings = False): + + if ea1 is None: + ea1 = idaapi.cvar.inf.minEA + + if ea2 is None: + ea2 = idaapi.cvar.inf.maxEA + + t = idaapi.strwinsetup_t() + t.strtypes = strtypes + t.minlen = minlen + t.only_7bit = only_7bit + t.ea1 = ea1 + t.ea2 = ea2 + t.display_only_existing_strings = display_only_existing_strings + idaapi.set_strlist_options(t) + + # Automatically refreshes + self.refresh() + + + def _get_item(self, index): + if not idaapi.get_strlist_item(index, self._si): + return None + else: + return Strings.StringItem(self._si) + + + def __iter__(self): + return (self._get_item(index) for index in xrange(0, self.size)) + + + def __getitem__(self, index): + """Returns a string item or None""" + if index >= self.size: + raise KeyError + else: + return self._get_item(index) + +# ----------------------------------------------------------------------- +def GetIdbDir(): + """ + Get IDB directory + + This function returns directory path of the current IDB database + """ + return os.path.dirname(idaapi.cvar.database_idb) + os.sep + +# ----------------------------------------------------------------------- +def GetRegisterList(): + """Returns the register list""" + return idaapi.ph_get_regnames() + +# ----------------------------------------------------------------------- +def GetInstructionList(): + """Returns the instruction list of the current processor module""" + return [i[0] for i in idaapi.ph_get_instruc() if i[0]] + +# ----------------------------------------------------------------------- +def _Assemble(ea, line): + """ + Please refer to Assemble() - INTERNAL USE ONLY + """ + if type(line) == types.StringType: + lines = [line] + else: + lines = line + ret = [] + for line in lines: + seg = idaapi.getseg(ea) + if not seg: + return (False, "No segment at ea") + ip = ea - (idaapi.ask_selector(seg.sel) << 4) + buf = idaapi.AssembleLine(ea, seg.sel, ip, seg.bitness, line) + if not buf: + return (False, "Assembler failed: " + line) + ea += len(buf) + ret.append(buf) + + if len(ret) == 1: + ret = ret[0] + return (True, ret) + + +def Assemble(ea, line): + """ + Assembles one or more lines (does not display an message dialogs) + If line is a list then this function will attempt to assemble all the lines + This function will turn on batch mode temporarily so that no messages are displayed on the screen + + @param ea: start address + @return: (False, "Error message") or (True, asm_buf) or (True, [asm_buf1, asm_buf2, asm_buf3]) + """ + old_batch = idc.Batch(1) + ret = _Assemble(ea, line) + idc.Batch(old_batch) + return ret + +def _copy_obj(src, dest, skip_list = None): + """ + Copy non private/non callable attributes from a class instance to another + @param src: Source class to copy from + @param dest: If it is a string then it designates the new class type that will be created and copied to. + Otherwise dest should be an instance of another class + @return: A new instance or "dest" + """ + if type(dest) == types.StringType: + # instantiate a new destination class of the specified type name? + dest = new.classobj(dest, (), {}) + for x in dir(src): + # skip special and private fields + if x.startswith("__") and x.endswith("__"): + continue + # skip items in the skip list + if skip_list and x in skip_list: + continue + t = getattr(src, x) + # skip callable + if callable(t): + continue + setattr(dest, x, t) + return dest + +# ----------------------------------------------------------------------- +class _reg_dtyp_t(object): + """ + INTERNAL + This class describes a register's number and dtyp. + The equal operator is overloaded so that two instances can be tested for equality + """ + def __init__(self, reg, dtyp): + self.reg = reg + self.dtyp = dtyp + + def __eq__(self, other): + return (self.reg == other.reg) and (self.dtyp == other.dtyp) + +# ----------------------------------------------------------------------- +class _procregs(object): + """Utility class allowing the users to identify registers in a decoded instruction""" + def __getattr__(self, attr): + ri = idaapi.reg_info_t() + if not idaapi.parse_reg_name(attr, ri): + raise AttributeError() + r = _reg_dtyp_t(ri.reg, ord(idaapi.get_dtyp_by_size(ri.size))) + self.__dict__[attr] = r + return r + + def __setattr__(self, attr, value): + raise AttributeError(attr) + + +# ----------------------------------------------------------------------- +class _cpu(object): + "Simple wrapper around GetRegValue/SetRegValue" + def __getattr__(self, name): + #print "cpu.get(%s)" % name + return idc.GetRegValue(name) + + def __setattr__(self, name, value): + #print "cpu.set(%s)" % name + return idc.SetRegValue(value, name) + + +# -------------------------------------------------------------------------- +class __process_ui_actions_helper(object): + def __init__(self, actions, flags = 0): + """Expect a list or a string with a list of actions""" + if isinstance(actions, str): + lst = actions.split(";") + elif isinstance(actions, (list, tuple)): + lst = actions + else: + raise ValueError, "Must pass a string, list or a tuple" + + # Remember the action list and the flags + self.__action_list = lst + self.__flags = flags + + # Reset action index + self.__idx = 0 + + def __len__(self): + return len(self.__action_list) + + def __call__(self): + if self.__idx >= len(self.__action_list): + return False + + # Execute one action + idaapi.process_ui_action( + self.__action_list[self.__idx], + self.__flags) + + # Move to next action + self.__idx += 1 + + # Reschedule + return True + + +# -------------------------------------------------------------------------- +def ProcessUiActions(actions, flags=0): + """ + @param actions: A string containing a list of actions separated by semicolon, a list or a tuple + @param flags: flags to be passed to process_ui_action() + @return: Boolean. Returns False if the action list was empty or execute_ui_requests() failed. + """ + + # Instantiate a helper + helper = __process_ui_actions_helper(actions, flags) + return False if len(helper) < 1 else idaapi.execute_ui_requests((helper,)) + + +# ----------------------------------------------------------------------- +class peutils_t(object): + """ + PE utility class. Retrieves PE information from the database. + + Constants from pe.h + """ + PE_NODE = "$ PE header" # netnode name for PE header + PE_ALT_DBG_FPOS = idaapi.BADADDR & -1 # altval() -> translated fpos of debuginfo + PE_ALT_IMAGEBASE = idaapi.BADADDR & -2 # altval() -> loading address (usually pe.imagebase) + PE_ALT_PEHDR_OFF = idaapi.BADADDR & -3 # altval() -> offset of PE header + PE_ALT_NEFLAGS = idaapi.BADADDR & -4 # altval() -> neflags + PE_ALT_TDS_LOADED = idaapi.BADADDR & -5 # altval() -> tds already loaded(1) or invalid(-1) + PE_ALT_PSXDLL = idaapi.BADADDR & -6 # altval() -> if POSIX(x86) imports from PSXDLL netnode + + def __init__(self): + self.__penode = idaapi.netnode() + self.__penode.create(peutils_t.PE_NODE) + + imagebase = property( + lambda self: self.__penode.altval(peutils_t.PE_ALT_IMAGEBASE) + ) + + header = property( + lambda self: self.__penode.altval(peutils_t.PE_ALT_PEHDR_OFF) + ) + + def __str__(self): + return "peutils_t(imagebase=%s, header=%s)" % (hex(self.imagebase), hex(self.header)) + + def header(self): + """ + Returns the complete PE header as an instance of peheader_t (defined in the SDK). + """ + return self.__penode.valobj() + +# ----------------------------------------------------------------------- +cpu = _cpu() +"""This is a special class instance used to access the registers as if they were attributes of this object. +For example to access the EAX register: + print "%x" % cpu.Eax +""" + +procregs = _procregs() +"""This object is used to access the processor registers. It is useful when decoding instructions and you want to see which instruction is which. +For example: + x = idautils.DecodeInstruction(here()) + if x[0] == procregs.Esp: + print "This operand is the register ESP +""" diff --git a/python/idc.py b/python/idc.py index 7406f7dc..b34277c4 100644 --- a/python/idc.py +++ b/python/idc.py @@ -3632,7 +3632,7 @@ def GetSegmentAttr(segea, attr): seg = idaapi.getseg(segea) assert seg, "could not find segment at 0x%x" % segea if attr in [ SEGATTR_ES, SEGATTR_CS, SEGATTR_SS, SEGATTR_DS, SEGATTR_FS, SEGATTR_GS ]: - return idaapi.get_defsr(seg, _SEGATTRMAP[attr]) + return idaapi.get_defsr(seg, _SEGATTRMAP[attr][1]) else: return _IDC_GetAttr(seg, _SEGATTRMAP, attr) @@ -3651,7 +3651,7 @@ def SetSegmentAttr(segea, attr, value): seg = idaapi.getseg(segea) assert seg, "could not find segment at 0x%x" % segea if attr in [ SEGATTR_ES, SEGATTR_CS, SEGATTR_SS, SEGATTR_DS, SEGATTR_FS, SEGATTR_GS ]: - idaapi.set_defsr(seg, _SEGATTRMAP[attr], value) + idaapi.set_defsr(seg, _SEGATTRMAP[attr][1], value) else: _IDC_SetAttr(seg, _SEGATTRMAP, attr, value) return seg.update() @@ -3995,7 +3995,11 @@ def SaveFile(filepath, pos, ea, size): @return: 0 - error, 1 - ok """ - of = idaapi.fopenM(filepath) + if ( os.path.isfile(filepath) ): + of = idaapi.fopenM(filepath) + else: + of = idaapi.fopenWB(filepath) + if of: retval = idaapi.base2file(of, pos, ea, ea+size) @@ -5584,6 +5588,23 @@ def SetMemberComment(sid, member_offset, comment, repeatable): return idaapi.set_member_cmt(m, comment, repeatable) +def ExpandStruc(sid, offset, delta, recalc): + """ + Expand or shrink a structure type + @param id: structure type ID + @param offset: offset in the structure + @param delta: how many bytes to add or remove + @param recalc: recalculate the locations where the structure + type is used + @return: != 0 - ok + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + return idaapi.expand_struc(s, offset, delta, recalc) + + def GetFchunkAttr(ea, attr): """ Get a function chunk attribute diff --git a/python/init.py b/python/init.py index dbbb5319..06b74b4a 100644 --- a/python/init.py +++ b/python/init.py @@ -108,4 +108,4 @@ def readline(self): if os.path.exists(userrc): idaapi.IDAPython_ExecScript(userrc, globals()) -# All done, ready to rock. \ No newline at end of file +# All done, ready to rock. diff --git a/pywraps.hpp b/pywraps.hpp index 7a264eaa..36f604f4 100644 --- a/pywraps.hpp +++ b/pywraps.hpp @@ -375,4 +375,27 @@ void deinit_pywraps(); void hexrays_clear_python_cfuncptr_t_references(void); +void free_compiled_form_instances(void); + +//#define PYGDBG_ENABLED +#ifdef PYGDBG_ENABLED +#define PYGLOG(...) msg(__VA_ARGS__) +#else +#define PYGLOG(...) +#endif + +//------------------------------------------------------------------------- +struct pycall_res_t +{ + pycall_res_t(PyObject *pyo); + ~pycall_res_t(); + + inline bool success() const { return result.o != NULL; } + + newref_t result; + +private: + pycall_res_t(); // No. +}; + #endif diff --git a/pywraps/deploy.bat b/pywraps/deploy.bat index 256ad746..713b9d85 100644 --- a/pywraps/deploy.bat +++ b/pywraps/deploy.bat @@ -1,2 +1,2 @@ -@echo off -deploy_all.py +@echo off +deploy_all.py diff --git a/pywraps/deploy.py b/pywraps/deploy.py index ab099402..ce349f95 100644 --- a/pywraps/deploy.py +++ b/pywraps/deploy.py @@ -1,98 +1,98 @@ -""" -Deploy code snips into swig interface files - -(c) Hex-Rays -""" - -import sys -import re -import os - -# creates a regular expression -def make_re(tag, mod_name, prefix): - s = '%(p)s<%(tag)s\(%(m)s\)>(.+?)%(p)s' % {'m': mod_name, 'tag': tag, 'p': prefix} - return (s, re.compile(s, re.DOTALL)) - -def convert_path(path_in): - parts = path_in.split('/') - return os.sep.join(parts) - -def deploy(mod_name, src_files, dest_file, silent = True): - dest_file = convert_path(dest_file) - src_files = map(convert_path, src_files) - # create regular expressions - templates = ( - ('pycode', make_re('pycode', mod_name, '#')), - ('code', make_re('code', mod_name, '//')), - ('inline', make_re('inline', mod_name, '//')) - ) - - if not os.path.exists(dest_file): - print "File", dest_file, "does not exist and will be skipped" - return - - if not os.access(dest_file, os.W_OK): - print "File", dest_file, "is not writable and will be skipped" - return - - # read dest file - dest_lines = "".join(file(dest_file, "r").readlines()) - - # read all source files into one buffer - src_lines = "".join(["".join(file(x, "r").readlines()) for x in src_files]) - - pcount = 0 - for desc, (expr_str, expr) in templates: - # find source pattern - matches = expr.findall(src_lines) - if not matches: - if not silent: - print "Failed to match <%s> source expression against '%s', skipping...!" % (desc, expr_str) - continue - - # find pattern in destination - dest = expr.search(dest_lines) - if not dest: - if not silent: - print "Failed to match <%s> destination expression against '%s', skipping..." % (desc, expr_str) - print dest_lines - sys.exit(0) - continue - - # accumulate all the strings to be replaced - replaces = [] - for src in matches: - replaces.append(src) - - dest_lines = dest_lines[:dest.start(1)] + "\n".join(replaces) + dest_lines[dest.end(1):] - pcount += 1 - - - f = file(dest_file, 'w') - if not f: - print "Failed to open destination file:", dest_file - return - f.write(dest_lines) - f.close() - - if pcount: - print "Deployed successfully: %s (%d)" % (dest_file, pcount) - else: - print "Nothing was deployed in: %s" % dest_file - - -def main(argv = None): - if not argv: - argv = sys.argv - if len(argv) != 4: - print "Usage deploy.py modname src_file1,src_file2,... dest_file" - return - - mod_name = argv[1] - src_files = argv[2].split(',') - dest_file = argv[3] - deploy(mod_name, src_files, dest_file) - -#main(['', 'py_graph', 'py_graph.hpp,py_graph.py', 'graph.i']) -if __name__ == '__main__': - main() +""" +Deploy code snips into swig interface files + +(c) Hex-Rays +""" + +import sys +import re +import os + +# creates a regular expression +def make_re(tag, mod_name, prefix): + s = '%(p)s<%(tag)s\(%(m)s\)>(.+?)%(p)s' % {'m': mod_name, 'tag': tag, 'p': prefix} + return (s, re.compile(s, re.DOTALL)) + +def convert_path(path_in): + parts = path_in.split('/') + return os.sep.join(parts) + +def deploy(mod_name, src_files, dest_file, silent = True): + dest_file = convert_path(dest_file) + src_files = map(convert_path, src_files) + # create regular expressions + templates = ( + ('pycode', make_re('pycode', mod_name, '#')), + ('code', make_re('code', mod_name, '//')), + ('inline', make_re('inline', mod_name, '//')) + ) + + if not os.path.exists(dest_file): + print "File", dest_file, "does not exist and will be skipped" + return + + if not os.access(dest_file, os.W_OK): + print "File", dest_file, "is not writable and will be skipped" + return + + # read dest file + dest_lines = "".join(file(dest_file, "r").readlines()) + + # read all source files into one buffer + src_lines = "".join(["".join(file(x, "r").readlines()) for x in src_files]) + + pcount = 0 + for desc, (expr_str, expr) in templates: + # find source pattern + matches = expr.findall(src_lines) + if not matches: + if not silent: + print "Failed to match <%s> source expression against '%s', skipping...!" % (desc, expr_str) + continue + + # find pattern in destination + dest = expr.search(dest_lines) + if not dest: + if not silent: + print "Failed to match <%s> destination expression against '%s', skipping..." % (desc, expr_str) + print dest_lines + sys.exit(0) + continue + + # accumulate all the strings to be replaced + replaces = [] + for src in matches: + replaces.append(src) + + dest_lines = dest_lines[:dest.start(1)] + "\n".join(replaces) + dest_lines[dest.end(1):] + pcount += 1 + + + f = file(dest_file, 'w') + if not f: + print "Failed to open destination file:", dest_file + return + f.write(dest_lines) + f.close() + + if pcount: + print "Deployed successfully: %s (%d)" % (dest_file, pcount) + else: + print "Nothing was deployed in: %s" % dest_file + + +def main(argv = None): + if not argv: + argv = sys.argv + if len(argv) != 4: + print "Usage deploy.py modname src_file1,src_file2,... dest_file" + return + + mod_name = argv[1] + src_files = argv[2].split(',') + dest_file = argv[3] + deploy(mod_name, src_files, dest_file) + +#main(['', 'py_graph', 'py_graph.hpp,py_graph.py', 'graph.i']) +if __name__ == '__main__': + main() diff --git a/pywraps/driver.cpp b/pywraps/driver.cpp index 2648e249..33fdce96 100644 --- a/pywraps/driver.cpp +++ b/pywraps/driver.cpp @@ -1,172 +1,172 @@ -//-------------------------------------------------------------------------- -// IDA includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-------------------------------------------------------------------------- -// PyWraps -#include -#include "pywraps.hpp" -#include "swig_stub.h" -#include "py_cvt.hpp" -#include "py_idaapi.hpp" -#include "py_graph.hpp" -#include "py_typeinf.hpp" -#include "py_bytes.hpp" -#include "py_linput.hpp" -#include "py_qfile.hpp" -#include "py_ua.hpp" -#include "py_custdata.hpp" -#include "py_notifywhen.hpp" -#include "py_dbg.hpp" -#include "py_choose2.hpp" -#include "py_plgform.hpp" -#include "py_cli.hpp" -#include "py_custview.hpp" -#include "py_lines.hpp" -#include "py_nalt.hpp" -#include "py_loader.hpp" -#include "py_idp.hpp" -#include "py_kernwin.hpp" -#include "py_askusingform.hpp" -#include "py_expr.hpp" - -//-------------------------------------------------------------------------- -qvector all_methods; -void driver_add_methods(PyMethodDef *methods) -{ - for ( ; methods->ml_name != NULL ; ++methods ) - all_methods.push_back(*methods); -} - -//-------------------------------------------------------------------------- -// Define a class and declare an instance so it gets executed on startup -// It will add the desired methods to the all_methods global variable -#define DRIVER_INIT_METHODS(name) \ - class init_##name##_driver_t \ - { \ - public: \ - init_##name##_driver_t() \ - { \ - driver_add_methods(py_methods_##name##); \ - } \ - } init_##name##_driver; - -//-------------------------------------------------------------------------- -// PyWraps test drivers -//#include "driver_kernwin.cpp" -//#include "driver_chooser.cpp" -#include "driver_expr.cpp" -//#include "driver_custview.cpp" -//#include "driver_notifywhen.cpp" -//#include "driver_custdata.cpp" -//#include "driver_graph.cpp" -//#include "driver_diskio.cpp" -//#include "driver_bytes.cpp" -//#include "driver_dbg.cpp" -//#include "driver_nalt.cpp" -//#include "driver_cli.cpp" - -//-------------------------------------------------------------------------- -//#define DRIVER_FIX - -#ifdef DRIVER_FIX - #define PLUGIN_FLAGS PLUGIN_FIX -#else - #define PLUGIN_FLAGS 0 -#endif - -//-------------------------------------------------------------------------- -void setup_pywraps() -{ - static bool installed = false; - if ( installed ) - { - msg("pywraps already installed\n"); - return; - } - static const PyMethodDef null_method = {0}; - all_methods.push_back(null_method); - Py_InitModule("pywraps", all_methods.begin()); - init_pywraps(); - msg("pywraps installed!\n"); - installed = true; -} - -//-------------------------------------------------------------------------- -void idaapi run(int /*arg*/) -{ - setup_pywraps(); -#ifdef DRIVER_RUN - driver_run(0); -#endif -} - -//-------------------------------------------------------------------------- -// -// Initialize. -// -int idaapi init(void) -{ -#ifndef DRIVER_FIX - setup_pywraps(); -#endif -#ifdef DRIVER_INIT - return driver_init(); -#else - return PLUGIN_KEEP; -#endif -} - -//-------------------------------------------------------------------------- -void idaapi term(void) -{ -#ifdef DRIVER_TERM - driver_term(); -#endif -} - -//-------------------------------------------------------------------------- -// -// PLUGIN DESCRIPTION BLOCK -// -//-------------------------------------------------------------------------- -plugin_t PLUGIN = -{ - IDP_INTERFACE_VERSION, - PLUGIN_FLAGS, // plugin flags - init, // initialize - - term, // terminate. this pointer may be NULL. - - run, // invoke plugin - - // long comment about the plugin - "PyWraps plugin", - - // it could appear in the status line - // or as a hint - "", // multiline help about the plugin - - "pywraps", // the preferred short name of the plugin - "" // the preferred hotkey to run the plugin -}; +//-------------------------------------------------------------------------- +// IDA includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------------- +// PyWraps +#include +#include "pywraps.hpp" +#include "swig_stub.h" +#include "py_cvt.hpp" +#include "py_idaapi.hpp" +#include "py_graph.hpp" +#include "py_typeinf.hpp" +#include "py_bytes.hpp" +#include "py_linput.hpp" +#include "py_qfile.hpp" +#include "py_ua.hpp" +#include "py_custdata.hpp" +#include "py_notifywhen.hpp" +#include "py_dbg.hpp" +#include "py_choose2.hpp" +#include "py_plgform.hpp" +#include "py_cli.hpp" +#include "py_custview.hpp" +#include "py_lines.hpp" +#include "py_nalt.hpp" +#include "py_loader.hpp" +#include "py_idp.hpp" +#include "py_kernwin.hpp" +#include "py_askusingform.hpp" +#include "py_expr.hpp" + +//-------------------------------------------------------------------------- +qvector all_methods; +void driver_add_methods(PyMethodDef *methods) +{ + for ( ; methods->ml_name != NULL ; ++methods ) + all_methods.push_back(*methods); +} + +//-------------------------------------------------------------------------- +// Define a class and declare an instance so it gets executed on startup +// It will add the desired methods to the all_methods global variable +#define DRIVER_INIT_METHODS(name) \ + class init_##name##_driver_t \ + { \ + public: \ + init_##name##_driver_t() \ + { \ + driver_add_methods(py_methods_##name##); \ + } \ + } init_##name##_driver; + +//-------------------------------------------------------------------------- +// PyWraps test drivers +//#include "driver_kernwin.cpp" +//#include "driver_chooser.cpp" +#include "driver_expr.cpp" +//#include "driver_custview.cpp" +//#include "driver_notifywhen.cpp" +//#include "driver_custdata.cpp" +//#include "driver_graph.cpp" +//#include "driver_diskio.cpp" +//#include "driver_bytes.cpp" +//#include "driver_dbg.cpp" +//#include "driver_nalt.cpp" +//#include "driver_cli.cpp" + +//-------------------------------------------------------------------------- +//#define DRIVER_FIX + +#ifdef DRIVER_FIX + #define PLUGIN_FLAGS PLUGIN_FIX +#else + #define PLUGIN_FLAGS 0 +#endif + +//-------------------------------------------------------------------------- +void setup_pywraps() +{ + static bool installed = false; + if ( installed ) + { + msg("pywraps already installed\n"); + return; + } + static const PyMethodDef null_method = {0}; + all_methods.push_back(null_method); + Py_InitModule("pywraps", all_methods.begin()); + init_pywraps(); + msg("pywraps installed!\n"); + installed = true; +} + +//-------------------------------------------------------------------------- +void idaapi run(int /*arg*/) +{ + setup_pywraps(); +#ifdef DRIVER_RUN + driver_run(0); +#endif +} + +//-------------------------------------------------------------------------- +// +// Initialize. +// +int idaapi init(void) +{ +#ifndef DRIVER_FIX + setup_pywraps(); +#endif +#ifdef DRIVER_INIT + return driver_init(); +#else + return PLUGIN_KEEP; +#endif +} + +//-------------------------------------------------------------------------- +void idaapi term(void) +{ +#ifdef DRIVER_TERM + driver_term(); +#endif +} + +//-------------------------------------------------------------------------- +// +// PLUGIN DESCRIPTION BLOCK +// +//-------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_FLAGS, // plugin flags + init, // initialize + + term, // terminate. this pointer may be NULL. + + run, // invoke plugin + + // long comment about the plugin + "PyWraps plugin", + + // it could appear in the status line + // or as a hint + "", // multiline help about the plugin + + "pywraps", // the preferred short name of the plugin + "" // the preferred hotkey to run the plugin +}; diff --git a/pywraps/driver_bytes.cpp b/pywraps/driver_bytes.cpp index 4180bfe5..8dd61eba 100644 --- a/pywraps/driver_bytes.cpp +++ b/pywraps/driver_bytes.cpp @@ -1,19 +1,19 @@ -#include "py_bytes.hpp" - -//-------------------------------------------------------------------------- -static PyObject *ex_nextthat(PyObject *self, PyObject *args) -{ - PyObject *callback; - pyul_t addr, bound; - if ( !PyArg_ParseTuple(args, PY_FMT64 PY_FMT64 "O", &addr, &bound, &callback) ) - return NULL; - return Py_BuildValue("i", py_nextthat(pyul_t(addr), pyul_t(bound), callback)); -} - -//-------------------------------------------------------------------------- -static PyMethodDef py_methods_bytes[] = -{ - {"nextthat", ex_nextthat, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} -}; +#include "py_bytes.hpp" + +//-------------------------------------------------------------------------- +static PyObject *ex_nextthat(PyObject *self, PyObject *args) +{ + PyObject *callback; + pyul_t addr, bound; + if ( !PyArg_ParseTuple(args, PY_FMT64 PY_FMT64 "O", &addr, &bound, &callback) ) + return NULL; + return Py_BuildValue("i", py_nextthat(pyul_t(addr), pyul_t(bound), callback)); +} + +//-------------------------------------------------------------------------- +static PyMethodDef py_methods_bytes[] = +{ + {"nextthat", ex_nextthat, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; DRIVER_INIT_METHODS(bytes); \ No newline at end of file diff --git a/pywraps/driver_chooser.cpp b/pywraps/driver_chooser.cpp index 618440cc..f18de181 100644 --- a/pywraps/driver_chooser.cpp +++ b/pywraps/driver_chooser.cpp @@ -1,109 +1,109 @@ -#include "py_choose2.hpp" - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_find(PyObject *self, PyObject *args) -{ - char *title; - if ( !PyArg_ParseTuple(args, "s", &title) ) - return NULL; - else - return choose2_find(title); -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_create(PyObject *self, PyObject *args) -{ - PyObject *obj; - int embedded; - if ( !PyArg_ParseTuple(args, "Oi", &obj, &embedded) ) - return NULL; - else - return PyInt_FromLong(choose2_create(obj, embedded == 1 ? true : false)); -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_activate(PyObject *self, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - - choose2_activate(obj); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_close(PyObject *self, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - - choose2_close(obj); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_refresh(PyObject *self, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - - choose2_refresh(obj); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_add_command(PyObject *self, PyObject *args) -{ - PyObject *obj; - char *caption; - int flags, menu_index, icon; - if ( !PyArg_ParseTuple(args, "Osiii", &obj, &caption, &flags, &menu_index, &icon) ) - return NULL; - else - return PyInt_FromLong(choose2_add_command(obj, caption, flags, menu_index, icon)); -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_get_test_embedded(PyObject *self, PyObject *args) -{ - return PyLong_FromSize_t(choose2_get_test_embedded()); -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_get_embedded(PyObject *self, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - else - return choose2_get_embedded(obj); -} - -//------------------------------------------------------------------------- -static PyObject *ex_choose2_get_embedded_selection(PyObject *self, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - else - return choose2_get_embedded_selection(obj); -} - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_chooser[] = -{ - {"py_choose2_find", ex_choose2_find, METH_VARARGS, ""}, - {"py_choose2_create", ex_choose2_create, METH_VARARGS, ""}, - {"py_choose2_close", ex_choose2_close, METH_VARARGS, ""}, - {"py_choose2_activate", ex_choose2_activate, METH_VARARGS, ""}, - {"py_choose2_refresh", ex_choose2_refresh, METH_VARARGS, ""}, - {"py_choose2_add_command", ex_choose2_add_command, METH_VARARGS, ""}, - {"py_choose2_get_test_embedded", ex_choose2_get_test_embedded, METH_VARARGS, ""}, - {"py_choose2_get_embedded", ex_choose2_get_embedded, METH_VARARGS, ""}, - {"py_choose2_get_embedded_selection", ex_choose2_get_embedded_selection, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} // End of methods -}; -DRIVER_INIT_METHODS(chooser); +#include "py_choose2.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_find(PyObject *self, PyObject *args) +{ + char *title; + if ( !PyArg_ParseTuple(args, "s", &title) ) + return NULL; + else + return choose2_find(title); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_create(PyObject *self, PyObject *args) +{ + PyObject *obj; + int embedded; + if ( !PyArg_ParseTuple(args, "Oi", &obj, &embedded) ) + return NULL; + else + return PyInt_FromLong(choose2_create(obj, embedded == 1 ? true : false)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_activate(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + choose2_activate(obj); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_close(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + choose2_close(obj); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_refresh(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + choose2_refresh(obj); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_add_command(PyObject *self, PyObject *args) +{ + PyObject *obj; + char *caption; + int flags, menu_index, icon; + if ( !PyArg_ParseTuple(args, "Osiii", &obj, &caption, &flags, &menu_index, &icon) ) + return NULL; + else + return PyInt_FromLong(choose2_add_command(obj, caption, flags, menu_index, icon)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_get_test_embedded(PyObject *self, PyObject *args) +{ + return PyLong_FromSize_t(choose2_get_test_embedded()); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_get_embedded(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + else + return choose2_get_embedded(obj); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_get_embedded_selection(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + else + return choose2_get_embedded_selection(obj); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_chooser[] = +{ + {"py_choose2_find", ex_choose2_find, METH_VARARGS, ""}, + {"py_choose2_create", ex_choose2_create, METH_VARARGS, ""}, + {"py_choose2_close", ex_choose2_close, METH_VARARGS, ""}, + {"py_choose2_activate", ex_choose2_activate, METH_VARARGS, ""}, + {"py_choose2_refresh", ex_choose2_refresh, METH_VARARGS, ""}, + {"py_choose2_add_command", ex_choose2_add_command, METH_VARARGS, ""}, + {"py_choose2_get_test_embedded", ex_choose2_get_test_embedded, METH_VARARGS, ""}, + {"py_choose2_get_embedded", ex_choose2_get_embedded, METH_VARARGS, ""}, + {"py_choose2_get_embedded_selection", ex_choose2_get_embedded_selection, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} // End of methods +}; +DRIVER_INIT_METHODS(chooser); diff --git a/pywraps/driver_cli.cpp b/pywraps/driver_cli.cpp index 92b24a13..6b55c82d 100644 --- a/pywraps/driver_cli.cpp +++ b/pywraps/driver_cli.cpp @@ -1,29 +1,29 @@ -#include "py_custview.hpp" - -//------------------------------------------------------------------------- -static PyObject *ex_install_command_interpreter(PyObject *self, PyObject *args) -{ - PyObject *py_obj; - if ( !PyArg_ParseTuple(args, "O", &py_obj) ) - return NULL; - return PyInt_FromLong(py_install_command_interpreter(py_obj)); -} - -//------------------------------------------------------------------------- -static PyObject *ex_remove_command_interpreter(PyObject *self, PyObject *args) -{ - int cli_idx; - if ( !PyArg_ParseTuple(args, "i", &cli_idx) ) - return NULL; - py_remove_command_interpreter(cli_idx); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_cli[] = -{ - {"install_command_interpreter", ex_install_command_interpreter, METH_VARARGS, ""}, - {"remove_command_interpreter", ex_remove_command_interpreter, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} -}; +#include "py_custview.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_install_command_interpreter(PyObject *self, PyObject *args) +{ + PyObject *py_obj; + if ( !PyArg_ParseTuple(args, "O", &py_obj) ) + return NULL; + return PyInt_FromLong(py_install_command_interpreter(py_obj)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_remove_command_interpreter(PyObject *self, PyObject *args) +{ + int cli_idx; + if ( !PyArg_ParseTuple(args, "i", &cli_idx) ) + return NULL; + py_remove_command_interpreter(cli_idx); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_cli[] = +{ + {"install_command_interpreter", ex_install_command_interpreter, METH_VARARGS, ""}, + {"remove_command_interpreter", ex_remove_command_interpreter, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; DRIVER_INIT_METHODS(cli); \ No newline at end of file diff --git a/pywraps/driver_custview.cpp b/pywraps/driver_custview.cpp index 66a9a054..4b8515b7 100644 --- a/pywraps/driver_custview.cpp +++ b/pywraps/driver_custview.cpp @@ -1,384 +1,384 @@ -#include "py_custview.hpp" - -//-------------------------------------------------------------------------- -class my_custviewer: public customviewer_t -{ -private: - cvdata_simpleline_t data; - size_t id_n; - virtual bool on_popup_menu(size_t menu_id) - { - if ( menu_id == id_n ) - msg("popup menu N chosen!\n"); - return true; - } - virtual bool on_click(int shift) - { - msg("onclick; shift=%d\n", shift); - return true; - } - virtual void on_close() - { - id_n = 0; - msg("closed...\n"); - } - virtual bool on_keydown(int key, int shift) - { - switch ( key ) - { - case 'N': - warning("The hotkey 'N' has been pressed"); - return true; - case 'I': - { - int x, y; - place_t *pl = get_place(false, &x, &y); - if ( pl == NULL ) - return false; - msg("x=%d y=%d\n", x, y); - simpleline_t sl = *data.get_line(pl); - sl.bgcolor = bgcolor_t(~uint32(sl.bgcolor)); - data.set_line(data.to_lineno(pl), sl); - refresh_current(); - return true; - } - case 'A': - { - char buf[100]; - qsnprintf(buf, sizeof(buf), "This is line %d\n", data.count()); - data.add_line(buf); - msg("Added one more line...\n"); - return true; - } - case 'S': - { - twinpos_t p1, p2; - ::readsel2(_cv, &p1, &p2); - size_t y1 = data.to_lineno(p1.at); - size_t y2 = data.to_lineno(p2.at); - int x1 = p1.x; - int x2 = p2.x; - msg("(x1=%d y1=%d) (x2=%d y2=%d)", x1, y1, x2, y2); - return true; - } - case 'X': - data.set_minmax(); - return true; - case 'R': - refresh(); - msg("refreshing!\n"); - return true; - case IK_ESCAPE: - close(); - return true; - } - return false; - } - virtual void on_curpos_changed() - { - qstring word; - if ( get_current_word(false, word) ) - msg("Current word is: %s\n", word.c_str()); - } - virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) - { - simpleline_t *line = data.get_line(place); - if ( line == NULL ) - return false; - *important_lines = 1; - hint = line->line; - return true; - } - -public: - void init_sample_lines() - { - strvec_t &lines = data.get_lines(); - static struct - { - const char *text; - bgcolor_t color; - } const sample_lines[] = - { - { "This is a sample text", 0xFFFFFF }, - { "It will be displayed in the custom view", 0xFFC0C0 }, - { COLSTR("This line will be colored as erroneous", SCOLOR_ERROR), 0xC0FFC0 }, - { COLSTR("Every", SCOLOR_AUTOCMT) " " - COLSTR("word", SCOLOR_DNAME) " " - COLSTR("can", SCOLOR_IMPNAME) " " - COLSTR("be", SCOLOR_NUMBER) " " - COLSTR("colored!", SCOLOR_EXTRA), 0xC0C0FF }, - { " No limit on the number of lines.", 0xC0FFFF }, - }; - for ( int i=0; iinit("My sample viewer!") ) - { - msg("Failed to create cv\n!"); - return; - } - g_cv->show(); -} - -#define DRIVER_INIT -int driver_init() -{ - g_cv = new my_custviewer(); - return PLUGIN_KEEP; -} - -#define DRIVER_TERM -void driver_term() -{ - g_cv->close(); - delete g_cv; -} +#include "py_custview.hpp" + +//-------------------------------------------------------------------------- +class my_custviewer: public customviewer_t +{ +private: + cvdata_simpleline_t data; + size_t id_n; + virtual bool on_popup_menu(size_t menu_id) + { + if ( menu_id == id_n ) + msg("popup menu N chosen!\n"); + return true; + } + virtual bool on_click(int shift) + { + msg("onclick; shift=%d\n", shift); + return true; + } + virtual void on_close() + { + id_n = 0; + msg("closed...\n"); + } + virtual bool on_keydown(int key, int shift) + { + switch ( key ) + { + case 'N': + warning("The hotkey 'N' has been pressed"); + return true; + case 'I': + { + int x, y; + place_t *pl = get_place(false, &x, &y); + if ( pl == NULL ) + return false; + msg("x=%d y=%d\n", x, y); + simpleline_t sl = *data.get_line(pl); + sl.bgcolor = bgcolor_t(~uint32(sl.bgcolor)); + data.set_line(data.to_lineno(pl), sl); + refresh_current(); + return true; + } + case 'A': + { + char buf[100]; + qsnprintf(buf, sizeof(buf), "This is line %d\n", data.count()); + data.add_line(buf); + msg("Added one more line...\n"); + return true; + } + case 'S': + { + twinpos_t p1, p2; + ::readsel2(_cv, &p1, &p2); + size_t y1 = data.to_lineno(p1.at); + size_t y2 = data.to_lineno(p2.at); + int x1 = p1.x; + int x2 = p2.x; + msg("(x1=%d y1=%d) (x2=%d y2=%d)", x1, y1, x2, y2); + return true; + } + case 'X': + data.set_minmax(); + return true; + case 'R': + refresh(); + msg("refreshing!\n"); + return true; + case IK_ESCAPE: + close(); + return true; + } + return false; + } + virtual void on_curpos_changed() + { + qstring word; + if ( get_current_word(false, word) ) + msg("Current word is: %s\n", word.c_str()); + } + virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) + { + simpleline_t *line = data.get_line(place); + if ( line == NULL ) + return false; + *important_lines = 1; + hint = line->line; + return true; + } + +public: + void init_sample_lines() + { + strvec_t &lines = data.get_lines(); + static struct + { + const char *text; + bgcolor_t color; + } const sample_lines[] = + { + { "This is a sample text", 0xFFFFFF }, + { "It will be displayed in the custom view", 0xFFC0C0 }, + { COLSTR("This line will be colored as erroneous", SCOLOR_ERROR), 0xC0FFC0 }, + { COLSTR("Every", SCOLOR_AUTOCMT) " " + COLSTR("word", SCOLOR_DNAME) " " + COLSTR("can", SCOLOR_IMPNAME) " " + COLSTR("be", SCOLOR_NUMBER) " " + COLSTR("colored!", SCOLOR_EXTRA), 0xC0C0FF }, + { " No limit on the number of lines.", 0xC0FFFF }, + }; + for ( int i=0; iinit("My sample viewer!") ) + { + msg("Failed to create cv\n!"); + return; + } + g_cv->show(); +} + +#define DRIVER_INIT +int driver_init() +{ + g_cv = new my_custviewer(); + return PLUGIN_KEEP; +} + +#define DRIVER_TERM +void driver_term() +{ + g_cv->close(); + delete g_cv; +} diff --git a/pywraps/driver_dbg.cpp b/pywraps/driver_dbg.cpp index 8fbe0de1..d662b277 100644 --- a/pywraps/driver_dbg.cpp +++ b/pywraps/driver_dbg.cpp @@ -1,79 +1,79 @@ -#include "py_dbg.hpp" - -//------------------------------------------------------------------------- -static PyObject *ex_getthreadsregbase(PyObject * /*self*/, PyObject *args) -{ - PyObject *py_tid, *py_sreg_value; - if ( !PyArg_ParseTuple(args, "OO", &py_tid, &py_sreg_value) ) - return NULL; - return dbg_get_thread_sreg_base(py_tid, py_sreg_value); -} - -//------------------------------------------------------------------------- -static PyObject *ex_readmemory(PyObject * /*self*/, PyObject *args) -{ - PyObject *py_ea, *py_size; - if ( !PyArg_ParseTuple(args, "OO", &py_ea, &py_size) ) - return NULL; - return dbg_read_memory(py_ea, py_size); -} - -//------------------------------------------------------------------------- -static PyObject *ex_writememory(PyObject * /*self*/, PyObject *args) -{ - PyObject *py_ea, *py_buf; - if ( !PyArg_ParseTuple(args, "OO", &py_ea, &py_buf) ) - return NULL; - return dbg_write_memory(py_ea, py_buf); -} - -//------------------------------------------------------------------------- -static PyObject *ex_getmeminfo(PyObject * /*self*/, PyObject *args) -{ - return dbg_get_memory_info(); -} - -//------------------------------------------------------------------------- -static PyObject *ex_getregs(PyObject *self, PyObject *args) -{ - return dbg_get_registers(); -} - -//------------------------------------------------------------------------- -static PyObject *ex_appcall(PyObject * /*self*/, PyObject *args) -{ - PyObject *app_args, *type, *fields; - int func_ea, tid; - if ( !PyArg_ParseTuple(args, "iiOOO", &func_ea, &tid, &type, &fields, &app_args) ) - return NULL; - return py_appcall(func_ea, tid, type, fields, app_args); -} - -//------------------------------------------------------------------------- -static PyObject *ex_pytoidc( - PyObject *self, - PyObject *args) -{ - if ( !PyArg_ParseTuple(args, "O", &self) ) - return NULL; - idc_value_t v; - int sn = 0; - borref_t self_ref(self); - if ( pyvar_to_idcvar(self_ref, &v, &sn) < CIP_OK ) - Py_RETURN_NONE; - Py_RETURN_TRUE; -} - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_dbg[] = -{ - {"getregs", ex_getregs, METH_VARARGS, ""}, - {"getmeminfo", ex_getmeminfo, METH_VARARGS, ""}, - {"readmemory", ex_readmemory, METH_VARARGS, ""}, - {"writememory", ex_writememory, METH_VARARGS, ""}, - {"getthreadsregbase", ex_getthreadsregbase, METH_VARARGS, ""}, - {"appcall", ex_appcall, METH_VARARGS, ""}, - {"pytoidc", ex_pytoidc, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; -DRIVER_INIT_METHODS(dbg); +#include "py_dbg.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_getthreadsregbase(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_tid, *py_sreg_value; + if ( !PyArg_ParseTuple(args, "OO", &py_tid, &py_sreg_value) ) + return NULL; + return dbg_get_thread_sreg_base(py_tid, py_sreg_value); +} + +//------------------------------------------------------------------------- +static PyObject *ex_readmemory(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_ea, *py_size; + if ( !PyArg_ParseTuple(args, "OO", &py_ea, &py_size) ) + return NULL; + return dbg_read_memory(py_ea, py_size); +} + +//------------------------------------------------------------------------- +static PyObject *ex_writememory(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_ea, *py_buf; + if ( !PyArg_ParseTuple(args, "OO", &py_ea, &py_buf) ) + return NULL; + return dbg_write_memory(py_ea, py_buf); +} + +//------------------------------------------------------------------------- +static PyObject *ex_getmeminfo(PyObject * /*self*/, PyObject *args) +{ + return dbg_get_memory_info(); +} + +//------------------------------------------------------------------------- +static PyObject *ex_getregs(PyObject *self, PyObject *args) +{ + return dbg_get_registers(); +} + +//------------------------------------------------------------------------- +static PyObject *ex_appcall(PyObject * /*self*/, PyObject *args) +{ + PyObject *app_args, *type, *fields; + int func_ea, tid; + if ( !PyArg_ParseTuple(args, "iiOOO", &func_ea, &tid, &type, &fields, &app_args) ) + return NULL; + return py_appcall(func_ea, tid, type, fields, app_args); +} + +//------------------------------------------------------------------------- +static PyObject *ex_pytoidc( + PyObject *self, + PyObject *args) +{ + if ( !PyArg_ParseTuple(args, "O", &self) ) + return NULL; + idc_value_t v; + int sn = 0; + borref_t self_ref(self); + if ( pyvar_to_idcvar(self_ref, &v, &sn) < CIP_OK ) + Py_RETURN_NONE; + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_dbg[] = +{ + {"getregs", ex_getregs, METH_VARARGS, ""}, + {"getmeminfo", ex_getmeminfo, METH_VARARGS, ""}, + {"readmemory", ex_readmemory, METH_VARARGS, ""}, + {"writememory", ex_writememory, METH_VARARGS, ""}, + {"getthreadsregbase", ex_getthreadsregbase, METH_VARARGS, ""}, + {"appcall", ex_appcall, METH_VARARGS, ""}, + {"pytoidc", ex_pytoidc, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(dbg); diff --git a/pywraps/driver_diskio.cpp b/pywraps/driver_diskio.cpp index 566541e4..69450743 100644 --- a/pywraps/driver_diskio.cpp +++ b/pywraps/driver_diskio.cpp @@ -1,47 +1,47 @@ -#include "py_diskio.hpp" - -static PyObject *ex_enumfiles(PyObject * /*self*/, PyObject *args) -{ - PyObject *path, *fname, *callback; - if ( !PyArg_ParseTuple(args, "OOO", &path, &fname, &callback) ) - return NULL; - return py_enumerate_files(path, fname, callback); -} - -// -//static PyObject *ex_linput_close(PyObject * /*self*/, PyObject *args) -//{ -// PyObject *obj; -// if ( !PyArg_ParseTuple(args, "O", &obj) ) -// return NULL; -// pyl_close(obj); -// Py_RETURN_NONE; -//} -// -//static PyObject *ex_linput_open(PyObject *self, PyObject *args) -//{ -// PyObject *obj, *py_filename, *py_remote; -// if ( !PyArg_ParseTuple(args, "OOO", &obj, &py_filename, &py_remote) ) -// return NULL; -// return pyl_open(obj, py_filename, py_remote); -//} -// -//static PyObject *ex_linput_read(PyObject *self, PyObject *args) -//{ -// PyObject *obj, *py_size; -// if ( !PyArg_ParseTuple(args, "OO", &obj, &py_size) ) -// return NULL; -// return pyl_read(obj, py_size); -//} - -static PyMethodDef py_methods_diskio[] = -{ - {"enumfiles", ex_enumfiles, METH_VARARGS, ""}, - //{"tell", ex_linput_tell, METH_VARARGS, ""}, - //{"open", ex_linput_open, METH_VARARGS, ""}, - //{"size", ex_linput_tell, METH_VARARGS, ""}, - //{"read", ex_linput_read, METH_VARARGS, ""}, - //{"close", ex_linput_close, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; +#include "py_diskio.hpp" + +static PyObject *ex_enumfiles(PyObject * /*self*/, PyObject *args) +{ + PyObject *path, *fname, *callback; + if ( !PyArg_ParseTuple(args, "OOO", &path, &fname, &callback) ) + return NULL; + return py_enumerate_files(path, fname, callback); +} + +// +//static PyObject *ex_linput_close(PyObject * /*self*/, PyObject *args) +//{ +// PyObject *obj; +// if ( !PyArg_ParseTuple(args, "O", &obj) ) +// return NULL; +// pyl_close(obj); +// Py_RETURN_NONE; +//} +// +//static PyObject *ex_linput_open(PyObject *self, PyObject *args) +//{ +// PyObject *obj, *py_filename, *py_remote; +// if ( !PyArg_ParseTuple(args, "OOO", &obj, &py_filename, &py_remote) ) +// return NULL; +// return pyl_open(obj, py_filename, py_remote); +//} +// +//static PyObject *ex_linput_read(PyObject *self, PyObject *args) +//{ +// PyObject *obj, *py_size; +// if ( !PyArg_ParseTuple(args, "OO", &obj, &py_size) ) +// return NULL; +// return pyl_read(obj, py_size); +//} + +static PyMethodDef py_methods_diskio[] = +{ + {"enumfiles", ex_enumfiles, METH_VARARGS, ""}, + //{"tell", ex_linput_tell, METH_VARARGS, ""}, + //{"open", ex_linput_open, METH_VARARGS, ""}, + //{"size", ex_linput_tell, METH_VARARGS, ""}, + //{"read", ex_linput_read, METH_VARARGS, ""}, + //{"close", ex_linput_close, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; DRIVER_INIT_METHODS(diskio); \ No newline at end of file diff --git a/pywraps/driver_expr.cpp b/pywraps/driver_expr.cpp index ef8db432..be2703c5 100644 --- a/pywraps/driver_expr.cpp +++ b/pywraps/driver_expr.cpp @@ -1,56 +1,56 @@ -#include "py_expr.hpp" - -#pragma warning(push) -#pragma warning(disable: 4244) - -//--------------------------------------------------------------------------- +#include "py_expr.hpp" + +#pragma warning(push) +#pragma warning(disable: 4244) + +//--------------------------------------------------------------------------- static PyObject *ex_pyw_register_idc_func(PyObject *self, PyObject *args) { - char *name, *arg; - PyObject *py_fp; - if ( !PyArg_ParseTuple(args, "ssO", &name, &arg, &py_fp) ) - return NULL; - else - return PyLong_FromUnsignedLongLong(pyw_register_idc_func(name, arg, py_fp)); + char *name, *arg; + PyObject *py_fp; + if ( !PyArg_ParseTuple(args, "ssO", &name, &arg, &py_fp) ) + return NULL; + else + return PyLong_FromUnsignedLongLong(pyw_register_idc_func(name, arg, py_fp)); } - -//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- static PyObject *ex_pyw_unregister_idc_func(PyObject *self, PyObject *args) { unsigned PY_LONG_LONG ctxptr; - if ( !PyArg_ParseTuple(args, "K", &ctxptr) ) - return NULL; - return PyLong_FromLong(pyw_unregister_idc_func(ctxptr)); + if ( !PyArg_ParseTuple(args, "K", &ctxptr) ) + return NULL; + return PyLong_FromLong(pyw_unregister_idc_func(ctxptr)); } - + static PyObject *ex_py_set_idc_func_ex(PyObject *self, PyObject *pyargs) { - const char *name; - unsigned PY_LONG_LONG fp_ptr; - const char *args; + const char *name; + unsigned PY_LONG_LONG fp_ptr; + const char *args; int flags; - if ( !PyArg_ParseTuple(pyargs, "sKsi", &name, &fp_ptr, &args, &flags) ) - return NULL; - else - return PyLong_FromLong(py_set_idc_func_ex(name, fp_ptr, args, flags)); + if ( !PyArg_ParseTuple(pyargs, "sKsi", &name, &fp_ptr, &args, &flags) ) + return NULL; + else + return PyLong_FromLong(py_set_idc_func_ex(name, fp_ptr, args, flags)); } - -//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- static PyObject *ex_py_get_call_idc_func(PyObject *self, PyObject *args) { return PyLong_FromUnsignedLongLong(py_get_call_idc_func()); } -//------------------------------------------------------------------------- +//------------------------------------------------------------------------- #pragma warning(pop) - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_expr[] = -{ - {"pyw_register_idc_func", ex_pyw_register_idc_func, METH_VARARGS, ""}, - {"pyw_unregister_idc_func", ex_pyw_unregister_idc_func, METH_VARARGS, ""}, - {"py_get_call_idc_func", ex_py_get_call_idc_func, METH_VARARGS, ""}, - {"py_set_idc_func_ex", ex_py_set_idc_func_ex, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} // End of methods -}; -DRIVER_INIT_METHODS(expr); + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_expr[] = +{ + {"pyw_register_idc_func", ex_pyw_register_idc_func, METH_VARARGS, ""}, + {"pyw_unregister_idc_func", ex_pyw_unregister_idc_func, METH_VARARGS, ""}, + {"py_get_call_idc_func", ex_py_get_call_idc_func, METH_VARARGS, ""}, + {"py_set_idc_func_ex", ex_py_set_idc_func_ex, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} // End of methods +}; +DRIVER_INIT_METHODS(expr); diff --git a/pywraps/driver_graph.cpp b/pywraps/driver_graph.cpp index b44e2328..c6d0ab1a 100644 --- a/pywraps/driver_graph.cpp +++ b/pywraps/driver_graph.cpp @@ -1,44 +1,44 @@ -#include "py_graph.hpp" - -//-------------------------------------------------------------------------- -//py_choose2_t *last_c2 = NULL; -static PyObject *ex_graph_show(PyObject * /*self*/, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - - py_graph_t *ret = py_graph_t::Show(obj); - return PyBool_FromLong(ret == NULL ? 0 : 1); -} - -//-------------------------------------------------------------------------- -static PyObject *ex_graph_refresh(PyObject * /*self*/, PyObject *args) -{ - PyObject *obj; - if ( !PyArg_ParseTuple(args, "O", &obj) ) - return NULL; - py_graph_t::Refresh(obj); - Py_RETURN_NONE; -} - -//-------------------------------------------------------------------------- -static PyObject *ex_graph_addcmd(PyObject *self, PyObject *args) -{ - PyObject *obj; - const char *title, *hotkey; - if ( !PyArg_ParseTuple(args, "Oss", &obj, &title, &hotkey) ) - return NULL; - Py_ssize_t r = py_graph_t::AddCommand(obj, title, hotkey); - return Py_BuildValue("n", r); -} - -//-------------------------------------------------------------------------- -static PyMethodDef py_methods_graph[] = -{ - {"show", ex_graph_show, METH_VARARGS, ""}, - {"refresh", ex_graph_refresh, METH_VARARGS, ""}, - {"addcmd", ex_graph_addcmd, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; +#include "py_graph.hpp" + +//-------------------------------------------------------------------------- +//py_choose2_t *last_c2 = NULL; +static PyObject *ex_graph_show(PyObject * /*self*/, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + py_graph_t *ret = py_graph_t::Show(obj); + return PyBool_FromLong(ret == NULL ? 0 : 1); +} + +//-------------------------------------------------------------------------- +static PyObject *ex_graph_refresh(PyObject * /*self*/, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + py_graph_t::Refresh(obj); + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +static PyObject *ex_graph_addcmd(PyObject *self, PyObject *args) +{ + PyObject *obj; + const char *title, *hotkey; + if ( !PyArg_ParseTuple(args, "Oss", &obj, &title, &hotkey) ) + return NULL; + Py_ssize_t r = py_graph_t::AddCommand(obj, title, hotkey); + return Py_BuildValue("n", r); +} + +//-------------------------------------------------------------------------- +static PyMethodDef py_methods_graph[] = +{ + {"show", ex_graph_show, METH_VARARGS, ""}, + {"refresh", ex_graph_refresh, METH_VARARGS, ""}, + {"addcmd", ex_graph_addcmd, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; DRIVER_INIT_METHODS(graph); \ No newline at end of file diff --git a/pywraps/driver_kernwin.cpp b/pywraps/driver_kernwin.cpp index 6e3ea094..c2346d17 100644 --- a/pywraps/driver_kernwin.cpp +++ b/pywraps/driver_kernwin.cpp @@ -1,78 +1,78 @@ -#include "py_kernwin.hpp" - -//------------------------------------------------------------------------- -static PyObject *ex_add_menu_item(PyObject *self, PyObject *args) -{ - const char *menupath, *name, *hotkey; - PyObject *pyfunc, *pyargs; - int flags; - if ( !PyArg_ParseTuple(args, "sssiOO", &menupath, &name, &hotkey, &flags, &pyfunc, &pyargs) ) - return NULL; - return py_add_menu_item(menupath, name, hotkey, flags, pyfunc, pyargs); -} - -//------------------------------------------------------------------------- -static PyObject *ex_del_menu_item(PyObject *self, PyObject *args) -{ - if ( !PyArg_ParseTuple(args, "O", &self) ) - return NULL; - if ( py_del_menu_item(self) ) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -//------------------------------------------------------------------------- -static PyObject *ex_execute_sync(PyObject *self, PyObject *args) -{ - PyObject *pycall; - int reqf; - if ( !PyArg_ParseTuple(args, "Oi", &pycall, &reqf) ) - return NULL; - return PyInt_FromLong(py_execute_sync(pycall, reqf)); -} - -//------------------------------------------------------------------------- -static PyObject *ex_add_hotkey(PyObject *self, PyObject *args) -{ - PyObject *pyfunc; - const char *hotkey; - if ( !PyArg_ParseTuple(args, "sO", &hotkey, &pyfunc) ) - return NULL; - else - return py_add_hotkey(hotkey, pyfunc); -} - -//------------------------------------------------------------------------- -static PyObject *ex_del_hotkey(PyObject *self, PyObject *args) -{ - PyObject *pyctx; - if ( !PyArg_ParseTuple(args, "O", &pyctx) ) - return NULL; - else - return PyInt_FromLong(py_del_hotkey(pyctx) ? 1 : 0); -} - -//------------------------------------------------------------------------- -static PyObject *ex_execute_ui_request(PyObject *self, PyObject *args) -{ - PyObject *py_list; - if ( !PyArg_ParseTuple(args, "O", &py_list) ) - return NULL; - else - return PyBool_FromLong(py_execute_ui_requests(py_list) ? 1 : 0); -} - - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_kernwin[] = -{ - {"py_del_menu_item", ex_del_menu_item, METH_VARARGS, ""}, - {"py_add_menu_item", ex_add_menu_item, METH_VARARGS, ""}, - {"py_execute_sync", ex_execute_sync, METH_VARARGS, ""}, - {"py_add_hotkey", ex_add_hotkey, METH_VARARGS, ""}, - {"py_del_hotkey", ex_del_hotkey, METH_VARARGS, ""}, - {"py_execute_ui_request", ex_execute_ui_request, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; +#include "py_kernwin.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_add_menu_item(PyObject *self, PyObject *args) +{ + const char *menupath, *name, *hotkey; + PyObject *pyfunc, *pyargs; + int flags; + if ( !PyArg_ParseTuple(args, "sssiOO", &menupath, &name, &hotkey, &flags, &pyfunc, &pyargs) ) + return NULL; + return py_add_menu_item(menupath, name, hotkey, flags, pyfunc, pyargs); +} + +//------------------------------------------------------------------------- +static PyObject *ex_del_menu_item(PyObject *self, PyObject *args) +{ + if ( !PyArg_ParseTuple(args, "O", &self) ) + return NULL; + if ( py_del_menu_item(self) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_execute_sync(PyObject *self, PyObject *args) +{ + PyObject *pycall; + int reqf; + if ( !PyArg_ParseTuple(args, "Oi", &pycall, &reqf) ) + return NULL; + return PyInt_FromLong(py_execute_sync(pycall, reqf)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_add_hotkey(PyObject *self, PyObject *args) +{ + PyObject *pyfunc; + const char *hotkey; + if ( !PyArg_ParseTuple(args, "sO", &hotkey, &pyfunc) ) + return NULL; + else + return py_add_hotkey(hotkey, pyfunc); +} + +//------------------------------------------------------------------------- +static PyObject *ex_del_hotkey(PyObject *self, PyObject *args) +{ + PyObject *pyctx; + if ( !PyArg_ParseTuple(args, "O", &pyctx) ) + return NULL; + else + return PyInt_FromLong(py_del_hotkey(pyctx) ? 1 : 0); +} + +//------------------------------------------------------------------------- +static PyObject *ex_execute_ui_request(PyObject *self, PyObject *args) +{ + PyObject *py_list; + if ( !PyArg_ParseTuple(args, "O", &py_list) ) + return NULL; + else + return PyBool_FromLong(py_execute_ui_requests(py_list) ? 1 : 0); +} + + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_kernwin[] = +{ + {"py_del_menu_item", ex_del_menu_item, METH_VARARGS, ""}, + {"py_add_menu_item", ex_add_menu_item, METH_VARARGS, ""}, + {"py_execute_sync", ex_execute_sync, METH_VARARGS, ""}, + {"py_add_hotkey", ex_add_hotkey, METH_VARARGS, ""}, + {"py_del_hotkey", ex_del_hotkey, METH_VARARGS, ""}, + {"py_execute_ui_request", ex_execute_ui_request, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; DRIVER_INIT_METHODS(kernwin); \ No newline at end of file diff --git a/pywraps/driver_nalt.cpp b/pywraps/driver_nalt.cpp index 5ff9e564..4588b7e7 100644 --- a/pywraps/driver_nalt.cpp +++ b/pywraps/driver_nalt.cpp @@ -1,18 +1,18 @@ -#include "py_nalt.hpp" - -//------------------------------------------------------------------------- -static PyObject *ex_get_switch_info_ex(PyObject *self, PyObject *args) -{ - pyul_t ea; - if ( !PyArg_ParseTuple(args, PY_FMT64, &ea) ) - return NULL; - return py_get_switch_info_ex(ea_t(ea)); -} - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_nalt[] = -{ - {"get_switch_info_ex", ex_get_switch_info_ex, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} -}; -DRIVER_INIT_METHODS(nalt); +#include "py_nalt.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_get_switch_info_ex(PyObject *self, PyObject *args) +{ + pyul_t ea; + if ( !PyArg_ParseTuple(args, PY_FMT64, &ea) ) + return NULL; + return py_get_switch_info_ex(ea_t(ea)); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_nalt[] = +{ + {"get_switch_info_ex", ex_get_switch_info_ex, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; +DRIVER_INIT_METHODS(nalt); diff --git a/pywraps/driver_notifywhen.cpp b/pywraps/driver_notifywhen.cpp index 1d440044..ea0cc120 100644 --- a/pywraps/driver_notifywhen.cpp +++ b/pywraps/driver_notifywhen.cpp @@ -1,36 +1,36 @@ -#include "py_notifywhen.hpp" - -//------------------------------------------------------------------------- -static PyObject *ex_notify_when(PyObject *self, PyObject *args) -{ - int when; - PyObject *py_callable; - if ( !PyArg_ParseTuple(args, "IO", &when, &py_callable) ) - return NULL; - return Py_BuildValue("i", notify_when(when, py_callable)); -} - -//------------------------------------------------------------------------- -static PyMethodDef py_methods_nw[] = -{ - {"notify_when", ex_notify_when, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; -DRIVER_INIT_METHODS(nw); - -#define DRIVER_INIT -int driver_init() -{ - bool ok = pywraps_nw_init(); - if ( !ok ) - return PLUGIN_SKIP; - pywraps_nw_notify(NW_INITIDA_SLOT); - return PLUGIN_KEEP; -} - -#define DRIVER_TERM -void driver_term() -{ - pywraps_nw_notify(NW_TERMIDA_SLOT); - pywraps_nw_term(); -} +#include "py_notifywhen.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_notify_when(PyObject *self, PyObject *args) +{ + int when; + PyObject *py_callable; + if ( !PyArg_ParseTuple(args, "IO", &when, &py_callable) ) + return NULL; + return Py_BuildValue("i", notify_when(when, py_callable)); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_nw[] = +{ + {"notify_when", ex_notify_when, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(nw); + +#define DRIVER_INIT +int driver_init() +{ + bool ok = pywraps_nw_init(); + if ( !ok ) + return PLUGIN_SKIP; + pywraps_nw_notify(NW_INITIDA_SLOT); + return PLUGIN_KEEP; +} + +#define DRIVER_TERM +void driver_term() +{ + pywraps_nw_notify(NW_TERMIDA_SLOT); + pywraps_nw_term(); +} diff --git a/pywraps/link_gen.py b/pywraps/link_gen.py index fad012a6..1ff7608a 100644 --- a/pywraps/link_gen.py +++ b/pywraps/link_gen.py @@ -1,336 +1,336 @@ -import types - -C_TO_PY_CAST = { - 'b' : 'char', - 'i' : 'int', - 'H' : 'uint16', - 'h' : 'int16', - 'B' : 'uchar', -} -# -------------------------------------------------------------------------------------------- -class gen_fmt(object): - def __init__(self, fields, tp = None, bv = None, cast=None, cmt = None): - self.fields = fields - self.tp = tp - # Format to be passed to Py_BuildValue - if not bv: - self.bv = "XXX" - else: - if bv == "K": - self.bv = "PY_FMT64" - else: - self.bv = '"%s"' % bv - - if not cast: - if bv == "K": - cast = "pyul_t" - elif bv in C_TO_PY_CAST: - cast = C_TO_PY_CAST[bv] - - self.cast = "" if not cast else "(%s)" % cast - self.cmt = cmt - if bv == "K": - self.setcvt = "uint64 v(0); PyGetNumber(value, &v);" - elif bv == 'i': - self.setcvt = "int v = PyInt_AsLong(value);" - else: - self.setcvt = "uint64 v = %sPyInt_AsLong(value);" % self.cast - - -# -------------------------------------------------------------------------------------------- -switch_info_ex_t_gen = [ - gen_fmt('regdtyp', bv = 'b', cmt = 'size of the switch expression register as dtyp'), - gen_fmt('flags2', bv = 'i'), - gen_fmt('jcases', bv = 'i', cmt = 'number of entries in the jump table (SWI2_INDIRECT)'), - gen_fmt('regnum', bv = 'i', cmt = 'the switch expression as a register number'), - gen_fmt('flags', bv = 'H', cmt = 'the switch expression as a register number'), - gen_fmt('ncases', bv = 'H', cmt = 'number of cases (excluding default)'), - gen_fmt('defjump', bv = 'K', cmt = 'default jump address'), - gen_fmt('jumps', bv = 'K', cmt = 'jump table address'), - gen_fmt('elbase', bv = 'K', cmt = 'element base'), - gen_fmt('startea', bv = 'K', cmt = 'start of switch idiom'), - gen_fmt('custom', bv = 'K', cmt = 'information for custom tables (filled and used by modules)'), - gen_fmt('ind_lowcase', bv = 'K'), - gen_fmt(['values', 'lowcase'], bv = 'K'), -] - -op_t_gen = [ - gen_fmt('n', bv = 'b'), - gen_fmt('type', bv = 'B'), - gen_fmt('offb', bv = 'b'), - gen_fmt('offo', bv = 'b'), - gen_fmt('flags', bv = 'B'), - gen_fmt('dtyp', bv = 'b'), - gen_fmt(['reg', 'phrase'], bv = 'H'), - gen_fmt('value', bv = 'K'), - gen_fmt('addr', bv = 'K'), - gen_fmt('specval', bv = 'K'), - gen_fmt('specflag1', bv = 'b'), - gen_fmt('specflag2', bv = 'b'), - gen_fmt('specflag3', bv = 'b'), - gen_fmt('specflag4', bv = 'b') -] - -insn_t_gen = [ - gen_fmt('cs', bv = 'K'), - gen_fmt('ip', bv = 'K'), - gen_fmt('ea', bv = 'K'), - gen_fmt('itype', bv = 'H'), - gen_fmt('size', bv = 'H'), - gen_fmt('auxpref', bv = 'H'), - gen_fmt('segpref', bv = 'b'), - gen_fmt('insnpref', bv = 'b'), - gen_fmt('Op1', tp = 'op_t'), - gen_fmt('Op2', tp = 'op_t'), - gen_fmt('Op3', tp = 'op_t'), - gen_fmt('Op4', tp = 'op_t'), - gen_fmt('Op5', tp = 'op_t'), - gen_fmt('Op6', tp = 'op_t'), - gen_fmt('flags', bv = 'b') -] - -regval_t_gen = [ - gen_fmt('rvtype', bv = 'i'), - gen_fmt('ival', bv = 'K'), - gen_fmt('fval', bv = 'd'), - gen_fmt('bytes', bv = 's'), -] - -# -------------------------------------------------------------------------------------------- -S_LINK_ATTR = 'S_CLINK_NAME' # If the name is a literal, make sure you specify double quotations -S_CMOD_NAME = '_idaapi' - -# -------------------------------------------------------------------------------------------- -def gen_stub(gen, name, cname = None, tabs=4, gen_py_file = False, gen_c_file = False): - # Assume C type name same as python type name - if not cname: - cname = name - - # Python property lines - prop_body = [] - - # Python get/set bodies - getset_body = [] - - # C get/set bodies - cgetset_body = [] - - # some spacing constants - spc = ' ' * tabs - spc2 = spc * 2 - nspc = '\n' + spc - nspc2 = '\n' + spc2 - - cget_link = '%s_get_clink' % cname - - # - # Process fields - # - for g in gen: - # a union will be represented by a list - if type(g.fields) != types.ListType: - fields = [g.fields] - else: - fields = g.fields - - # join all field names (in case of a union) - flds_name = '_'.join(fields) - - # form the method and variable names - set_method = '__set_%s__' % flds_name - get_method = '__get_%s__' % flds_name - cset_method = '%s_set_%s' % (name, flds_name) - cget_method = '%s_get_%s' % (name, flds_name) - fld_name = '__%s__' % flds_name - - basic_type = not g.tp - - vars = { - 'get': get_method, - 'set': set_method, - 'l': S_LINK_ATTR, - 'fld' : fld_name, - 'cmod' : S_CMOD_NAME, - 'cget': cget_method, - 'cset': cset_method, - 'csetcvt': g.setcvt, - 'cname': cname, - 'cgetlink': cget_link, - 'cfield1': fields[0], - 'bv': g.bv, - 'bvcast': g.cast - } - - # - # Python code - # - - # basic type? - # For basic types we need to create property and get/set methods - if basic_type: - for fld in fields: - prop_body.append('%s = property(%s, %s)' % (fld, get_method, set_method)) - if g.cmt: - prop_body.append('"""%s"""' % g.cmt) - - # - code = '\n'.join([ - # get method - 'def %(get)s(self):', - spc2 + 'return %(cmod)s.%(cget)s(self)', - # set method - spc + 'def %(set)s(self, v):', - spc2 + '%(cmod)s.%(cset)s(self, v)', - ]) % vars - - getset_body.append(code) - - # - # C code - # - if basic_type: - code = '\n'.join([ -"""static PyObject *%(cget)s(PyObject *self) -{ - %(cname)s *link = %(cgetlink)s(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(%(bv)s, %(bvcast)slink->%(cfield1)s); -} -static void %(cset)s(PyObject *self, PyObject *value) -{ - %(cname)s *link = %(cgetlink)s(self); - if ( link == NULL ) - return; - %(csetcvt)s - link->%(cfield1)s = %(bvcast)sv; -} - -""" - ]) % vars - - cgetset_body.append(code) - -# print 'prop_body->\n\t', '\n\t'.join(prop_body), '\n<' -# print 'getset_body->\n', '\n'.join(getset_body), '\n<' -# print 'cgetset_body->\n', '\n'.join(cgetset_body), '\n<' - - vars = { - 'name': name, - 'cname': cname, - 'getlink': cget_link, - 'l': S_LINK_ATTR, - 'cmod' : S_CMOD_NAME - } - - # - # Form the complete Python code - # - py = '\n'.join([ - 'class %(name)s(py_clinked_object_t):', - - # init() code - spc + 'def __init__(self, lnk = None):', - spc2 + 'py_clinked_object_t.__init__(self, lnk)', - '', - spc + 'def _create_clink(self):', - spc2 + 'return _idaapi.%(name)s_create()', - '', - spc + 'def _del_clink(self, lnk):', - spc2 + 'return _idaapi.%(name)s_destroy(lnk)', - '', - spc + 'def assign(self, other):', - spc2 + 'return _idaapi.%(name)s_assign(self, other)', - '', - '', - spc + '#', - spc + '# Autogenerated', - spc + '#', - # get/set code - spc + nspc.join(getset_body), - # props code - spc + nspc.join(prop_body), - ]) % vars - - # - # Form the Python to C conversion function - # - - # - # Form the complete C code - # - ccode = '\n'.join([ - # Form the C get link code -"""%(cname)s *%(getlink)s(PyObject *self) -{ - if ( !PyObject_HasAttrString(self, %(l)s) ) - return NULL; - %(cname)s *r; - PyObject *attr = PyObject_GetAttrString(self, %(l)s); - if ( PyCObject_Check(attr) ) - r = (%(cname)s *) PyCObject_AsVoidPtr(attr); - else - r = NULL; - Py_DECREF(attr); - return r; -} - -static PyObject *%(cname)s_create() -{ - %(cname)s *inst = new %(cname)s(); - return PyCObject_FromVoidPtr(inst, NULL); -} - -static bool %(cname)s_destroy(PyObject *py_obj) -{ - if ( !PyCObject_Check(py_obj) ) - return false; - %(cname)s *inst = (%(cname)s *) PyCObject_AsVoidPtr(py_obj); - delete inst; - return true; -} - -static bool %(cname)s_assign(PyObject *self, PyObject *other) -{ - %(cname)s *lhs = %(cname)s_get_clink(self); - %(cname)s *rhs = %(cname)s_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -// Auto generated - begin -// -""", - # Form C get/set functions - ''.join(cgetset_body), -"""// -// Auto generated - end -// -//-------------------------------------------------------------------------""" - ]) % vars - - # write the Python file - if gen_py_file: - f = open(name + '.py', 'w') - f.write(py) - f.close() - - # write C file - if gen_c_file: - f = open(name + '.cpp', 'w') - f.write(ccode) - f.close() - -# -------------------------------------------------------------------------------------------- -def main(): - files = [ - ('switch_info_ex_t', switch_info_ex_t_gen), - ] - for (n, g) in files: - gen_stub(g, n, gen_py_file = True, gen_c_file = True) - +import types + +C_TO_PY_CAST = { + 'b' : 'char', + 'i' : 'int', + 'H' : 'uint16', + 'h' : 'int16', + 'B' : 'uchar', +} +# -------------------------------------------------------------------------------------------- +class gen_fmt(object): + def __init__(self, fields, tp = None, bv = None, cast=None, cmt = None): + self.fields = fields + self.tp = tp + # Format to be passed to Py_BuildValue + if not bv: + self.bv = "XXX" + else: + if bv == "K": + self.bv = "PY_FMT64" + else: + self.bv = '"%s"' % bv + + if not cast: + if bv == "K": + cast = "pyul_t" + elif bv in C_TO_PY_CAST: + cast = C_TO_PY_CAST[bv] + + self.cast = "" if not cast else "(%s)" % cast + self.cmt = cmt + if bv == "K": + self.setcvt = "uint64 v(0); PyGetNumber(value, &v);" + elif bv == 'i': + self.setcvt = "int v = PyInt_AsLong(value);" + else: + self.setcvt = "uint64 v = %sPyInt_AsLong(value);" % self.cast + + +# -------------------------------------------------------------------------------------------- +switch_info_ex_t_gen = [ + gen_fmt('regdtyp', bv = 'b', cmt = 'size of the switch expression register as dtyp'), + gen_fmt('flags2', bv = 'i'), + gen_fmt('jcases', bv = 'i', cmt = 'number of entries in the jump table (SWI2_INDIRECT)'), + gen_fmt('regnum', bv = 'i', cmt = 'the switch expression as a register number'), + gen_fmt('flags', bv = 'H', cmt = 'the switch expression as a register number'), + gen_fmt('ncases', bv = 'H', cmt = 'number of cases (excluding default)'), + gen_fmt('defjump', bv = 'K', cmt = 'default jump address'), + gen_fmt('jumps', bv = 'K', cmt = 'jump table address'), + gen_fmt('elbase', bv = 'K', cmt = 'element base'), + gen_fmt('startea', bv = 'K', cmt = 'start of switch idiom'), + gen_fmt('custom', bv = 'K', cmt = 'information for custom tables (filled and used by modules)'), + gen_fmt('ind_lowcase', bv = 'K'), + gen_fmt(['values', 'lowcase'], bv = 'K'), +] + +op_t_gen = [ + gen_fmt('n', bv = 'b'), + gen_fmt('type', bv = 'B'), + gen_fmt('offb', bv = 'b'), + gen_fmt('offo', bv = 'b'), + gen_fmt('flags', bv = 'B'), + gen_fmt('dtyp', bv = 'b'), + gen_fmt(['reg', 'phrase'], bv = 'H'), + gen_fmt('value', bv = 'K'), + gen_fmt('addr', bv = 'K'), + gen_fmt('specval', bv = 'K'), + gen_fmt('specflag1', bv = 'b'), + gen_fmt('specflag2', bv = 'b'), + gen_fmt('specflag3', bv = 'b'), + gen_fmt('specflag4', bv = 'b') +] + +insn_t_gen = [ + gen_fmt('cs', bv = 'K'), + gen_fmt('ip', bv = 'K'), + gen_fmt('ea', bv = 'K'), + gen_fmt('itype', bv = 'H'), + gen_fmt('size', bv = 'H'), + gen_fmt('auxpref', bv = 'H'), + gen_fmt('segpref', bv = 'b'), + gen_fmt('insnpref', bv = 'b'), + gen_fmt('Op1', tp = 'op_t'), + gen_fmt('Op2', tp = 'op_t'), + gen_fmt('Op3', tp = 'op_t'), + gen_fmt('Op4', tp = 'op_t'), + gen_fmt('Op5', tp = 'op_t'), + gen_fmt('Op6', tp = 'op_t'), + gen_fmt('flags', bv = 'b') +] + +regval_t_gen = [ + gen_fmt('rvtype', bv = 'i'), + gen_fmt('ival', bv = 'K'), + gen_fmt('fval', bv = 'd'), + gen_fmt('bytes', bv = 's'), +] + +# -------------------------------------------------------------------------------------------- +S_LINK_ATTR = 'S_CLINK_NAME' # If the name is a literal, make sure you specify double quotations +S_CMOD_NAME = '_idaapi' + +# -------------------------------------------------------------------------------------------- +def gen_stub(gen, name, cname = None, tabs=4, gen_py_file = False, gen_c_file = False): + # Assume C type name same as python type name + if not cname: + cname = name + + # Python property lines + prop_body = [] + + # Python get/set bodies + getset_body = [] + + # C get/set bodies + cgetset_body = [] + + # some spacing constants + spc = ' ' * tabs + spc2 = spc * 2 + nspc = '\n' + spc + nspc2 = '\n' + spc2 + + cget_link = '%s_get_clink' % cname + + # + # Process fields + # + for g in gen: + # a union will be represented by a list + if type(g.fields) != types.ListType: + fields = [g.fields] + else: + fields = g.fields + + # join all field names (in case of a union) + flds_name = '_'.join(fields) + + # form the method and variable names + set_method = '__set_%s__' % flds_name + get_method = '__get_%s__' % flds_name + cset_method = '%s_set_%s' % (name, flds_name) + cget_method = '%s_get_%s' % (name, flds_name) + fld_name = '__%s__' % flds_name + + basic_type = not g.tp + + vars = { + 'get': get_method, + 'set': set_method, + 'l': S_LINK_ATTR, + 'fld' : fld_name, + 'cmod' : S_CMOD_NAME, + 'cget': cget_method, + 'cset': cset_method, + 'csetcvt': g.setcvt, + 'cname': cname, + 'cgetlink': cget_link, + 'cfield1': fields[0], + 'bv': g.bv, + 'bvcast': g.cast + } + + # + # Python code + # + + # basic type? + # For basic types we need to create property and get/set methods + if basic_type: + for fld in fields: + prop_body.append('%s = property(%s, %s)' % (fld, get_method, set_method)) + if g.cmt: + prop_body.append('"""%s"""' % g.cmt) + + # + code = '\n'.join([ + # get method + 'def %(get)s(self):', + spc2 + 'return %(cmod)s.%(cget)s(self)', + # set method + spc + 'def %(set)s(self, v):', + spc2 + '%(cmod)s.%(cset)s(self, v)', + ]) % vars + + getset_body.append(code) + + # + # C code + # + if basic_type: + code = '\n'.join([ +"""static PyObject *%(cget)s(PyObject *self) +{ + %(cname)s *link = %(cgetlink)s(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(%(bv)s, %(bvcast)slink->%(cfield1)s); +} +static void %(cset)s(PyObject *self, PyObject *value) +{ + %(cname)s *link = %(cgetlink)s(self); + if ( link == NULL ) + return; + %(csetcvt)s + link->%(cfield1)s = %(bvcast)sv; +} + +""" + ]) % vars + + cgetset_body.append(code) + +# print 'prop_body->\n\t', '\n\t'.join(prop_body), '\n<' +# print 'getset_body->\n', '\n'.join(getset_body), '\n<' +# print 'cgetset_body->\n', '\n'.join(cgetset_body), '\n<' + + vars = { + 'name': name, + 'cname': cname, + 'getlink': cget_link, + 'l': S_LINK_ATTR, + 'cmod' : S_CMOD_NAME + } + + # + # Form the complete Python code + # + py = '\n'.join([ + 'class %(name)s(py_clinked_object_t):', + + # init() code + spc + 'def __init__(self, lnk = None):', + spc2 + 'py_clinked_object_t.__init__(self, lnk)', + '', + spc + 'def _create_clink(self):', + spc2 + 'return _idaapi.%(name)s_create()', + '', + spc + 'def _del_clink(self, lnk):', + spc2 + 'return _idaapi.%(name)s_destroy(lnk)', + '', + spc + 'def assign(self, other):', + spc2 + 'return _idaapi.%(name)s_assign(self, other)', + '', + '', + spc + '#', + spc + '# Autogenerated', + spc + '#', + # get/set code + spc + nspc.join(getset_body), + # props code + spc + nspc.join(prop_body), + ]) % vars + + # + # Form the Python to C conversion function + # + + # + # Form the complete C code + # + ccode = '\n'.join([ + # Form the C get link code +"""%(cname)s *%(getlink)s(PyObject *self) +{ + if ( !PyObject_HasAttrString(self, %(l)s) ) + return NULL; + %(cname)s *r; + PyObject *attr = PyObject_GetAttrString(self, %(l)s); + if ( PyCObject_Check(attr) ) + r = (%(cname)s *) PyCObject_AsVoidPtr(attr); + else + r = NULL; + Py_DECREF(attr); + return r; +} + +static PyObject *%(cname)s_create() +{ + %(cname)s *inst = new %(cname)s(); + return PyCObject_FromVoidPtr(inst, NULL); +} + +static bool %(cname)s_destroy(PyObject *py_obj) +{ + if ( !PyCObject_Check(py_obj) ) + return false; + %(cname)s *inst = (%(cname)s *) PyCObject_AsVoidPtr(py_obj); + delete inst; + return true; +} + +static bool %(cname)s_assign(PyObject *self, PyObject *other) +{ + %(cname)s *lhs = %(cname)s_get_clink(self); + %(cname)s *rhs = %(cname)s_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +// Auto generated - begin +// +""", + # Form C get/set functions + ''.join(cgetset_body), +"""// +// Auto generated - end +// +//-------------------------------------------------------------------------""" + ]) % vars + + # write the Python file + if gen_py_file: + f = open(name + '.py', 'w') + f.write(py) + f.close() + + # write C file + if gen_c_file: + f = open(name + '.cpp', 'w') + f.write(ccode) + f.close() + +# -------------------------------------------------------------------------------------------- +def main(): + files = [ + ('switch_info_ex_t', switch_info_ex_t_gen), + ] + for (n, g) in files: + gen_stub(g, n, gen_py_file = True, gen_c_file = True) + main() \ No newline at end of file diff --git a/pywraps/py_appcall.py b/pywraps/py_appcall.py index 20a37bd9..9750a27a 100644 --- a/pywraps/py_appcall.py +++ b/pywraps/py_appcall.py @@ -1,1031 +1,1031 @@ -try: - import pywraps - pywraps_there = True -except: - pywraps_there = False -import _idaapi -import random -import operator -import datetime - -if pywraps_there: - _idaapi.appcall = pywraps.appcall - from py_idaapi import * -else: - import idaapi - from idaapi import * - -# ---------------------------------------------------------------------------------------------------------------------------------------------- -# -import types - -# ----------------------------------------------------------------------- -class Appcall_array__(object): - """This class is used with Appcall.array() method""" - def __init__(self, tp): - self.__type = tp - - def pack(self, L): - """Packs a list or tuple into a byref buffer""" - t = type(L) - if not (t == types.ListType or t == types.TupleType): - raise ValueError, "Either a list or a tuple must be passed" - self.__size = len(L) - if self.__size == 1: - self.__typedobj = Appcall__.typedobj(self.__type + ";") - else: - self.__typedobj = Appcall__.typedobj("%s x[%d];" % (self.__type, self.__size)) - # Now store the object in a string buffer - ok, buf = self.__typedobj.store(L) - if ok: - return Appcall__.byref(buf) - else: - return None - - def try_to_convert_to_list(self, obj): - """Is this object a list? We check for the existance of attribute zero and attribute self.size-1""" - if not (hasattr(obj, "0") and hasattr(obj, str(self.__size-1))): - return obj - # at this point, we are sure we have an "idc list" - # let us convert to a Python list - return [getattr(obj, str(x)) for x in xrange(0, self.__size)] - - def unpack(self, buf, as_list=True): - """Unpacks an array back into a list or an object""" - # take the value from the special ref object - if isinstance(buf, PyIdc_cvt_refclass__): - buf = buf.value - - # we can only unpack from strings - if type(buf) != types.StringType: - raise ValueError, "Cannot unpack this type!" - # now unpack - ok, obj = self.__typedobj.retrieve(buf) - if not ok: - raise ValueError, "Failed while unpacking!" - if not as_list: - return obj - return self.try_to_convert_to_list(obj) - - -# ----------------------------------------------------------------------- -# Wrapper class for the appcall() -class Appcall_callable__(object): - """ - Helper class to issue appcalls using a natural syntax: - appcall.FunctionNameInTheDatabase(arguments, ....) - or - appcall["Function@8"](arguments, ...) - or - f8 = appcall["Function@8"] - f8(arg1, arg2, ...) - or - o = appcall.obj() - i = byref(5) - appcall.funcname(arg1, i, "hello", o) - """ - def __init__(self, ea, tp = None, fld = None): - """Initializes an appcall with a given function ea""" - self.__ea = ea - self.__type = tp - self.__fields = fld - self.__options = None # Appcall options - self.__timeout = None # Appcall timeout - - def __get_timeout(self): - return self.__timeout - - def __set_timeout(self, v): - self.__timeout = v - - timeout = property(__get_timeout, __set_timeout) - """An Appcall instance can change its timeout value with this attribute""" - - def __get_options(self): - return self.__options if self.__options != None else Appcall__.get_appcall_options() - - def __set_options(self, v): - if self.timeout: - # If timeout value is set, then put the timeout flag and encode the timeout value - v |= Appcall__.APPCALL_TIMEOUT | (self.timeout << 16) - else: - # Timeout is not set, then clear the timeout flag - v &= ~Appcall__.APPCALL_TIMEOUT - - self.__options = v - - options = property(__get_options, __set_options) - """Sets the Appcall options locally to this Appcall instance""" - - def __call__(self, *args): - """Make object callable. We redirect execution to idaapi.appcall()""" - if self.ea is None: - raise ValueError, "Object not callable!" - - # convert arguments to a list - arg_list = list(args) - - # Save appcall options and set new global options - old_opt = Appcall__.get_appcall_options() - Appcall__.set_appcall_options(self.options) - - # Do the Appcall (use the wrapped version) - e_obj = None - try: - r = _idaapi.appcall( - self.ea, - _idaapi.get_current_thread(), - self.type, - self.fields, - arg_list) - except Exception as e: - e_obj = e - - # Restore appcall options - Appcall__.set_appcall_options(old_opt) - - # Return or re-raise exception - if e_obj: - raise Exception, e_obj - - return r - - def __get_ea(self): - return self.__ea - - def __set_ea(self, val): - self.__ea = val - - ea = property(__get_ea, __set_ea) - """Returns or sets the EA associated with this object""" - - def __get_size(self): - if self.__type == None: - return -1 - r = _idaapi.calc_type_size(_idaapi.cvar.idati, self.__type) - if not r: - return -1 - return r - - size = property(__get_size) - """Returns the size of the type""" - - def __get_type(self): - return self.__type - - type = property(__get_type) - """Returns the typestring""" - - def __get_fields(self): - return self.__fields - - fields = property(__get_fields) - """Returns the field names""" - - - def retrieve(self, src=None, flags=0): - """ - Unpacks a typed object from the database if an ea is given or from a string if a string was passed - @param src: the address of the object or a string - @return: Returns a tuple of boolean and object or error number (Bool, Error | Object). - """ - - # Nothing passed? Take the address and unpack from the database - if src is None: - src = self.ea - - if type(src) == types.StringType: - return _idaapi.unpack_object_from_bv(_idaapi.cvar.idati, self.type, self.fields, src, flags) - else: - return _idaapi.unpack_object_from_idb(_idaapi.cvar.idati, self.type, self.fields, src, flags) - - def store(self, obj, dest_ea=None, base_ea=0, flags=0): - """ - Packs an object into a given ea if provided or into a string if no address was passed. - @param obj: The object to pack - @param dest_ea: If packing to idb this will be the store location - @param base_ea: If packing to a buffer, this will be the base that will be used to relocate the pointers - - @return: - - If packing to a string then a Tuple(Boolean, packed_string or error code) - - If packing to the database then a return code is returned (0 is success) - """ - - # no ea passed? thus pack to a string - if dest_ea is None: - return _idaapi.pack_object_to_bv(obj, - _idaapi.cvar.idati, - self.type, - self.fields, - base_ea, - flags) - else: - return _idaapi.pack_object_to_idb(obj, - _idaapi.cvar.idati, - self.type, - self.fields, - dest_ea, - flags) - -# ----------------------------------------------------------------------- -class Appcall_consts__(object): - """Helper class used by Appcall.Consts attribute - It is used to retrieve constants via attribute access""" - def __init__(self, default=0): - self.__default = default - - def __getattr__(self, attr): - return Appcall__.valueof(attr, self.__default) - -# ----------------------------------------------------------------------- -class Appcall__(object): - APPCALL_MANUAL = 0x1 - """ - Only set up the appcall, do not run it. - you should call CleanupAppcall() when finished - """ - - APPCALL_DEBEV = 0x2 - """ - Return debug event information - If this bit is set, exceptions during appcall - will generate idc exceptions with full - information about the exception - """ - - APPCALL_TIMEOUT = 0x4 - """ - Appcall with timeout - The timeout value in milliseconds is specified - in the high 2 bytes of the 'options' argument: - If timed out, errbuf will contain "timeout". - """ - - def __init__(self): - self.__consts = Appcall_consts__() - def __get_consts(self): - return self.__consts - Consts = property(__get_consts) - """Use Appcall.Consts.CONST_NAME to access constants""" - - @staticmethod - def __name_or_ea(name_or_ea): - """ - Function that accepts a name or an ea and checks if the address is enabled. - If a name is passed then idaapi.get_name_ea() is applied to retrieve the name - @return: - - Returns the resolved EA or - - Raises an exception if the address is not enabled - """ - - # a string? try to resolve it - if type(name_or_ea) == types.StringType: - ea = _idaapi.get_name_ea(_idaapi.BADADDR, name_or_ea) - else: - ea = name_or_ea - # could not resolve name or invalid address? - if ea == _idaapi.BADADDR or not _idaapi.isEnabled(ea): - raise ValueError, "Undefined function " + name_or_ea - return ea - - @staticmethod - def proto(name_or_ea, prototype, flags = None): - """ - Allows you to instantiate an appcall (callable object) with the desired prototype - @param name_or_ea: The name of the function (will be resolved with LocByName()) - @param prototype: - @return: - - On failure it raises an exception if the prototype could not be parsed - or the address is not resolvable - - Returns a callbable Appcall instance with the given prototypes and flags - """ - - # resolve and raise exception on error - ea = Appcall__.__name_or_ea(name_or_ea) - # parse the type - if flags is None: - flags = 1 | 2 | 4 # PT_SIL | PT_NDC | PT_TYP - - result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, prototype, flags) - if result is None: - raise ValueError, "Could not parse type: " + prototype - - # Return the callable method with type info - return Appcall_callable__(ea, result[1], result[2]) - - def __getattr__(self, name_or_ea): - """Allows you to call functions as if they were member functions (by returning a callable object)""" - # resolve and raise exception on error - ea = self.__name_or_ea(name_or_ea) - if ea == _idaapi.BADADDR: - raise ValueError, "Undefined function " + name - # Return the callable method - return Appcall_callable__(ea) - - def __getitem__(self, idx): - """ - Use self[func_name] syntax if the function name contains invalid characters for an attribute name - See __getattr___ - """ - return self.__getattr__(idx) - - @staticmethod - def valueof(name, default=0): - """ - Returns the numeric value of a given name string. - If the name could not be resolved then the default value will be returned - """ - t, v = _idaapi.get_name_value(_idaapi.BADADDR, name) - if t == 0: # NT_NONE - v = default - return v - - @staticmethod - def int64(v): - """Whenever a 64bit number is needed use this method to construct an object""" - return PyIdc_cvt_int64__(v) - - @staticmethod - def byref(val): - """ - Method to create references to immutable objects - Currently we support references to int/strings - Objects need not be passed by reference (this will be done automatically) - """ - return PyIdc_cvt_refclass__(val) - - @staticmethod - def buffer(str = None, size = 0, fill="\x00"): - """ - Creates a string buffer. The returned value (r) will be a byref object. - Use r.value to get the contents and r.size to get the buffer's size - """ - if str is None: - str = "" - left = size - len(str) - if left > 0: - str = str + (fill * left) - r = Appcall__.byref(str) - r.size = size - return r - - @staticmethod - def obj(**kwds): - """Returns an empty object or objects with attributes as passed via its keywords arguments""" - return object_t(**kwds) - - @staticmethod - def cstr(val): - return as_cstr(val) - - @staticmethod - def unicode(s): - return as_unicode(s) - - @staticmethod - def array(type_name): - """Defines an array type. Later you need to pack() / unpack()""" - return Appcall_array__(type_name) - - @staticmethod - def typedobj(typestr, ea=None): - """ - Parses a type string and returns an appcall object. - One can then use retrieve() member method - @param ea: Optional parameter that later can be used to retrieve the type - @return: Appcall object or raises ValueError exception - """ - # parse the type - result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, typestr, 1 | 2 | 4) # PT_SIL | PT_NDC | PT_TYP - if result is None: - raise ValueError, "Could not parse type: " + typestr - # Return the callable method with type info - return Appcall_callable__(ea, result[1], result[2]) - - @staticmethod - def set_appcall_options(opt): - """Method to change the Appcall options globally (not per Appcall)""" - old_opt = Appcall__.get_appcall_options() - _idaapi.cvar.inf.appcall_options = opt - return old_opt - - @staticmethod - def get_appcall_options(): - """Return the global Appcall options""" - return _idaapi.cvar.inf.appcall_options - - @staticmethod - def cleanup_appcall(tid = 0): - """Equivalent to IDC's CleanupAppcall()""" - return _idaapi.cleanup_appcall(tid) - -Appcall = Appcall__() -# - -# ---------------------------------------------------------------------------------------------------------------------------------------------- - -# -a = Appcall # Take a shortcut to Appcall -c = Appcall.Consts # take shortcut to constants - -# ----------------------------------------------------------------------- -# - Adds missing types -# - ... -def init(): - # add neeeded types - if a.valueof("ERROR_FILE_NOT_FOUND") == 0: - add_type("MACRO_ERROR") - - setattr(c, "ERROR_FILE_NOT_FOUND", 2) - - if a.valueof("MEM_COMMIT") == 0: - add_type("MACRO_PAGE") - - # open log file - global test_log - try: - test_log = file("python_test.log", "a") - except: - test_log = None - -# ----------------------------------------------------------------------- -def deinit(): - global test_log - test_log.close() - test_log = None - return 1 - -# ----------------------------------------------------------------------- -def logprint(s): - if not test_log: - print s - else: - test_log.write(s + "\n") - return 1 - -# ----------------------------------------------------------------------- -def add_type(t): - print "adding type:", t - idaapi.import_type(idaapi.cvar.idati, 0, t) - -# ----------------------------------------------------------------------- -def test_init(): - hmod = getmodulehandlew(a.unicode("kernel32.dll")) - print "k32hmod=%x" % hmod - if hmod == 0: - return -1 - p = getprocaddr(hmod, "VirtualAlloc") - if p == 0: - return -2 - print "VirtualAlloc->%x" % p - virtualalloc.ea = p - - p = getprocaddr(hmod, "VirtualFree") - if p == 0: - return -3 - print "VirtualFree->%x" % p - virtualfree.ea = p - - m = virtualalloc(0, c.MEM_COMMIT, 0x1000, c.PAGE_EXECUTE_READWRITE) - idc.RefreshDebuggerMemory() - print "%x: allocated memory\n" % m - global WRITE_AREA - WRITE_AREA = m - - return 1 - -# ----------------------------------------------------------------------- -def test_deinit(): - virtualfree(WRITE_AREA, 0, c.MEM_FREE) - -# ----------------------------------------------------------------------- -# Tests changedir/setdir/buffer creation (two methods) and cstr() -def test_enum_files(): - # create a buffer - savedpath = a.byref("\x00" * 260) - # get current directory - n = getcurdir(250, savedpath) - out = [] - out.append("curdir=%s" % savedpath.value[0:n]) - - # get windir - windir = a.buffer(size=260) # create a buffer using helper function - n = getwindir(windir, windir.size) - if n == 0: - return -1 # could not get current directory - - windir = windir.value[:n] - out.append("windir=%s" % windir) - - # change to windows folder - setcurdir(windir) - - # initiate find - fd = a.obj() - h = findfirst("*.exe", fd) - if h == -1: - return -2 # no files found! - - found = -6 - while True: - fn = a.cstr(fd.cFileName) - if "regedit" in fn: - found = 1 - out.append("fn=%s<" % fn) - fd = a.obj() # reset the FD object - ok = findnext(h, fd) - if not ok: - break - # - findclose(h) - - # restore cur dir - setcurdir(savedpath.value) - - # verify - t = a.buffer(size=260) - n = getcurdir(t.size, t) - if t.cstr() != savedpath.cstr(): - return -4 # could not restore cur dir - - out.append("curdir=%s<" % t.cstr()) -# print "all done!" -# for l in out: -# print l - - return found -# ----------------------------------------------------------------------- -def test_gpa(): - h = loadlib("user32.dll") - if h == 0: - print "failed to load library!" - return -1 - p = getprocaddr(h, "FindWindowA") - if p == 0: - print "failed to gpa!" - return -2 - findwin = a.proto(p, "int FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);") - hwnd = findwin("TIdaWindow", 0) - freelib(h) - print "%x: ok!->hwnd=%x" % (p, hwnd) - - return 1 - -# ----------------------------------------------------------------------- -def test_loaddll(): - h = loadlib("twain_32.dll") - if h == 0: - print "failed to load twain32 library!" - return -1 - return 1 - -# ----------------------------------------------------------------------- -# Packs a simple structure (into the database) and unpacks it back using the idaapi methods -def test_pck_idb_raw(): - name, tp, flds = idc.ParseType("struct { int a, b; char x[5];};", 0) - o = a.obj(a=15, b=17,x="hi") - idaapi.pack_object_to_idb(o, idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) - - ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) - if obj.a != 15 and obj.b != 17 and obj != "hi": - return -2 - return 1 - -# ----------------------------------------------------------------------- -# Packs a simple structure (into a string) and unpacks it back using the idaapi methods -def test_pck_bv_raw(): - name, tp, flds = idc.ParseType("struct { int a; char x[5]; int b;};", 0) - va,vb,vx = 15,17,"hi" - o = a.obj(a=va, b=vb,x=vx) - ok, s = idaapi.pack_object_to_bv(o, idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) - if not ok: - return -1 - ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) - if obj.a != va and obj.b != vb and obj.x != vx: - return -2 - return 1 - -# ----------------------------------------------------------------------- -# 1. Unpacks the DOS header at 0x400000 and verify the fields -# 2. Unpacks a string and see if it is unpacked correctly -def test_unpack_raw(): - name, tp, flds = idc.ParseType("IMAGE_DOS_HEADER;", 0) - ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, 0x400000, 0) - if obj.e_magic != 23117 and obj.e_cblp != 144: - return -1 - - name, tp, flds = idc.ParseType("struct abc_t { int a, b;};", 0) - ok, obj = idaapi.unpack_object_from_bv(idaapi.cvar.idati, tp, flds, "\x01\x00\x00\x00\x02\x00\x00\x00", 0) - if obj.a != 1 and obj.b != 2: - return -2 - - return 1 - -# ----------------------------------------------------------------------- -# Packs/Unpacks a structure to the database using appcall facilities -def test_pck_idb(): - print "%x: ..." % WRITE_AREA - tp = a.typedobj("struct { int a, b; char x[5];};") - o = a.obj(a=16, b=17,x="zzzhi") - if tp.store(o, WRITE_AREA) != 0: - return -1 - idc.RefreshDebuggerMemory() - - ok, r = tp.retrieve(WRITE_AREA) - if not ok: - return -2 - if r.a != o.a and r.b != o.b and r.x != o.x: - return -3 - - return 1 - -# ----------------------------------------------------------------------- -# Packs/Unpacks a structure to/from a string -def test_pck_bv(): - tp = a.typedobj("struct { int a, b; char x[5];};") - o = a.obj(a=16, b=17,x="zzzhi") - ok, packed = tp.store(o) - if not ok: - return -1 - print "packed->", repr(packed) - ok, r = tp.retrieve(packed) - if not ok: - return -2 - - if r.a != o.a and r.b != o.b and r.x != o.x: - return -3 - - return 1 - -# ----------------------------------------------------------------------- -# Test work with local types -def test_local_types(): - (type, fields) = GetLocalTinfo(1) - if not type: - return -1 - - decl = GetLocalType(1, PRTYPE_MULTI) - if decl != "enum\n"\ - + "{\n"\ - + " FEATURE_OBJECT_CACHING = 0x0,\n"\ - + " FEATURE_ZONE_ELEVATION = 0x1,\n"\ - + " FEATURE_MIME_HANDLING = 0x2,\n"\ - + " FEATURE_MIME_SNIFFING = 0x3,\n"\ - + " FEATURE_WINDOW_RESTRICTIONS = 0x4,\n"\ - + " FEATURE_WEBOC_POPUPMANAGEMENT = 0x5,\n"\ - + " FEATURE_BEHAVIORS = 0x6,\n"\ - + " FEATURE_DISABLE_MK_PROTOCOL = 0x7,\n"\ - + " FEATURE_LOCALMACHINE_LOCKDOWN = 0x8,\n"\ - + " FEATURE_SECURITYBAND = 0x9,\n"\ - + " FEATURE_RESTRICT_ACTIVEXINSTALL = 0xA,\n"\ - + " FEATURE_VALIDATE_NAVIGATE_URL = 0xB,\n"\ - + " FEATURE_RESTRICT_FILEDOWNLOAD = 0xC,\n"\ - + " FEATURE_ADDON_MANAGEMENT = 0xD,\n"\ - + " FEATURE_PROTOCOL_LOCKDOWN = 0xE,\n"\ - + " FEATURE_HTTP_USERNAME_PASSWORD_DISABLE = 0xF,\n"\ - + " FEATURE_SAFE_BINDTOOBJECT = 0x10,\n"\ - + " FEATURE_UNC_SAVEDFILECHECK = 0x11,\n"\ - + " FEATURE_GET_URL_DOM_FILEPATH_UNENCODED = 0x12,\n"\ - + " FEATURE_TABBED_BROWSING = 0x13,\n"\ - + " FEATURE_SSLUX = 0x14,\n"\ - + " FEATURE_DISABLE_NAVIGATION_SOUNDS = 0x15,\n"\ - + " FEATURE_DISABLE_LEGACY_COMPRESSION = 0x16,\n"\ - + " FEATURE_FORCE_ADDR_AND_STATUS = 0x17,\n"\ - + " FEATURE_XMLHTTP = 0x18,\n"\ - + " FEATURE_DISABLE_TELNET_PROTOCOL = 0x19,\n"\ - + " FEATURE_FEEDS = 0x1A,\n"\ - + " FEATURE_BLOCK_INPUT_PROMPTS = 0x1B,\n"\ - + " FEATURE_ENTRY_COUNT = 0x1C,\n"\ - + "} _tagINTERNETFEATURELIST\n": - print "decl = " + decl - return -2 - - return 1 - -# ----------------------------------------------------------------------- -# various tests -def test1(stage): - # call a method that takes a string buffer and appends a dot to its end - if stage == st_ref2: - buf = a.buffer("test", 100) - vals = [378, 424, 470] - for i in xrange(0, 2+1): - n = a._ref2(buf) - if buf.value[4+i] != '.': - return -st_ref2 - if vals[i] != n: - return -stage - # call a method that takes an integer reference - elif stage == st_ref1: - v = 5 - i = a.byref(v) - a._ref1(i) - if v + 1 != i.value: - return -stage - # call a method that takes an array of integers - elif stage == st_ref3: - # create an array type - arr = a.array("int") - # create a list - L = [x for x in xrange(1, 10)] - # pack the list - p_list = arr.pack(L) - # appcall to compute the total - c_total = a._ref3(p_list, len(L)) - # internally compute the total - total = reduce(operator.add, L) - if total != c_total: - return -stage - # subst() - elif stage == st_subst: - v = a._subst(5, 1) - if v != 4: - return -stage - v = subst(5, 1) # subst() / pascal - if v != -4: - return -stage*2 - elif stage == st_make2: - x = a._str2_make(5) - s = a.cstr(x.next.str) -# print "len=%d;<%s>" % (len(s), s) - if s != "This is string 2": - return -stage - n = a._str2_print(x) - if n != 5: - return -st_make2*2 - elif stage == st_make1: - x = a._str1_make(6) - if x.val != 1 and x.next.val != 2: - return -st_make1 - n = a._str1_print(x) - if n != 6: - return -stage - # 64bit test - elif stage == st_make3: - global gr - try: - x = a._str3_make(6) - gr = x - except Exception as e: - print "Exception: ", str(e) - return -stage - print "x.val32=", x.val32 - if (x.val32 != 1) and (x.val64 != (1 << 32)): - return -stage * 2 - va = 0 - vb = 0 - r = x - i = 0 - while x != 0: - i += 1 - print "1" - va += x.val32 - print "2" - vb += x.val64.value - print "3" - x = x.next - print "i=", i, "a=", va, "b=", vb - if va != 21 and vb != 90194313216: - return -stage*3 - elif stage == st_asm: - n = asm1(5, 1) - if n != 4: - return -stage - n = asm2(5, 1, 1) - if n != 7: - return -stage*2 - elif stage == st_byvalref1: - v1 = a.obj(val=5, next=0) - v2 = a.obj(str="Hello", next=0) - n = a._byvalref1(v1, 1, v2) - if n != 78: - return -stage - # v1 is passed by ref, thus it will be changed - if n + 1 != v1.val: - return -stage * 2 - elif stage == st_altsum: - # 1 + 2 - 3 + 5 = 5 - n = altsum(1, 2, 3, 5, 0) - if n != 5: - return -stage; - elif stage == st_op64: - # invalid opcode, should return -1 - r = a._op_two64(1, 2, 6).value - print "r=", r - if r != -1: - return -stage - r = a._op_two64(6, a.int64(2), 3).value - if r != 12: - return -stage * 2 - return 1 - elif stage == st_byval3: - o = a.obj(val=6,next=a.obj(val=-1, next=0)) -# print "before: o.val=%d o.next.val=%d" % (o.val, o.next.val) - n = a._byval3(o) - if n != 5: - return -stage -# print "after: o.val=%d o.next.val=%d, n=%d" % (o.val, o.next.val, n) - #--------- - elif stage == st_ex: - def doit(): - try: - # causes a DebugBreak() - a._ex2(1) - except Exception as e: - if not "Software breakpoint" in e.message: - return -stage - try: - a._ex1(1, 2) - return -st_ex * 2 - except Exception as e: - if not "referenced memory" in e.message: - return -stage - return 1 - old = a.set_appcall_options(0) - r = doit() - a.set_appcall_options(old) - if r <= 0: - return r - #--------- - elif stage == st_ref4: - i = a.int64(5) - v = a.byref(i) - if a._ref4(v) != 1: - return -st_ref4 - # test (in case recycling failed) - if v.value.value != 6: - return -st_ref4 * 2 - # test recycling - if i.value != 6: - return -st_ref4 * 3 - # return success - return 1 - -# ----------------------------------------------------------------------- -def test2(): - fn = "pc_win32_appcall.pe" - - # Try to open an non-existing file - h = a._file_open(fn + ".1") - if h != 0: - return -1 - - n = getlasterror() - print "gle=", n - if n != c.ERROR_FILE_NOT_FOUND: - return -2 - - # Should succeed - h = a._file_open(fn) - if h == 0: - return -3 - - s = a.buffer("", 10, "!") - n = a._file_read(h, s, 2) -# print "read=%d; buf=%s" % (n, repr(s.value)) - if s.value[:2] != "MZ": - return -4 - - n = a._file_close(h) - if n != h-1: - return -5 - - return 1 - -# ----------------------------------------------------------------------- -# This test changes the appcall options and sees if the appcall -# generates an exception and if we get it properly -# An appcall can throw an exception: -# - ValueError: conversion from/to idc/py failed -# - OSError: an OSError when APPCALL_DEBEV in Appcall_Options. In that case check the exc.args[0] to get the debug event -# - Exception: in all other cases -def test_exec_throw(): - old = a.set_appcall_options(0) - print "old_opt=", old, "new=0" - try: - # causes a divide by zero exception (will be reported as an Exception) - print a._op_two64(2,0,4).value - return -1 - except Exception as e: - print "runtime error!" - - # now appcall exceptions will be reported as OSErr and other exceptions as Exception - print "old_opt=", a.set_appcall_options(a.APPCALL_DEBEV), "new=2" - try: - # causes an error: Wrong number of arguments - print a._op_two64(2).value - return -2 - except OSError, e: - return -3 - except Exception as e: - print "Got other exception:", e # good - - try: - # causes an OS error and "e" will contain the last debug_event - # in our case exception, and the code is int_divide_zero = 0xC0000094 - print a._op_two64(2, 0, 4).value - return -4 - except OSError, e: - if idaapi.as_uint32(e.args[0].code) != 0xC0000094: - return -5 - except Exception as e: - print "Got other exception:", e - return -6 - a.set_appcall_options(old) - return 1 -# ----------------------------------------------------------------------- -# all the tests that take zero parameters -tests0 = (test_gpa, test_pck_idb_raw, test_pck_bv_raw, - test_unpack_raw, test_pck_idb, test_pck_bv, test_local_types, - test_enum_files, test2, test_exec_throw, test_loaddll) -test_log = None # test log file - -# ----------------------------------------------------------------------- -def test_all(): - if test_init() <= 0: - print "test_init() failed!" - return -1 - - # tests 0 - for t in tests0: - print "testing->", t - r = t() - if r <= 0: - return r - # test 1 - for i in xrange(1, st_last): - print "test1 #", i - r = test1(i) - if r <= 0: - return r - - logprint(datetime.date.today().strftime("Python was here: %Y-%m-%d @ %I:%M:%S%p")) - - test_deinit() - - return 1 - -# ----------------------------------------------------------------------- -init() - -# reference to an integer. use i.value to dereference -i = a.byref(5) - -# object representing the str1_t type -o = a.obj(val=5,next=a.obj(val=-2, next=0)) - -# dictionary representing the str1_t type -# (dictionaries will be converted into IDC objects) -d = {'val':5, 'next': {'val':-2, 'next':0} } - -# initialize some pointers -findfirst = a["__imp__FindFirstFileA@8"] -findnext = a["__imp__FindNextFileA@8"] -findclose = a["__imp__FindClose@4"] -getlasterror = a["__imp__GetLastError@0"] -setcurdir = a["__imp__SetCurrentDirectoryA@4"] -beep = a["__imp__Beep@8"] -getwindir = a["__imp__GetWindowsDirectoryA@8"] -getprocaddr = a.proto("__imp__GetProcAddress@8", "int (__stdcall *GetProcAddress)(int hModule, LPCSTR lpProcName);") -getcurdir = a["__imp__GetCurrentDirectoryA@8"] -loadlib = a.proto("__imp__LoadLibraryA@4", "int (__stdcall *LoadLibraryA)(const char *lpLibFileName);") -freelib = a.proto("__imp__FreeLibrary@4", "int (__stdcall *FreeLibrary)(int hLibModule);") -setlasterror = a.typedobj("void __stdcall SetLastError(int dwErrCode);") -getmodulehandlew = a.proto("__imp__GetModuleHandleW@4", "int (__stdcall *GetModuleHandleW)(LPCWSTR lpModuleName);") -virtualalloc = a.typedobj("int __stdcall VirtualAlloc(int lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);") -# typed objects can become calling if an EA was provided. Thus virtualfree.ea = some_address, then we can call virtualfree(., .., ...) -virtualfree = a.typedobj("BOOL __stdcall VirtualFree(int lpAddress, SIZE_T dwSize, DWORD dwFreeType);") -asm1 = a.proto("_asm1", "int __usercall asm1(int a, int b);") -asm2 = a.proto("_asm2", "int __usercall asm2(int a, int b, int c);") -asm3 = a.proto("_asm3", "int __usercall asm3(int f, int a, int b, int c);") -asm4 = a.proto("_asm4", "unsigned __int16 __usercall asm4(unsigned __int8 a, unsigned __int8 b);") -asm5 = a.proto("_asm5", "unsigned int __usercall asm5(unsigned __int8 a, unsigned __int8 b, unsigned __int8 c
, int d);") -altsum = a.proto("_va_altsum", "int __cdecl va_altsum(int n1, ...);") -getcommandline = a.proto("__imp__GetCommandLineA@0", "LPSTR (__stdcall *GetCommandLineA)();") - -# make an appcall with a user defined prototype -subst = a.proto("_subst", "int __pascal subst(int a, int b);") - -# some test identifiers -st_ref1 = 1 -st_ref2 = 2 -st_ref3 = 3 -st_subst = 4 -st_make1 = 5 -st_make2 = 6 -st_asm = 7 -st_ex = 8 -st_byval3 = 9 -st_byvalref1 = 10 -st_altsum = 11 -st_make3 = 12 -st_op64 = 13 -st_ref4 = 14 -st_last = 15 -# some area where we can write some bytes _safely_ -WRITE_AREA = 0x400020 -gr = None -# - -# initialize the test -#test_init() +try: + import pywraps + pywraps_there = True +except: + pywraps_there = False +import _idaapi +import random +import operator +import datetime + +if pywraps_there: + _idaapi.appcall = pywraps.appcall + from py_idaapi import * +else: + import idaapi + from idaapi import * + +# ---------------------------------------------------------------------------------------------------------------------------------------------- +# +import types + +# ----------------------------------------------------------------------- +class Appcall_array__(object): + """This class is used with Appcall.array() method""" + def __init__(self, tp): + self.__type = tp + + def pack(self, L): + """Packs a list or tuple into a byref buffer""" + t = type(L) + if not (t == types.ListType or t == types.TupleType): + raise ValueError, "Either a list or a tuple must be passed" + self.__size = len(L) + if self.__size == 1: + self.__typedobj = Appcall__.typedobj(self.__type + ";") + else: + self.__typedobj = Appcall__.typedobj("%s x[%d];" % (self.__type, self.__size)) + # Now store the object in a string buffer + ok, buf = self.__typedobj.store(L) + if ok: + return Appcall__.byref(buf) + else: + return None + + def try_to_convert_to_list(self, obj): + """Is this object a list? We check for the existance of attribute zero and attribute self.size-1""" + if not (hasattr(obj, "0") and hasattr(obj, str(self.__size-1))): + return obj + # at this point, we are sure we have an "idc list" + # let us convert to a Python list + return [getattr(obj, str(x)) for x in xrange(0, self.__size)] + + def unpack(self, buf, as_list=True): + """Unpacks an array back into a list or an object""" + # take the value from the special ref object + if isinstance(buf, PyIdc_cvt_refclass__): + buf = buf.value + + # we can only unpack from strings + if type(buf) != types.StringType: + raise ValueError, "Cannot unpack this type!" + # now unpack + ok, obj = self.__typedobj.retrieve(buf) + if not ok: + raise ValueError, "Failed while unpacking!" + if not as_list: + return obj + return self.try_to_convert_to_list(obj) + + +# ----------------------------------------------------------------------- +# Wrapper class for the appcall() +class Appcall_callable__(object): + """ + Helper class to issue appcalls using a natural syntax: + appcall.FunctionNameInTheDatabase(arguments, ....) + or + appcall["Function@8"](arguments, ...) + or + f8 = appcall["Function@8"] + f8(arg1, arg2, ...) + or + o = appcall.obj() + i = byref(5) + appcall.funcname(arg1, i, "hello", o) + """ + def __init__(self, ea, tp = None, fld = None): + """Initializes an appcall with a given function ea""" + self.__ea = ea + self.__type = tp + self.__fields = fld + self.__options = None # Appcall options + self.__timeout = None # Appcall timeout + + def __get_timeout(self): + return self.__timeout + + def __set_timeout(self, v): + self.__timeout = v + + timeout = property(__get_timeout, __set_timeout) + """An Appcall instance can change its timeout value with this attribute""" + + def __get_options(self): + return self.__options if self.__options != None else Appcall__.get_appcall_options() + + def __set_options(self, v): + if self.timeout: + # If timeout value is set, then put the timeout flag and encode the timeout value + v |= Appcall__.APPCALL_TIMEOUT | (self.timeout << 16) + else: + # Timeout is not set, then clear the timeout flag + v &= ~Appcall__.APPCALL_TIMEOUT + + self.__options = v + + options = property(__get_options, __set_options) + """Sets the Appcall options locally to this Appcall instance""" + + def __call__(self, *args): + """Make object callable. We redirect execution to idaapi.appcall()""" + if self.ea is None: + raise ValueError, "Object not callable!" + + # convert arguments to a list + arg_list = list(args) + + # Save appcall options and set new global options + old_opt = Appcall__.get_appcall_options() + Appcall__.set_appcall_options(self.options) + + # Do the Appcall (use the wrapped version) + e_obj = None + try: + r = _idaapi.appcall( + self.ea, + _idaapi.get_current_thread(), + self.type, + self.fields, + arg_list) + except Exception as e: + e_obj = e + + # Restore appcall options + Appcall__.set_appcall_options(old_opt) + + # Return or re-raise exception + if e_obj: + raise Exception, e_obj + + return r + + def __get_ea(self): + return self.__ea + + def __set_ea(self, val): + self.__ea = val + + ea = property(__get_ea, __set_ea) + """Returns or sets the EA associated with this object""" + + def __get_size(self): + if self.__type == None: + return -1 + r = _idaapi.calc_type_size(_idaapi.cvar.idati, self.__type) + if not r: + return -1 + return r + + size = property(__get_size) + """Returns the size of the type""" + + def __get_type(self): + return self.__type + + type = property(__get_type) + """Returns the typestring""" + + def __get_fields(self): + return self.__fields + + fields = property(__get_fields) + """Returns the field names""" + + + def retrieve(self, src=None, flags=0): + """ + Unpacks a typed object from the database if an ea is given or from a string if a string was passed + @param src: the address of the object or a string + @return: Returns a tuple of boolean and object or error number (Bool, Error | Object). + """ + + # Nothing passed? Take the address and unpack from the database + if src is None: + src = self.ea + + if type(src) == types.StringType: + return _idaapi.unpack_object_from_bv(_idaapi.cvar.idati, self.type, self.fields, src, flags) + else: + return _idaapi.unpack_object_from_idb(_idaapi.cvar.idati, self.type, self.fields, src, flags) + + def store(self, obj, dest_ea=None, base_ea=0, flags=0): + """ + Packs an object into a given ea if provided or into a string if no address was passed. + @param obj: The object to pack + @param dest_ea: If packing to idb this will be the store location + @param base_ea: If packing to a buffer, this will be the base that will be used to relocate the pointers + + @return: + - If packing to a string then a Tuple(Boolean, packed_string or error code) + - If packing to the database then a return code is returned (0 is success) + """ + + # no ea passed? thus pack to a string + if dest_ea is None: + return _idaapi.pack_object_to_bv(obj, + _idaapi.cvar.idati, + self.type, + self.fields, + base_ea, + flags) + else: + return _idaapi.pack_object_to_idb(obj, + _idaapi.cvar.idati, + self.type, + self.fields, + dest_ea, + flags) + +# ----------------------------------------------------------------------- +class Appcall_consts__(object): + """Helper class used by Appcall.Consts attribute + It is used to retrieve constants via attribute access""" + def __init__(self, default=0): + self.__default = default + + def __getattr__(self, attr): + return Appcall__.valueof(attr, self.__default) + +# ----------------------------------------------------------------------- +class Appcall__(object): + APPCALL_MANUAL = 0x1 + """ + Only set up the appcall, do not run it. + you should call CleanupAppcall() when finished + """ + + APPCALL_DEBEV = 0x2 + """ + Return debug event information + If this bit is set, exceptions during appcall + will generate idc exceptions with full + information about the exception + """ + + APPCALL_TIMEOUT = 0x4 + """ + Appcall with timeout + The timeout value in milliseconds is specified + in the high 2 bytes of the 'options' argument: + If timed out, errbuf will contain "timeout". + """ + + def __init__(self): + self.__consts = Appcall_consts__() + def __get_consts(self): + return self.__consts + Consts = property(__get_consts) + """Use Appcall.Consts.CONST_NAME to access constants""" + + @staticmethod + def __name_or_ea(name_or_ea): + """ + Function that accepts a name or an ea and checks if the address is enabled. + If a name is passed then idaapi.get_name_ea() is applied to retrieve the name + @return: + - Returns the resolved EA or + - Raises an exception if the address is not enabled + """ + + # a string? try to resolve it + if type(name_or_ea) == types.StringType: + ea = _idaapi.get_name_ea(_idaapi.BADADDR, name_or_ea) + else: + ea = name_or_ea + # could not resolve name or invalid address? + if ea == _idaapi.BADADDR or not _idaapi.isEnabled(ea): + raise ValueError, "Undefined function " + name_or_ea + return ea + + @staticmethod + def proto(name_or_ea, prototype, flags = None): + """ + Allows you to instantiate an appcall (callable object) with the desired prototype + @param name_or_ea: The name of the function (will be resolved with LocByName()) + @param prototype: + @return: + - On failure it raises an exception if the prototype could not be parsed + or the address is not resolvable + - Returns a callbable Appcall instance with the given prototypes and flags + """ + + # resolve and raise exception on error + ea = Appcall__.__name_or_ea(name_or_ea) + # parse the type + if flags is None: + flags = 1 | 2 | 4 # PT_SIL | PT_NDC | PT_TYP + + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, prototype, flags) + if result is None: + raise ValueError, "Could not parse type: " + prototype + + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + def __getattr__(self, name_or_ea): + """Allows you to call functions as if they were member functions (by returning a callable object)""" + # resolve and raise exception on error + ea = self.__name_or_ea(name_or_ea) + if ea == _idaapi.BADADDR: + raise ValueError, "Undefined function " + name + # Return the callable method + return Appcall_callable__(ea) + + def __getitem__(self, idx): + """ + Use self[func_name] syntax if the function name contains invalid characters for an attribute name + See __getattr___ + """ + return self.__getattr__(idx) + + @staticmethod + def valueof(name, default=0): + """ + Returns the numeric value of a given name string. + If the name could not be resolved then the default value will be returned + """ + t, v = _idaapi.get_name_value(_idaapi.BADADDR, name) + if t == 0: # NT_NONE + v = default + return v + + @staticmethod + def int64(v): + """Whenever a 64bit number is needed use this method to construct an object""" + return PyIdc_cvt_int64__(v) + + @staticmethod + def byref(val): + """ + Method to create references to immutable objects + Currently we support references to int/strings + Objects need not be passed by reference (this will be done automatically) + """ + return PyIdc_cvt_refclass__(val) + + @staticmethod + def buffer(str = None, size = 0, fill="\x00"): + """ + Creates a string buffer. The returned value (r) will be a byref object. + Use r.value to get the contents and r.size to get the buffer's size + """ + if str is None: + str = "" + left = size - len(str) + if left > 0: + str = str + (fill * left) + r = Appcall__.byref(str) + r.size = size + return r + + @staticmethod + def obj(**kwds): + """Returns an empty object or objects with attributes as passed via its keywords arguments""" + return object_t(**kwds) + + @staticmethod + def cstr(val): + return as_cstr(val) + + @staticmethod + def unicode(s): + return as_unicode(s) + + @staticmethod + def array(type_name): + """Defines an array type. Later you need to pack() / unpack()""" + return Appcall_array__(type_name) + + @staticmethod + def typedobj(typestr, ea=None): + """ + Parses a type string and returns an appcall object. + One can then use retrieve() member method + @param ea: Optional parameter that later can be used to retrieve the type + @return: Appcall object or raises ValueError exception + """ + # parse the type + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, typestr, 1 | 2 | 4) # PT_SIL | PT_NDC | PT_TYP + if result is None: + raise ValueError, "Could not parse type: " + typestr + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + @staticmethod + def set_appcall_options(opt): + """Method to change the Appcall options globally (not per Appcall)""" + old_opt = Appcall__.get_appcall_options() + _idaapi.cvar.inf.appcall_options = opt + return old_opt + + @staticmethod + def get_appcall_options(): + """Return the global Appcall options""" + return _idaapi.cvar.inf.appcall_options + + @staticmethod + def cleanup_appcall(tid = 0): + """Equivalent to IDC's CleanupAppcall()""" + return _idaapi.cleanup_appcall(tid) + +Appcall = Appcall__() +# + +# ---------------------------------------------------------------------------------------------------------------------------------------------- + +# +a = Appcall # Take a shortcut to Appcall +c = Appcall.Consts # take shortcut to constants + +# ----------------------------------------------------------------------- +# - Adds missing types +# - ... +def init(): + # add neeeded types + if a.valueof("ERROR_FILE_NOT_FOUND") == 0: + add_type("MACRO_ERROR") + + setattr(c, "ERROR_FILE_NOT_FOUND", 2) + + if a.valueof("MEM_COMMIT") == 0: + add_type("MACRO_PAGE") + + # open log file + global test_log + try: + test_log = file("python_test.log", "a") + except: + test_log = None + +# ----------------------------------------------------------------------- +def deinit(): + global test_log + test_log.close() + test_log = None + return 1 + +# ----------------------------------------------------------------------- +def logprint(s): + if not test_log: + print s + else: + test_log.write(s + "\n") + return 1 + +# ----------------------------------------------------------------------- +def add_type(t): + print "adding type:", t + idaapi.import_type(idaapi.cvar.idati, 0, t) + +# ----------------------------------------------------------------------- +def test_init(): + hmod = getmodulehandlew(a.unicode("kernel32.dll")) + print "k32hmod=%x" % hmod + if hmod == 0: + return -1 + p = getprocaddr(hmod, "VirtualAlloc") + if p == 0: + return -2 + print "VirtualAlloc->%x" % p + virtualalloc.ea = p + + p = getprocaddr(hmod, "VirtualFree") + if p == 0: + return -3 + print "VirtualFree->%x" % p + virtualfree.ea = p + + m = virtualalloc(0, c.MEM_COMMIT, 0x1000, c.PAGE_EXECUTE_READWRITE) + idc.RefreshDebuggerMemory() + print "%x: allocated memory\n" % m + global WRITE_AREA + WRITE_AREA = m + + return 1 + +# ----------------------------------------------------------------------- +def test_deinit(): + virtualfree(WRITE_AREA, 0, c.MEM_FREE) + +# ----------------------------------------------------------------------- +# Tests changedir/setdir/buffer creation (two methods) and cstr() +def test_enum_files(): + # create a buffer + savedpath = a.byref("\x00" * 260) + # get current directory + n = getcurdir(250, savedpath) + out = [] + out.append("curdir=%s" % savedpath.value[0:n]) + + # get windir + windir = a.buffer(size=260) # create a buffer using helper function + n = getwindir(windir, windir.size) + if n == 0: + return -1 # could not get current directory + + windir = windir.value[:n] + out.append("windir=%s" % windir) + + # change to windows folder + setcurdir(windir) + + # initiate find + fd = a.obj() + h = findfirst("*.exe", fd) + if h == -1: + return -2 # no files found! + + found = -6 + while True: + fn = a.cstr(fd.cFileName) + if "regedit" in fn: + found = 1 + out.append("fn=%s<" % fn) + fd = a.obj() # reset the FD object + ok = findnext(h, fd) + if not ok: + break + # + findclose(h) + + # restore cur dir + setcurdir(savedpath.value) + + # verify + t = a.buffer(size=260) + n = getcurdir(t.size, t) + if t.cstr() != savedpath.cstr(): + return -4 # could not restore cur dir + + out.append("curdir=%s<" % t.cstr()) +# print "all done!" +# for l in out: +# print l + + return found +# ----------------------------------------------------------------------- +def test_gpa(): + h = loadlib("user32.dll") + if h == 0: + print "failed to load library!" + return -1 + p = getprocaddr(h, "FindWindowA") + if p == 0: + print "failed to gpa!" + return -2 + findwin = a.proto(p, "int FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);") + hwnd = findwin("TIdaWindow", 0) + freelib(h) + print "%x: ok!->hwnd=%x" % (p, hwnd) + + return 1 + +# ----------------------------------------------------------------------- +def test_loaddll(): + h = loadlib("twain_32.dll") + if h == 0: + print "failed to load twain32 library!" + return -1 + return 1 + +# ----------------------------------------------------------------------- +# Packs a simple structure (into the database) and unpacks it back using the idaapi methods +def test_pck_idb_raw(): + name, tp, flds = idc.ParseType("struct { int a, b; char x[5];};", 0) + o = a.obj(a=15, b=17,x="hi") + idaapi.pack_object_to_idb(o, idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + + ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + if obj.a != 15 and obj.b != 17 and obj != "hi": + return -2 + return 1 + +# ----------------------------------------------------------------------- +# Packs a simple structure (into a string) and unpacks it back using the idaapi methods +def test_pck_bv_raw(): + name, tp, flds = idc.ParseType("struct { int a; char x[5]; int b;};", 0) + va,vb,vx = 15,17,"hi" + o = a.obj(a=va, b=vb,x=vx) + ok, s = idaapi.pack_object_to_bv(o, idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + if not ok: + return -1 + ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + if obj.a != va and obj.b != vb and obj.x != vx: + return -2 + return 1 + +# ----------------------------------------------------------------------- +# 1. Unpacks the DOS header at 0x400000 and verify the fields +# 2. Unpacks a string and see if it is unpacked correctly +def test_unpack_raw(): + name, tp, flds = idc.ParseType("IMAGE_DOS_HEADER;", 0) + ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, 0x400000, 0) + if obj.e_magic != 23117 and obj.e_cblp != 144: + return -1 + + name, tp, flds = idc.ParseType("struct abc_t { int a, b;};", 0) + ok, obj = idaapi.unpack_object_from_bv(idaapi.cvar.idati, tp, flds, "\x01\x00\x00\x00\x02\x00\x00\x00", 0) + if obj.a != 1 and obj.b != 2: + return -2 + + return 1 + +# ----------------------------------------------------------------------- +# Packs/Unpacks a structure to the database using appcall facilities +def test_pck_idb(): + print "%x: ..." % WRITE_AREA + tp = a.typedobj("struct { int a, b; char x[5];};") + o = a.obj(a=16, b=17,x="zzzhi") + if tp.store(o, WRITE_AREA) != 0: + return -1 + idc.RefreshDebuggerMemory() + + ok, r = tp.retrieve(WRITE_AREA) + if not ok: + return -2 + if r.a != o.a and r.b != o.b and r.x != o.x: + return -3 + + return 1 + +# ----------------------------------------------------------------------- +# Packs/Unpacks a structure to/from a string +def test_pck_bv(): + tp = a.typedobj("struct { int a, b; char x[5];};") + o = a.obj(a=16, b=17,x="zzzhi") + ok, packed = tp.store(o) + if not ok: + return -1 + print "packed->", repr(packed) + ok, r = tp.retrieve(packed) + if not ok: + return -2 + + if r.a != o.a and r.b != o.b and r.x != o.x: + return -3 + + return 1 + +# ----------------------------------------------------------------------- +# Test work with local types +def test_local_types(): + (type, fields) = GetLocalTinfo(1) + if not type: + return -1 + + decl = GetLocalType(1, PRTYPE_MULTI) + if decl != "enum\n"\ + + "{\n"\ + + " FEATURE_OBJECT_CACHING = 0x0,\n"\ + + " FEATURE_ZONE_ELEVATION = 0x1,\n"\ + + " FEATURE_MIME_HANDLING = 0x2,\n"\ + + " FEATURE_MIME_SNIFFING = 0x3,\n"\ + + " FEATURE_WINDOW_RESTRICTIONS = 0x4,\n"\ + + " FEATURE_WEBOC_POPUPMANAGEMENT = 0x5,\n"\ + + " FEATURE_BEHAVIORS = 0x6,\n"\ + + " FEATURE_DISABLE_MK_PROTOCOL = 0x7,\n"\ + + " FEATURE_LOCALMACHINE_LOCKDOWN = 0x8,\n"\ + + " FEATURE_SECURITYBAND = 0x9,\n"\ + + " FEATURE_RESTRICT_ACTIVEXINSTALL = 0xA,\n"\ + + " FEATURE_VALIDATE_NAVIGATE_URL = 0xB,\n"\ + + " FEATURE_RESTRICT_FILEDOWNLOAD = 0xC,\n"\ + + " FEATURE_ADDON_MANAGEMENT = 0xD,\n"\ + + " FEATURE_PROTOCOL_LOCKDOWN = 0xE,\n"\ + + " FEATURE_HTTP_USERNAME_PASSWORD_DISABLE = 0xF,\n"\ + + " FEATURE_SAFE_BINDTOOBJECT = 0x10,\n"\ + + " FEATURE_UNC_SAVEDFILECHECK = 0x11,\n"\ + + " FEATURE_GET_URL_DOM_FILEPATH_UNENCODED = 0x12,\n"\ + + " FEATURE_TABBED_BROWSING = 0x13,\n"\ + + " FEATURE_SSLUX = 0x14,\n"\ + + " FEATURE_DISABLE_NAVIGATION_SOUNDS = 0x15,\n"\ + + " FEATURE_DISABLE_LEGACY_COMPRESSION = 0x16,\n"\ + + " FEATURE_FORCE_ADDR_AND_STATUS = 0x17,\n"\ + + " FEATURE_XMLHTTP = 0x18,\n"\ + + " FEATURE_DISABLE_TELNET_PROTOCOL = 0x19,\n"\ + + " FEATURE_FEEDS = 0x1A,\n"\ + + " FEATURE_BLOCK_INPUT_PROMPTS = 0x1B,\n"\ + + " FEATURE_ENTRY_COUNT = 0x1C,\n"\ + + "} _tagINTERNETFEATURELIST\n": + print "decl = " + decl + return -2 + + return 1 + +# ----------------------------------------------------------------------- +# various tests +def test1(stage): + # call a method that takes a string buffer and appends a dot to its end + if stage == st_ref2: + buf = a.buffer("test", 100) + vals = [378, 424, 470] + for i in xrange(0, 2+1): + n = a._ref2(buf) + if buf.value[4+i] != '.': + return -st_ref2 + if vals[i] != n: + return -stage + # call a method that takes an integer reference + elif stage == st_ref1: + v = 5 + i = a.byref(v) + a._ref1(i) + if v + 1 != i.value: + return -stage + # call a method that takes an array of integers + elif stage == st_ref3: + # create an array type + arr = a.array("int") + # create a list + L = [x for x in xrange(1, 10)] + # pack the list + p_list = arr.pack(L) + # appcall to compute the total + c_total = a._ref3(p_list, len(L)) + # internally compute the total + total = reduce(operator.add, L) + if total != c_total: + return -stage + # subst() + elif stage == st_subst: + v = a._subst(5, 1) + if v != 4: + return -stage + v = subst(5, 1) # subst() / pascal + if v != -4: + return -stage*2 + elif stage == st_make2: + x = a._str2_make(5) + s = a.cstr(x.next.str) +# print "len=%d;<%s>" % (len(s), s) + if s != "This is string 2": + return -stage + n = a._str2_print(x) + if n != 5: + return -st_make2*2 + elif stage == st_make1: + x = a._str1_make(6) + if x.val != 1 and x.next.val != 2: + return -st_make1 + n = a._str1_print(x) + if n != 6: + return -stage + # 64bit test + elif stage == st_make3: + global gr + try: + x = a._str3_make(6) + gr = x + except Exception as e: + print "Exception: ", str(e) + return -stage + print "x.val32=", x.val32 + if (x.val32 != 1) and (x.val64 != (1 << 32)): + return -stage * 2 + va = 0 + vb = 0 + r = x + i = 0 + while x != 0: + i += 1 + print "1" + va += x.val32 + print "2" + vb += x.val64.value + print "3" + x = x.next + print "i=", i, "a=", va, "b=", vb + if va != 21 and vb != 90194313216: + return -stage*3 + elif stage == st_asm: + n = asm1(5, 1) + if n != 4: + return -stage + n = asm2(5, 1, 1) + if n != 7: + return -stage*2 + elif stage == st_byvalref1: + v1 = a.obj(val=5, next=0) + v2 = a.obj(str="Hello", next=0) + n = a._byvalref1(v1, 1, v2) + if n != 78: + return -stage + # v1 is passed by ref, thus it will be changed + if n + 1 != v1.val: + return -stage * 2 + elif stage == st_altsum: + # 1 + 2 - 3 + 5 = 5 + n = altsum(1, 2, 3, 5, 0) + if n != 5: + return -stage; + elif stage == st_op64: + # invalid opcode, should return -1 + r = a._op_two64(1, 2, 6).value + print "r=", r + if r != -1: + return -stage + r = a._op_two64(6, a.int64(2), 3).value + if r != 12: + return -stage * 2 + return 1 + elif stage == st_byval3: + o = a.obj(val=6,next=a.obj(val=-1, next=0)) +# print "before: o.val=%d o.next.val=%d" % (o.val, o.next.val) + n = a._byval3(o) + if n != 5: + return -stage +# print "after: o.val=%d o.next.val=%d, n=%d" % (o.val, o.next.val, n) + #--------- + elif stage == st_ex: + def doit(): + try: + # causes a DebugBreak() + a._ex2(1) + except Exception as e: + if not "Software breakpoint" in e.message: + return -stage + try: + a._ex1(1, 2) + return -st_ex * 2 + except Exception as e: + if not "referenced memory" in e.message: + return -stage + return 1 + old = a.set_appcall_options(0) + r = doit() + a.set_appcall_options(old) + if r <= 0: + return r + #--------- + elif stage == st_ref4: + i = a.int64(5) + v = a.byref(i) + if a._ref4(v) != 1: + return -st_ref4 + # test (in case recycling failed) + if v.value.value != 6: + return -st_ref4 * 2 + # test recycling + if i.value != 6: + return -st_ref4 * 3 + # return success + return 1 + +# ----------------------------------------------------------------------- +def test2(): + fn = "pc_win32_appcall.pe" + + # Try to open an non-existing file + h = a._file_open(fn + ".1") + if h != 0: + return -1 + + n = getlasterror() + print "gle=", n + if n != c.ERROR_FILE_NOT_FOUND: + return -2 + + # Should succeed + h = a._file_open(fn) + if h == 0: + return -3 + + s = a.buffer("", 10, "!") + n = a._file_read(h, s, 2) +# print "read=%d; buf=%s" % (n, repr(s.value)) + if s.value[:2] != "MZ": + return -4 + + n = a._file_close(h) + if n != h-1: + return -5 + + return 1 + +# ----------------------------------------------------------------------- +# This test changes the appcall options and sees if the appcall +# generates an exception and if we get it properly +# An appcall can throw an exception: +# - ValueError: conversion from/to idc/py failed +# - OSError: an OSError when APPCALL_DEBEV in Appcall_Options. In that case check the exc.args[0] to get the debug event +# - Exception: in all other cases +def test_exec_throw(): + old = a.set_appcall_options(0) + print "old_opt=", old, "new=0" + try: + # causes a divide by zero exception (will be reported as an Exception) + print a._op_two64(2,0,4).value + return -1 + except Exception as e: + print "runtime error!" + + # now appcall exceptions will be reported as OSErr and other exceptions as Exception + print "old_opt=", a.set_appcall_options(a.APPCALL_DEBEV), "new=2" + try: + # causes an error: Wrong number of arguments + print a._op_two64(2).value + return -2 + except OSError, e: + return -3 + except Exception as e: + print "Got other exception:", e # good + + try: + # causes an OS error and "e" will contain the last debug_event + # in our case exception, and the code is int_divide_zero = 0xC0000094 + print a._op_two64(2, 0, 4).value + return -4 + except OSError, e: + if idaapi.as_uint32(e.args[0].code) != 0xC0000094: + return -5 + except Exception as e: + print "Got other exception:", e + return -6 + a.set_appcall_options(old) + return 1 +# ----------------------------------------------------------------------- +# all the tests that take zero parameters +tests0 = (test_gpa, test_pck_idb_raw, test_pck_bv_raw, + test_unpack_raw, test_pck_idb, test_pck_bv, test_local_types, + test_enum_files, test2, test_exec_throw, test_loaddll) +test_log = None # test log file + +# ----------------------------------------------------------------------- +def test_all(): + if test_init() <= 0: + print "test_init() failed!" + return -1 + + # tests 0 + for t in tests0: + print "testing->", t + r = t() + if r <= 0: + return r + # test 1 + for i in xrange(1, st_last): + print "test1 #", i + r = test1(i) + if r <= 0: + return r + + logprint(datetime.date.today().strftime("Python was here: %Y-%m-%d @ %I:%M:%S%p")) + + test_deinit() + + return 1 + +# ----------------------------------------------------------------------- +init() + +# reference to an integer. use i.value to dereference +i = a.byref(5) + +# object representing the str1_t type +o = a.obj(val=5,next=a.obj(val=-2, next=0)) + +# dictionary representing the str1_t type +# (dictionaries will be converted into IDC objects) +d = {'val':5, 'next': {'val':-2, 'next':0} } + +# initialize some pointers +findfirst = a["__imp__FindFirstFileA@8"] +findnext = a["__imp__FindNextFileA@8"] +findclose = a["__imp__FindClose@4"] +getlasterror = a["__imp__GetLastError@0"] +setcurdir = a["__imp__SetCurrentDirectoryA@4"] +beep = a["__imp__Beep@8"] +getwindir = a["__imp__GetWindowsDirectoryA@8"] +getprocaddr = a.proto("__imp__GetProcAddress@8", "int (__stdcall *GetProcAddress)(int hModule, LPCSTR lpProcName);") +getcurdir = a["__imp__GetCurrentDirectoryA@8"] +loadlib = a.proto("__imp__LoadLibraryA@4", "int (__stdcall *LoadLibraryA)(const char *lpLibFileName);") +freelib = a.proto("__imp__FreeLibrary@4", "int (__stdcall *FreeLibrary)(int hLibModule);") +setlasterror = a.typedobj("void __stdcall SetLastError(int dwErrCode);") +getmodulehandlew = a.proto("__imp__GetModuleHandleW@4", "int (__stdcall *GetModuleHandleW)(LPCWSTR lpModuleName);") +virtualalloc = a.typedobj("int __stdcall VirtualAlloc(int lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);") +# typed objects can become calling if an EA was provided. Thus virtualfree.ea = some_address, then we can call virtualfree(., .., ...) +virtualfree = a.typedobj("BOOL __stdcall VirtualFree(int lpAddress, SIZE_T dwSize, DWORD dwFreeType);") +asm1 = a.proto("_asm1", "int __usercall asm1(int a, int b);") +asm2 = a.proto("_asm2", "int __usercall asm2(int a, int b, int c);") +asm3 = a.proto("_asm3", "int __usercall asm3(int f, int a, int b, int c);") +asm4 = a.proto("_asm4", "unsigned __int16 __usercall asm4(unsigned __int8 a, unsigned __int8 b);") +asm5 = a.proto("_asm5", "unsigned int __usercall asm5(unsigned __int8 a, unsigned __int8 b, unsigned __int8 c
, int d);") +altsum = a.proto("_va_altsum", "int __cdecl va_altsum(int n1, ...);") +getcommandline = a.proto("__imp__GetCommandLineA@0", "LPSTR (__stdcall *GetCommandLineA)();") + +# make an appcall with a user defined prototype +subst = a.proto("_subst", "int __pascal subst(int a, int b);") + +# some test identifiers +st_ref1 = 1 +st_ref2 = 2 +st_ref3 = 3 +st_subst = 4 +st_make1 = 5 +st_make2 = 6 +st_asm = 7 +st_ex = 8 +st_byval3 = 9 +st_byvalref1 = 10 +st_altsum = 11 +st_make3 = 12 +st_op64 = 13 +st_ref4 = 14 +st_last = 15 +# some area where we can write some bytes _safely_ +WRITE_AREA = 0x400020 +gr = None +# + +# initialize the test +#test_init() diff --git a/pywraps/py_askusingform.hpp b/pywraps/py_askusingform.hpp index cca27901..05613252 100644 --- a/pywraps/py_askusingform.hpp +++ b/pywraps/py_askusingform.hpp @@ -1,373 +1,401 @@ -#ifndef __PY_ASKUSINGFORM__ -#define __PY_ASKUSINGFORM__ - -// -// - -//--------------------------------------------------------------------------- -// -#define DECLARE_FORM_ACTIONS form_actions_t *fa = (form_actions_t *)p_fa; - -//--------------------------------------------------------------------------- -static bool textctrl_info_t_assign(PyObject *self, PyObject *other) -{ - textctrl_info_t *lhs = textctrl_info_t_get_clink(self); - textctrl_info_t *rhs = textctrl_info_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -static bool textctrl_info_t_set_text(PyObject *self, const char *s) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - if ( ti == NULL ) - return false; - ti->text = s; - return true; -} - -//------------------------------------------------------------------------- -static const char *textctrl_info_t_get_text(PyObject *self) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - return ti == NULL ? "" : ti->text.c_str(); -} - -//------------------------------------------------------------------------- -static bool textctrl_info_t_set_flags(PyObject *self, unsigned int flags) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - if ( ti == NULL ) - return false; - ti->flags = flags; - return true; -} - -//------------------------------------------------------------------------- -static unsigned int textctrl_info_t_get_flags( - PyObject *self, - unsigned int flags) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - return ti == NULL ? 0 : ti->flags; -} - -//------------------------------------------------------------------------- -static bool textctrl_info_t_set_tabsize( - PyObject *self, - unsigned int tabsize) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - if ( ti == NULL ) - return false; - ti->tabsize = tabsize; - return true; -} - -//------------------------------------------------------------------------- -static unsigned int textctrl_info_t_get_tabsize( - PyObject *self, - unsigned int tabsize) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - return ti == NULL ? 0 : ti->tabsize; -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_enable_field(size_t p_fa, int fid, bool enable) -{ - DECLARE_FORM_ACTIONS; - return fa->enable_field(fid, enable); -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_show_field(size_t p_fa, int fid, bool show) -{ - DECLARE_FORM_ACTIONS; - return fa->show_field(fid, show); -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_move_field( - size_t p_fa, - int fid, - int x, - int y, - int w, - int h) -{ - DECLARE_FORM_ACTIONS; - return fa->move_field(fid, x, y, w, h); -} - -//--------------------------------------------------------------------------- -static int formchgcbfa_get_focused_field(size_t p_fa) -{ - DECLARE_FORM_ACTIONS; - return fa->get_focused_field(); -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_set_focused_field(size_t p_fa, int fid) -{ - DECLARE_FORM_ACTIONS; - return fa->set_focused_field(fid); -} - -//--------------------------------------------------------------------------- -static void formchgcbfa_refresh_field(size_t p_fa, int fid) -{ - DECLARE_FORM_ACTIONS; - return fa->refresh_field(fid); -} - -//--------------------------------------------------------------------------- -static void formchgcbfa_close(size_t p_fa, int close_normally) -{ - DECLARE_FORM_ACTIONS; - fa->close(close_normally); -} - -//--------------------------------------------------------------------------- -static PyObject *formchgcbfa_get_field_value( - size_t p_fa, - int fid, - int ft, - size_t sz) -{ - DECLARE_FORM_ACTIONS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - switch ( ft ) - { - // dropdown list - case 8: - { - // Readonly? Then return the selected index - if ( sz == 1 ) - { - int sel_idx; - if ( fa->get_combobox_value(fid, &sel_idx) ) - return PyLong_FromLong(sel_idx); - } - // Not readonly? Then return the qstring - else - { - qstring val; - if ( fa->get_combobox_value(fid, &val) ) - return PyString_FromString(val.c_str()); - } - break; - } - // multilinetext - tuple representing textctrl_info_t - case 7: - { - textctrl_info_t ti; - if ( fa->get_text_value(fid, &ti) ) - return Py_BuildValue("(sII)", ti.text.c_str(), ti.flags, ti.tabsize); - break; - } - // button - uint32 - case 4: - { - uval_t val; - if ( fa->get_unsigned_value(fid, &val) ) - return PyLong_FromUnsignedLong(val); - break; - } - // ushort - case 2: - { - ushort val; - if ( fa->_get_field_value(fid, &val) ) - return PyLong_FromUnsignedLong(val); - break; - } - // string label - case 1: - { - char val[MAXSTR]; - if ( fa->get_ascii_value(fid, val, sizeof(val)) ) - return PyString_FromString(val); - break; - } - // string input - case 3: - { - qstring val; - val.resize(sz + 1); - if ( fa->get_ascii_value(fid, val.begin(), val.size()) ) - return PyString_FromString(val.begin()); - break; - } - case 5: - { - intvec_t intvec; - // Returned as 1-base - if (fa->get_chooser_value(fid, &intvec)) - { - // Make 0-based - for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) - (*it)--; - ref_t l(PyW_IntVecToPyList(intvec)); - l.incref(); - return l.o; - } - break; - } - // Numeric control - case 6: - { - union - { - sel_t sel; - sval_t sval; - uval_t uval; - ulonglong ull; - } u; - switch ( sz ) - { - case 'S': // sel_t - { - if ( fa->get_segment_value(fid, &u.sel) ) - return Py_BuildValue(PY_FMT64, u.sel); - break; - } - // sval_t - case 'n': - case 'D': - case 'O': - case 'Y': - case 'H': - { - if ( fa->get_signed_value(fid, &u.sval) ) - return Py_BuildValue(PY_SFMT64, u.sval); - break; - } - case 'L': // uint64 - case 'l': // int64 - { - if ( fa->_get_field_value(fid, &u.ull) ) - return Py_BuildValue("K", u.ull); - break; - } - case 'N': - case 'M': // uval_t - { - if ( fa->get_unsigned_value(fid, &u.uval) ) - return Py_BuildValue(PY_FMT64, u.uval); - break; - } - case '$': // ea_t - { - if ( fa->get_ea_value(fid, &u.uval) ) - return Py_BuildValue(PY_FMT64, u.uval); - break; - } - } - break; - } - } - Py_RETURN_NONE; -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_set_field_value( - size_t p_fa, - int fid, - int ft, - PyObject *py_val) -{ - DECLARE_FORM_ACTIONS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - - switch ( ft ) - { - // dropdown list - case 8: - { - // Editable dropdown list - if ( PyString_Check(py_val) ) - { - qstring val(PyString_AsString(py_val)); - return fa->set_combobox_value(fid, &val); - } - // Readonly dropdown list - else - { - int sel_idx = PyLong_AsLong(py_val); - return fa->set_combobox_value(fid, &sel_idx); - } - break; - } - // multilinetext - textctrl_info_t - case 7: - { - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(py_val); - return ti == NULL ? false : fa->set_text_value(fid, ti); - } - // button - uint32 - case 4: - { - uval_t val = PyLong_AsUnsignedLong(py_val); - return fa->set_unsigned_value(fid, &val); - } - // ushort - case 2: - { - ushort val = PyLong_AsUnsignedLong(py_val) & 0xffff; - return fa->_set_field_value(fid, &val); - } - // strings - case 3: - case 1: - return fa->set_ascii_value(fid, PyString_AsString(py_val)); - // intvec_t - case 5: - { - intvec_t intvec; - // Passed as 0-based - if ( !PyW_PyListToIntVec(py_val, intvec) ) - break; - - // Make 1-based - for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) - (*it)++; - - return fa->set_chooser_value(fid, &intvec); - } - // Numeric - case 6: - { - uint64 num; - if ( PyW_GetNumber(py_val, &num) ) - return fa->_set_field_value(fid, &num); - } - } - return false; -} - -#undef DECLARE_FORM_ACTIONS - -static size_t py_get_AskUsingForm() -{ - // Return a pointer to the function. Note that, although - // the C implementation of AskUsingForm_cv will do some - // Qt/txt widgets generation, the Python's ctypes - // implementation through which the call well go will first - // unblock other threads. No need to do it ourselves. - return (size_t)AskUsingForm_c; -} - -static size_t py_get_OpenForm() -{ - // See comments above. - return (size_t)OpenForm_c; -} - -// - -#endif // __PY_ASKUSINGFORM__ \ No newline at end of file +#ifndef __PY_ASKUSINGFORM__ +#define __PY_ASKUSINGFORM__ + +// +void free_compiled_form_instances(void) +{ + while ( !py_compiled_form_vec.empty() ) + { + const ref_t &ref = py_compiled_form_vec[0]; + qstring title; + if ( !PyW_GetStringAttr(ref.o, "title", &title) ) + title = ""; + msg("WARNING: Form \"%s\" was not Free()d. Force-freeing.\n", title.c_str()); + // Will call 'py_unregister_compiled_form()', and thus trim the vector down. + newref_t unused(PyObject_CallMethod(ref.o, (char *)"Free", "()")); + } +} +// + +//--------------------------------------------------------------------------- +// +#define DECLARE_FORM_ACTIONS form_actions_t *fa = (form_actions_t *)p_fa; + +//--------------------------------------------------------------------------- +static bool textctrl_info_t_assign(PyObject *self, PyObject *other) +{ + textctrl_info_t *lhs = textctrl_info_t_get_clink(self); + textctrl_info_t *rhs = textctrl_info_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_text(PyObject *self, const char *s) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->text = s; + return true; +} + +//------------------------------------------------------------------------- +static const char *textctrl_info_t_get_text(PyObject *self) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? "" : ti->text.c_str(); +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_flags(PyObject *self, unsigned int flags) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->flags = flags; + return true; +} + +//------------------------------------------------------------------------- +static unsigned int textctrl_info_t_get_flags( + PyObject *self, + unsigned int flags) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? 0 : ti->flags; +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_tabsize( + PyObject *self, + unsigned int tabsize) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->tabsize = tabsize; + return true; +} + +//------------------------------------------------------------------------- +static unsigned int textctrl_info_t_get_tabsize( + PyObject *self, + unsigned int tabsize) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? 0 : ti->tabsize; +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_enable_field(size_t p_fa, int fid, bool enable) +{ + DECLARE_FORM_ACTIONS; + return fa->enable_field(fid, enable); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_show_field(size_t p_fa, int fid, bool show) +{ + DECLARE_FORM_ACTIONS; + return fa->show_field(fid, show); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_move_field( + size_t p_fa, + int fid, + int x, + int y, + int w, + int h) +{ + DECLARE_FORM_ACTIONS; + return fa->move_field(fid, x, y, w, h); +} + +//--------------------------------------------------------------------------- +static int formchgcbfa_get_focused_field(size_t p_fa) +{ + DECLARE_FORM_ACTIONS; + return fa->get_focused_field(); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_set_focused_field(size_t p_fa, int fid) +{ + DECLARE_FORM_ACTIONS; + return fa->set_focused_field(fid); +} + +//--------------------------------------------------------------------------- +static void formchgcbfa_refresh_field(size_t p_fa, int fid) +{ + DECLARE_FORM_ACTIONS; + return fa->refresh_field(fid); +} + +//--------------------------------------------------------------------------- +static void formchgcbfa_close(size_t p_fa, int close_normally) +{ + DECLARE_FORM_ACTIONS; + fa->close(close_normally); +} + +//--------------------------------------------------------------------------- +static PyObject *formchgcbfa_get_field_value( + size_t p_fa, + int fid, + int ft, + size_t sz) +{ + DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + switch ( ft ) + { + // dropdown list + case 8: + { + // Readonly? Then return the selected index + if ( sz == 1 ) + { + int sel_idx; + if ( fa->get_combobox_value(fid, &sel_idx) ) + return PyLong_FromLong(sel_idx); + } + // Not readonly? Then return the qstring + else + { + qstring val; + if ( fa->get_combobox_value(fid, &val) ) + return PyString_FromString(val.c_str()); + } + break; + } + // multilinetext - tuple representing textctrl_info_t + case 7: + { + textctrl_info_t ti; + if ( fa->get_text_value(fid, &ti) ) + return Py_BuildValue("(sII)", ti.text.c_str(), ti.flags, ti.tabsize); + break; + } + // button - uint32 + case 4: + { + uval_t val; + if ( fa->get_unsigned_value(fid, &val) ) + return PyLong_FromUnsignedLong(val); + break; + } + // ushort + case 2: + { + ushort val; + if ( fa->_get_field_value(fid, &val) ) + return PyLong_FromUnsignedLong(val); + break; + } + // string label + case 1: + { + char val[MAXSTR]; + if ( fa->get_ascii_value(fid, val, sizeof(val)) ) + return PyString_FromString(val); + break; + } + // string input + case 3: + { + qstring val; + val.resize(sz + 1); + if ( fa->get_ascii_value(fid, val.begin(), val.size()) ) + return PyString_FromString(val.begin()); + break; + } + case 5: + { + intvec_t intvec; + // Returned as 1-base + if (fa->get_chooser_value(fid, &intvec)) + { + // Make 0-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)--; + ref_t l(PyW_IntVecToPyList(intvec)); + l.incref(); + return l.o; + } + break; + } + // Numeric control + case 6: + { + union + { + sel_t sel; + sval_t sval; + uval_t uval; + ulonglong ull; + } u; + switch ( sz ) + { + case 'S': // sel_t + { + if ( fa->get_segment_value(fid, &u.sel) ) + return Py_BuildValue(PY_FMT64, u.sel); + break; + } + // sval_t + case 'n': + case 'D': + case 'O': + case 'Y': + case 'H': + { + if ( fa->get_signed_value(fid, &u.sval) ) + return Py_BuildValue(PY_SFMT64, u.sval); + break; + } + case 'L': // uint64 + case 'l': // int64 + { + if ( fa->_get_field_value(fid, &u.ull) ) + return Py_BuildValue("K", u.ull); + break; + } + case 'N': + case 'M': // uval_t + { + if ( fa->get_unsigned_value(fid, &u.uval) ) + return Py_BuildValue(PY_FMT64, u.uval); + break; + } + case '$': // ea_t + { + if ( fa->get_ea_value(fid, &u.uval) ) + return Py_BuildValue(PY_FMT64, u.uval); + break; + } + } + break; + } + } + Py_RETURN_NONE; +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_set_field_value( + size_t p_fa, + int fid, + int ft, + PyObject *py_val) +{ + DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + + switch ( ft ) + { + // dropdown list + case 8: + { + // Editable dropdown list + if ( PyString_Check(py_val) ) + { + qstring val(PyString_AsString(py_val)); + return fa->set_combobox_value(fid, &val); + } + // Readonly dropdown list + else + { + int sel_idx = PyLong_AsLong(py_val); + return fa->set_combobox_value(fid, &sel_idx); + } + break; + } + // multilinetext - textctrl_info_t + case 7: + { + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(py_val); + return ti == NULL ? false : fa->set_text_value(fid, ti); + } + // button - uint32 + case 4: + { + uval_t val = PyLong_AsUnsignedLong(py_val); + return fa->set_unsigned_value(fid, &val); + } + // ushort + case 2: + { + ushort val = PyLong_AsUnsignedLong(py_val) & 0xffff; + return fa->_set_field_value(fid, &val); + } + // strings + case 3: + case 1: + return fa->set_ascii_value(fid, PyString_AsString(py_val)); + // intvec_t + case 5: + { + intvec_t intvec; + // Passed as 0-based + if ( !PyW_PyListToIntVec(py_val, intvec) ) + break; + + // Make 1-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)++; + + return fa->set_chooser_value(fid, &intvec); + } + // Numeric + case 6: + { + uint64 num; + if ( PyW_GetNumber(py_val, &num) ) + return fa->_set_field_value(fid, &num); + } + } + return false; +} + +#undef DECLARE_FORM_ACTIONS + +static size_t py_get_AskUsingForm() +{ + // Return a pointer to the function. Note that, although + // the C implementation of AskUsingForm_cv will do some + // Qt/txt widgets generation, the Python's ctypes + // implementation through which the call well go will first + // unblock other threads. No need to do it ourselves. + return (size_t)AskUsingForm_c; +} + +static size_t py_get_OpenForm() +{ + // See comments above. + return (size_t)OpenForm_c; +} + +static qvector py_compiled_form_vec; +static void py_register_compiled_form(PyObject *py_form) +{ + ref_t ref = borref_t(py_form); + if ( !py_compiled_form_vec.has(ref) ) + py_compiled_form_vec.push_back(ref); +} + +static void py_unregister_compiled_form(PyObject *py_form) +{ + ref_t ref = borref_t(py_form); + if ( py_compiled_form_vec.has(ref) ) + py_compiled_form_vec.del(ref); +} + +// + +#endif // __PY_ASKUSINGFORM__ diff --git a/pywraps/py_askusingform.py b/pywraps/py_askusingform.py index 96ee2054..e88c3453 100644 --- a/pywraps/py_askusingform.py +++ b/pywraps/py_askusingform.py @@ -1,1947 +1,1953 @@ -# -------------------------------------------------------------------------- -import os -import gc -import sys - -try: - import _idaapi - from _idaapi import set_script_timeout - import idaapi - from idaapi import py_clinked_object_t - from idaapi import _qstrvec_t - stdalone = False -except: - stdalone = True - -print("Standalone: %s" % stdalone) - -pywraps_there = False - -try: - if stdalone: - class object_t(object): pass - class py_clinked_object_t(object): pass - _idaapi = object_t() - _idaapi.choose2_get_embedded = lambda obj: (id(obj), 0) - _idaapi.choose2_get_embedded_selection = lambda obj: None - - _idaapi.textctrl_info_t_set_text = lambda self, v: None - _idaapi.CHOOSER_POPUP_MENU = 1 - pywraps = object_t() - pywraps.py_get_AskUsingForm = lambda: 0 - pywraps.py_get_OpenForm = lambda: 0 - - class Choose2(object): - CH_MULTI = 1 - def __init__(self, title, cols, flags=0, popup_names=None, - icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, - embedded=False, width=None, height=None): - pass - - def Close(self): - print("Chooser closing...") - - Embedded = lambda self: 1 - - set_script_timeout = lambda x: x - - else: - import pywraps - pywraps_there = True - from py_choose2 import * - py_clinked_object_t = idaapi.py_clinked_object_t - textctrl_info_t = idaapi.textctrl_info_t - _qstrvec_t = idaapi._qstrvec_t - - _idaapi.BADADDR = 0xFFFFFFFF - _idaapi.MAXSTR = 1024 - set_script_timeout = _idaapi.set_script_timeout - - if not hasattr(_idaapi, 'formchgcbfa_enable_field'): - _idaapi.formchgcbfa_enable_field = pywraps.py_formchgcbfa_enable_field - -except Exception as e: - pass -# print("exception: %s" % str(e)) - - -print("Using PyWraps: %s" % pywraps_there) - -# -------------------------------------------------------------------------- -# -#ICON WARNING|QUESTION|INFO|NONE -#AUTOHIDE NONE|DATABASE|REGISTRY|SESSION -#HIDECANCEL -#BUTTON YES|NO|CANCEL "Value|NONE" -#STARTITEM {id:ItemName} -#HELP / ENDHELP -try: - import types - from ctypes import * - # On Windows, we use stdcall - - # Callback for buttons - # typedef void (idaapi *formcb_t)(TView *fields[], int code); - - _FORMCB_T = WINFUNCTYPE(None, c_void_p, c_int) - - # Callback for form change - # typedef int (idaapi *formchgcb_t)(int field_id, form_actions_t &fa); - _FORMCHGCB_T = WINFUNCTYPE(c_int, c_int, c_void_p) -except: - try: - _FORMCB_T = CFUNCTYPE(None, c_void_p, c_int) - _FORMCHGCB_T = CFUNCTYPE(c_int, c_int, c_void_p) - except: - _FORMCHGCB_T = _FORMCB_T = None - - -# ----------------------------------------------------------------------- -# textctrl_info_t clinked object -class textctrl_info_t(py_clinked_object_t): - """Class representing textctrl_info_t""" - - # Some constants - TXTF_AUTOINDENT = 0x0001 - """Auto-indent on new line""" - TXTF_ACCEPTTABS = 0x0002 - """Tab key inserts 'tabsize' spaces""" - TXTF_READONLY = 0x0004 - """Text cannot be edited (but can be selected and copied)""" - TXTF_SELECTED = 0x0008 - """Shows the field with its text selected""" - TXTF_MODIFIED = 0x0010 - """Gets/sets the modified status""" - TXTF_FIXEDFONT = 0x0020 - """The control uses IDA's fixed font""" - - def __init__(self, text="", flags=0, tabsize=0): - py_clinked_object_t.__init__(self) - if text: - self.text = text - if flags: - self.flags = flags - if tabsize: - self.tabsize = tabsize - - def _create_clink(self): - return _idaapi.textctrl_info_t_create() - - def _del_clink(self, lnk): - return _idaapi.textctrl_info_t_destroy(lnk) - - def _get_clink_ptr(self): - return _idaapi.textctrl_info_t_get_clink_ptr(self) - - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.textctrl_info_t_assign(self, other) - - def __set_text(self, s): - """Sets the text value""" - return _idaapi.textctrl_info_t_set_text(self, s) - - def __get_text(self): - """Sets the text value""" - return _idaapi.textctrl_info_t_get_text(self) - - def __set_flags__(self, flags): - """Sets the flags value""" - return _idaapi.textctrl_info_t_set_flags(self, flags) - - def __get_flags__(self): - """Returns the flags value""" - return _idaapi.textctrl_info_t_get_flags(self) - - def __set_tabsize__(self, tabsize): - """Sets the tabsize value""" - return _idaapi.textctrl_info_t_set_tabsize(self, tabsize) - - def __get_tabsize__(self): - """Returns the tabsize value""" - return _idaapi.textctrl_info_t_get_tabsize(self) - - value = property(__get_text, __set_text) - """Alias for the text property""" - text = property(__get_text, __set_text) - """Text value""" - flags = property(__get_flags__, __set_flags__) - """Flags value""" - tabsize = property(__get_tabsize__, __set_tabsize__) - -# ----------------------------------------------------------------------- -class Form(object): - - FT_ASCII = 'A' - """Ascii string - char *""" - FT_SEG = 'S' - """Segment - sel_t *""" - FT_HEX = 'N' - """Hex number - uval_t *""" - FT_SHEX = 'n' - """Signed hex number - sval_t *""" - FT_COLOR = 'K' - """Color button - bgcolor_t *""" - FT_ADDR = '$' - """Address - ea_t *""" - FT_UINT64 = 'L' - """default base uint64 - uint64""" - FT_INT64 = 'l' - """default base int64 - int64""" - FT_RAWHEX = 'M' - """Hex number, no 0x prefix - uval_t *""" - FT_FILE = 'f' - """File browse - char * at least QMAXPATH""" - FT_DEC = 'D' - """Decimal number - sval_t *""" - FT_OCT = 'O' - """Octal number, C notation - sval_t *""" - FT_BIN = 'Y' - """Binary number, 0b prefix - sval_t *""" - FT_CHAR = 'H' - """Char value -- sval_t *""" - FT_IDENT = 'I' - """Identifier - char * at least MAXNAMELEN""" - FT_BUTTON = 'B' - """Button - def handler(code)""" - FT_DIR = 'F' - """Path to directory - char * at least QMAXPATH""" - FT_TYPE = 'T' - """Type declaration - char * at least MAXSTR""" - _FT_USHORT = '_US' - """Unsigned short""" - FT_FORMCHG = '%/' - """Form change callback - formchgcb_t""" - FT_ECHOOSER = 'E' - """Embedded chooser - idaapi.Choose2""" - FT_MULTI_LINE_TEXT = 't' - """Multi text control - textctrl_info_t""" - FT_DROPDOWN_LIST = 'b' - """Dropdown list control - Form.DropdownControl""" - - FT_CHKGRP = 'C' - FT_CHKGRP2= 'c' - FT_RADGRP = 'R' - FT_RADGRP2= 'r' - - @staticmethod - def fieldtype_to_ctype(tp, i64 = False): - """ - Factory method returning a ctype class corresponding to the field type string - """ - if tp in (Form.FT_SEG, Form.FT_HEX, Form.FT_RAWHEX, Form.FT_ADDR): - return c_ulonglong if i64 else c_ulong - elif tp in (Form.FT_SHEX, Form.FT_DEC, Form.FT_OCT, Form.FT_BIN, Form.FT_CHAR): - return c_longlong if i64 else c_long - elif tp == Form.FT_UINT64: - return c_ulonglong - elif tp == Form.FT_INT64: - return c_longlong - elif tp == Form.FT_COLOR: - return c_ulong - elif tp == Form._FT_USHORT: - return c_ushort - elif tp in (Form.FT_FORMCHG, Form.FT_ECHOOSER): - return c_void_p - else: - return None - - - # - # Generic argument helper classes - # - class NumericArgument(object): - """ - Argument representing various integer arguments (ushort, uint32, uint64, etc...) - @param tp: One of Form.FT_XXX - """ - DefI64 = False - def __init__(self, tp, value): - cls = Form.fieldtype_to_ctype(tp, self.DefI64) - if cls is None: - raise TypeError("Invalid numeric field type: %s" % tp) - # Get a pointer type to the ctype type - self.arg = pointer(cls(value)) - - def __set_value(self, v): - self.arg.contents.value = v - value = property(lambda self: self.arg.contents.value, __set_value) - - - class StringArgument(object): - """ - Argument representing a character buffer - """ - def __init__(self, size=None, value=None): - if size is None: - raise SyntaxError("The string size must be passed") - - if value is None: - self.arg = create_string_buffer(size) - else: - self.arg = create_string_buffer(value, size) - self.size = size - - def __set_value(self, v): - self.arg.value = v - value = property(lambda self: self.arg.value, __set_value) - - - # - # Base control class - # - class Control(object): - def __init__(self): - self.id = 0 - """Automatically assigned control ID""" - - self.arg = None - """Control argument value. This could be one element or a list/tuple (for multiple args per control)""" - - self.form = None - """Reference to the parent form. It is filled by Form.Add()""" - - - def get_tag(self): - """ - Control tag character. One of Form.FT_XXXX. - The form class will expand the {} notation and replace them with the tags - """ - pass - - def get_arg(self): - """ - Control returns the parameter to be pushed on the stack - (Of AskUsingForm()) - """ - return self.arg - - def free(self): - """ - Free the control - """ - # Release the parent form reference - self.form = None - - - # - # Label controls - # - class LabelControl(Control): - """ - Base class for static label control - """ - def __init__(self, tp): - Form.Control.__init__(self) - self.tp = tp - - def get_tag(self): - return '%%%d%s' % (self.id, self.tp) - - - class StringLabel(LabelControl): - """ - String label control - """ - def __init__(self, value, tp=None, sz=1024): - """ - Type field can be one of: - A - ascii string - T - type declaration - I - ident - F - folder - f - file - X - command - """ - if tp is None: - tp = Form.FT_ASCII - Form.LabelControl.__init__(self, tp) - self.size = sz - self.arg = create_string_buffer(value, sz) - - - class NumericLabel(LabelControl, NumericArgument): - """ - Numeric label control - """ - def __init__(self, value, tp=None): - if tp is None: - tp = Form.FT_HEX - Form.LabelControl.__init__(self, tp) - Form.NumericArgument.__init__(self, tp, value) - - - # - # Group controls - # - class GroupItemControl(Control): - """ - Base class for group control items - """ - def __init__(self, tag, parent): - Form.Control.__init__(self) - self.tag = tag - self.parent = parent - # Item position (filled when form is compiled) - self.pos = 0 - - def assign_pos(self): - self.pos = self.parent.next_child_pos() - - def get_tag(self): - return "%s%d" % (self.tag, self.id) - - - class ChkGroupItemControl(GroupItemControl): - """ - Checkbox group item control - """ - def __init__(self, tag, parent): - Form.GroupItemControl.__init__(self, tag, parent) - - def __get_value(self): - return (self.parent.value & (1 << self.pos)) != 0 - - def __set_value(self, v): - pv = self.parent.value - if v: - pv = pv | (1 << self.pos) - else: - pv = pv & ~(1 << self.pos) - - self.parent.value = pv - - checked = property(__get_value, __set_value) - """Get/Sets checkbox item check status""" - - - class RadGroupItemControl(GroupItemControl): - """ - Radiobox group item control - """ - def __init__(self, tag, parent): - Form.GroupItemControl.__init__(self, tag, parent) - - def __get_value(self): - return self.parent.value == self.pos - - def __set_value(self, v): - self.parent.value = self.pos - - selected = property(__get_value, __set_value) - """Get/Sets radiobox item selection status""" - - - class GroupControl(Control, NumericArgument): - """ - Base class for group controls - """ - def __init__(self, children_names, tag, value=0): - Form.Control.__init__(self) - self.children_names = children_names - self.tag = tag - self._reset() - Form.NumericArgument.__init__(self, Form._FT_USHORT, value) - - def _reset(self): - self.childpos = 0 - - def next_child_pos(self): - v = self.childpos - self.childpos += 1 - return v - - def get_tag(self): - return "%d" % self.id - - - class ChkGroupControl(GroupControl): - """ - Checkbox group control class. - It holds a set of checkbox controls - """ - ItemClass = None - """ - Group control item factory class instance - We need this because later we won't be treating ChkGroupControl or RadGroupControl - individually, instead we will be working with GroupControl in general. - """ - def __init__(self, children_names, value=0, secondary=False): - # Assign group item factory class - if Form.ChkGroupControl.ItemClass is None: - Form.ChkGroupControl.ItemClass = Form.ChkGroupItemControl - - Form.GroupControl.__init__( - self, - children_names, - Form.FT_CHKGRP2 if secondary else Form.FT_CHKGRP, - value) - - - class RadGroupControl(GroupControl): - """ - Radiobox group control class. - It holds a set of radiobox controls - """ - ItemClass = None - def __init__(self, children_names, value=0, secondary=False): - """ - Creates a radiogroup control. - @param children_names: A tuple containing group item names - @param value: Initial selected radio item - @param secondory: Allows rendering one the same line as the previous group control. - Use this if you have another group control on the same line. - """ - # Assign group item factory class - if Form.RadGroupControl.ItemClass is None: - Form.RadGroupControl.ItemClass = Form.RadGroupItemControl - - Form.GroupControl.__init__( - self, - children_names, - Form.FT_RADGRP2 if secondary else Form.FT_RADGRP, - value) - - - # - # Input controls - # - class InputControl(Control): - """ - Generic form input control. - It could be numeric control, string control, directory/file browsing, etc... - """ - def __init__(self, tp, width, swidth, hlp = None): - """ - @param width: Display width - @param swidth: String width - """ - Form.Control.__init__(self) - self.tp = tp - self.width = width - self.switdh = swidth - self.hlp = hlp - - def get_tag(self): - return "%s%d:%s:%s:%s" % ( - self.tp, self.id, - self.width, - self.switdh, - ":" if self.hlp is None else self.hlp) - - - class NumericInput(InputControl, NumericArgument): - """ - A composite class serving as a base numeric input control class - """ - def __init__(self, tp=None, value=0, width=50, swidth=10, hlp=None): - if tp is None: - tp = Form.FT_HEX - Form.InputControl.__init__(self, tp, width, swidth, hlp) - Form.NumericArgument.__init__(self, self.tp, value) - - - class ColorInput(NumericInput): - """ - Color button input control - """ - def __init__(self, value = 0): - """ - @param value: Initial color value in RGB - """ - Form.NumericInput.__init__(self, tp=Form.FT_COLOR, value=value) - - - class StringInput(InputControl, StringArgument): - """ - Base string input control class. - This class also constructs a StringArgument - """ - def __init__(self, - tp=None, - width=1024, - swidth=40, - hlp=None, - value=None, - size=None): - """ - @param width: String size. But in some cases it has special meaning. For example in FileInput control. - If you want to define the string buffer size then pass the 'size' argument - @param swidth: Control width - @param value: Initial value - @param size: String size - """ - if tp is None: - tp = Form.FT_ASCII - if not size: - size = width - Form.InputControl.__init__(self, tp, width, swidth, hlp) - Form.StringArgument.__init__(self, size=size, value=value) - - - class FileInput(StringInput): - """ - File Open/Save input control - """ - def __init__(self, - width=512, - swidth=80, - save=False, open=False, - hlp=None, value=None): - - if save == open: - raise ValueError("Invalid mode. Choose either open or save") - if width < 512: - raise ValueError("Invalid width. Must be greater than 512.") - - # The width field is overloaded in this control and is used - # to denote the type of the FileInput dialog (save or load) - # On the other hand it is passed as is to the StringArgument part - Form.StringInput.__init__( - self, - tp=Form.FT_FILE, - width="1" if save else "0", - swidth=swidth, - hlp=hlp, - size=width, - value=value) - - - class DirInput(StringInput): - """ - Directory browsing control - """ - def __init__(self, - width=512, - swidth=80, - hlp=None, - value=None): - - if width < 512: - raise ValueError("Invalid width. Must be greater than 512.") - - Form.StringInput.__init__( - self, - tp=Form.FT_DIR, - width=width, - swidth=swidth, - hlp=hlp, - size=width, - value=value) - - - class ButtonInput(InputControl): - """ - Button control. - A handler along with a 'code' (numeric value) can be associated with the button. - This way one handler can handle many buttons based on the button code (or in other terms id or tag) - """ - def __init__(self, handler, code="", swidth="", hlp=None): - """ - @param handler: Button handler. A callback taking one argument which is the code. - @param code: A code associated with the button and that is later passed to the handler. - """ - Form.InputControl.__init__( - self, - Form.FT_BUTTON, - code, - swidth, - hlp) - self.arg = _FORMCB_T(lambda view, code, h=handler: h(code)) - - - class FormChangeCb(Control): - """ - Form change handler. - This can be thought of like a dialog procedure. - Everytime a form action occurs, this handler will be called along with the control id. - The programmer can then call various form actions accordingly: - - EnableField - - ShowField - - MoveField - - GetFieldValue - - etc... - - Special control IDs: -1 (The form is initialized) and -2 (Ok has been clicked) - - """ - def __init__(self, handler): - """ - Constructs the handler. - @param handler: The handler (preferrably a member function of a class derived from the Form class). - """ - Form.Control.__init__(self) - - # Save the handler - self.handler = handler - - # Create a callback stub - # We use this mechanism to create an intermediate step - # where we can create an 'fa' adapter for use by Python - self.arg = _FORMCHGCB_T(self.helper_cb) - - def helper_cb(self, fid, p_fa): - # Remember the pointer to the forms_action in the parent form - self.form.p_fa = p_fa - - # Call user's handler - r = self.handler(fid) - return 0 if r is None else r - - def get_tag(self): - return Form.FT_FORMCHG - - def free(self): - Form.Control.free(self) - # Remove reference to the handler - # (Normally the handler is a member function in the parent form) - self.handler = None - - - class EmbeddedChooserControl(InputControl): - """ - Embedded chooser control. - This control links to a Chooser2 control created with the 'embedded=True' - """ - def __init__(self, - chooser=None, - swidth=40, - hlp=None): - """ - Embedded chooser control - - @param chooser: A chooser2 instance (must be constructed with 'embedded=True') - """ - - # !! Make sure a chooser instance is passed !! - if chooser is None or not isinstance(chooser, Choose2): - raise ValueError("Invalid chooser passed.") - - # Create an embedded chooser structure from the Choose2 instance - if chooser.Embedded() != 1: - raise ValueError("Failed to create embedded chooser instance.") - - # Construct input control - Form.InputControl.__init__(self, Form.FT_ECHOOSER, "", swidth) - - # Get a pointer to the chooser_info_t and the selection vector - # (These two parameters are the needed arguments for the AskUsingForm()) - emb, sel = _idaapi.choose2_get_embedded(chooser) - - # Get a pointer to a c_void_p constructed from an address - p_embedded = pointer(c_void_p.from_address(emb)) - p_sel = pointer(c_void_p.from_address(sel)) - - # - Create the embedded chooser info on control creation - # - Do not free the embeded chooser because after we get the args - # via Compile() the user can still call Execute() which relies - # on the already computed args - self.arg = (p_embedded, p_sel) - - # Save chooser instance - self.chooser = chooser - - # Add a bogus 'size' attribute - self.size = 0 - - - value = property(lambda self: self.chooser) - """Returns the embedded chooser instance""" - - - def AddCommand(self, - caption, - flags = _idaapi.CHOOSER_POPUP_MENU, - menu_index = -1, - icon = -1): - """ - Adds a new embedded chooser command - Save the returned value and later use it in the OnCommand handler - - @return: Returns a negative value on failure or the command index - """ - if not self.form.title: - raise ValueError("Form title is not set!") - - # Encode all information for the AddCommand() in the 'caption' parameter - caption = "%s:%d:%s" % (self.form.title, self.id, caption) - return self.chooser.AddCommand(caption, flags=flags, menu_index=menu_index, icon=icon, emb=2002) - - - def free(self): - """ - Frees the embedded chooser data - """ - self.chooser.Close() - self.chooser = None - Form.Control.free(self) - - - class DropdownListControl(InputControl, _qstrvec_t): - """ - Dropdown control - This control allows manipulating a dropdown control - """ - def __init__(self, items=[], readonly=True, selval=0, width=50, swidth=50, hlp = None): - """ - @param items: A string list of items used to prepopulate the control - @param readonly: Specifies whether the dropdown list is editable or not - @param selval: The preselected item index (when readonly) or text value (when editable) - @param width: the control width (n/a if the dropdown list is readonly) - @param swidth: string width - """ - - # Ignore the width if readonly was set - if readonly: - width = 0 - - # Init the input control base class - Form.InputControl.__init__( - self, - Form.FT_DROPDOWN_LIST, - width, - swidth, - hlp) - - # Init the associated qstrvec - _qstrvec_t.__init__(self, items) - - # Remember if readonly or not - self.readonly = readonly - - if readonly: - # Create a C integer and remember it - self.__selval = c_int(selval) - val_addr = addressof(self.__selval) - else: - # Create an strvec with one qstring - self.__selval = _qstrvec_t([selval]) - # Get address of the first element - val_addr = self.__selval.addressof(0) - - # Two arguments: - # - argument #1: a pointer to the qstrvec containing the items - # - argument #2: an integer to hold the selection - # or - # #2: a qstring to hold the dropdown text control value - self.arg = ( - pointer(c_void_p.from_address(self.clink_ptr)), - pointer(c_void_p.from_address(val_addr)) - ) - - - def __set_selval(self, val): - if self.readonly: - self.__selval.value = val - else: - self.__selval[0] = val - - def __get_selval(self): - # Return the selection index - # or the entered text value - return self.__selval.value if self.readonly else self.__selval[0] - - value = property(__get_selval, __set_selval) - selval = property(__get_selval, __set_selval) - """ - Read/write the selection value. - The value is used as an item index in readonly mode or text value in editable mode - This value can be used only after the form has been closed. - """ - - def free(self): - self._free() - - - def set_items(self, items): - """Sets the dropdown list items""" - self.from_list(items) - - - class MultiLineTextControl(InputControl, textctrl_info_t): - """ - Multi line text control. - This class inherits from textctrl_info_t. Thus the attributes are also inherited - This control allows manipulating a multilinetext control - """ - def __init__(self, text="", flags=0, tabsize=0, width=50, swidth=50, hlp = None): - """ - @param text: Initial text value - @param flags: One of textctrl_info_t.TXTF_.... values - @param tabsize: Tab size - @param width: Display width - @param swidth: String width - """ - # Init the input control base class - Form.InputControl.__init__(self, Form.FT_MULTI_LINE_TEXT, width, swidth, hlp) - - # Init the associated textctrl_info base class - textctrl_info_t.__init__(self, text=text, flags=flags, tabsize=tabsize) - - # Get the argument as a pointer from the embedded ti - self.arg = pointer(c_void_p.from_address(self.clink_ptr)) - - - def free(self): - self._free() - - - # - # Form class - # - def __init__(self, form, controls): - """ - Contruct a Form class. - This class wraps around AskUsingForm() or OpenForm() and provides an easier / alternative syntax for describing forms. - The form control names are wrapped inside the opening and closing curly braces and the control themselves are - defined and instantiated via various form controls (subclasses of Form). - - @param form: The form string - @param controls: A dictionary containing the control name as a _key_ and control object as _value_ - """ - self._reset() - self.form = form - """Form string""" - self.controls = controls - """Dictionary of controls""" - self.__args = None - - self.title = None - """The Form title. It will be filled when the form is compiled""" - - self.modal = True - """By default, forms are modal""" - - self.openform_flags = 0 - """ - If non-modal, these flags will be passed to OpenForm. - This is an OR'ed combination of the PluginForm.FORM_* values. - """ - - - def Free(self): - """ - Frees all resources associated with a compiled form. - Make sure you call this function when you finish using the form. - """ - - # Free all the controls - for ctrl in self.__controls.values(): - ctrl.free() - - # Reset the controls - # (Note that we are not removing the form control attributes, no need) - self._reset() - - - def _reset(self): - """ - Resets the Form class state variables - """ - self.__controls = {} - self.__ctrl_id = 1 - - - def __getitem__(self, name): - """Returns a control object by name""" - return self.__controls[name] - - - def Add(self, name, ctrl, mkattr = True): - """ - Low level function. Prefer AddControls() to this function. - This function adds one control to the form. - - @param name: Control name - @param ctrl: Control object - @param mkattr: Create control name / control object as a form attribute - """ - # Assign a unique ID - ctrl.id = self.__ctrl_id - self.__ctrl_id += 1 - - # Create attribute with control name - if mkattr: - setattr(self, name, ctrl) - - # Remember the control - self.__controls[name] = ctrl - - # Link the form to the control via its form attribute - ctrl.form = self - - # Is it a group? Add each child - if isinstance(ctrl, Form.GroupControl): - self._AddGroup(ctrl, mkattr) - - - def FindControlById(self, id): - """ - Finds a control instance given its id - """ - for ctrl in self.__controls.values(): - if ctrl.id == id: - return ctrl - return None - - - @staticmethod - def _ParseFormTitle(form): - """ - Parses the form's title from the form text - """ - help_state = 0 - for i, line in enumerate(form.split("\n")): - if line.startswith("STARTITEM ") or line.startswith("BUTTON "): - continue - # Skip "HELP" and remember state - elif help_state == 0 and line == "HELP": - help_state = 1 # Mark inside HELP - continue - elif help_state == 1 and line == "ENDHELP": - help_state = 2 # Mark end of HELP - continue - return line.strip() - - return None - - - def _AddGroup(self, Group, mkattr=True): - """ - Internal function. - This function expands the group item names and creates individual group item controls - - @param Group: The group class (checkbox or radio group class) - """ - - # Create group item controls for each child - for child_name in Group.children_names: - self.Add( - child_name, - # Use the class factory - Group.ItemClass(Group.tag, Group), - mkattr) - - - def AddControls(self, controls, mkattr=True): - """ - Adds controls from a dictionary. - The dictionary key is the control name and the value is a Form.Control object - @param controls: The control dictionary - """ - for name, ctrl in controls.items(): - # Add the control - self.Add(name, ctrl, mkattr) - - - def CompileEx(self, form): - """ - Low level function. - Compiles (parses the form syntax and adds the control) the form string and - returns the argument list to be passed the argument list to AskUsingForm(). - - The form controls are wrapped inside curly braces: {ControlName}. - - A special operator can be used to return the ID of a given control by its name: {id:ControlName}. - This is useful when you use the STARTITEM form keyword to set the initially focused control. - - @param form: Compiles the form and returns the arguments needed to be passed to AskUsingForm() - """ - # First argument is the form string - args = [None] - - # Second argument, if form is not modal, is the set of flags - if not self.modal: - args.append(self.openform_flags | 0x80) # Add FORM_QWIDGET - - ctrlcnt = 1 - - # Reset all group control internal flags - for ctrl in self.__controls.values(): - if isinstance(ctrl, Form.GroupControl): - ctrl._reset() - - p = 0 - while True: - i1 = form.find("{", p) - # No more items? - if i1 == -1: - break - - # Check if escaped - if (i1 != 0) and form[i1-1] == "\\": - # Remove escape sequence and restart search - form = form[:i1-1] + form[i1:] - - # Skip current marker - p = i1 - - # Continue search - continue - - i2 = form.find("}", i1) - if i2 == -1: - raise SyntaxError("No matching closing brace '}'") - - # Parse control name - ctrlname = form[i1+1:i2] - if not ctrlname: - raise ValueError("Control %d has an invalid name!" % ctrlcnt) - - # Is it the IDOF operator? - if ctrlname.startswith("id:"): - idfunc = True - # Take actual ctrlname - ctrlname = ctrlname[3:] - else: - idfunc = False - - # Find the control - ctrl = self.__controls.get(ctrlname, None) - if ctrl is None: - raise ValueError("No matching control '%s'" % ctrlname) - - # Replace control name by tag - if idfunc: - tag = str(ctrl.id) - else: - tag = ctrl.get_tag() - taglen = len(tag) - form = form[:i1] + tag + form[i2+1:] - - # Set new position - p = i1 + taglen - - # Was it an IDOF() ? No need to push parameters - # Just ID substitution is fine - if idfunc: - continue - - - # For GroupItem controls, there are no individual arguments - # The argument is assigned for the group itself - if isinstance(ctrl, Form.GroupItemControl): - # GroupItem controls will have their position dynamically set - ctrl.assign_pos() - else: - # Push argument(s) - # (Some controls need more than one argument) - arg = ctrl.get_arg() - if isinstance(arg, (types.ListType, types.TupleType)): - # Push all args - args.extend(arg) - else: - # Push one arg - args.append(arg) - - ctrlcnt += 1 - - # If no FormChangeCb instance was passed, and thus there's no '%/' - # in the resulting form string, let's provide a minimal one, so that - # we will retrieve 'p_fa', and thus actions that rely on it will work. - if form.find(Form.FT_FORMCHG) < 0: - form = form + Form.FT_FORMCHG - fccb = Form.FormChangeCb(lambda *args: 1) - self.Add("___dummyfchgcb", fccb) - # Regardless of the actual position of '%/' in the form - # string, a formchange callback _must_ be right after - # the form string. - if self.modal: - inspos = 1 - else: - inspos = 2 - args.insert(inspos, fccb.get_arg()) - - # Patch in the final form string - args[0] = form - - self.title = self._ParseFormTitle(form) - return args - - - def Compile(self): - """ - Compiles a form and returns the form object (self) and the argument list. - The form object will contain object names corresponding to the form elements - - @return: It will raise an exception on failure. Otherwise the return value is ignored - """ - - # Reset controls - self._reset() - - # Insert controls - self.AddControls(self.controls) - - # Compile form and get args - self.__args = self.CompileEx(self.form) - - return (self, self.__args) - - - def Compiled(self): - """ - Checks if the form has already been compiled - - @return: Boolean - """ - return self.__args is not None - - - def _ChkCompiled(self): - if not self.Compiled(): - raise SyntaxError("Form is not compiled") - - - def Execute(self): - """ - Displays a modal dialog containing the compiled form. - @return: 1 - ok ; 0 - cancel - """ - self._ChkCompiled() - if not self.modal: - raise SyntaxError("Form is not modal. Open() should be instead") - - return AskUsingForm(*self.__args) - - - def Open(self): - """ - Opens a widget containing the compiled form. - """ - self._ChkCompiled() - if self.modal: - raise SyntaxError("Form is modal. Execute() should be instead") - - OpenForm(*self.__args) - - - def EnableField(self, ctrl, enable): - """ - Enable or disable an input field - @return: False - no such control - """ - return _idaapi.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) - - - def ShowField(self, ctrl, show): - """ - Show or hide an input field - @return: False - no such control - """ - return _idaapi.formchgcbfa_show_field(self.p_fa, ctrl.id, show) - - - def MoveField(self, ctrl, x, y, w, h): - """ - Move/resize an input field - - @return: False - no such fiel - """ - return _idaapi.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) - - - def GetFocusedField(self): - """ - Get currently focused input field. - @return: None if no field is selected otherwise the control ID - """ - id = _idaapi.formchgcbfa_get_focused_field(self.p_fa) - return self.FindControlById(id) - - - def SetFocusedField(self, ctrl): - """ - Set currently focused input field - @return: False - no such control - """ - return _idaapi.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) - - - def RefreshField(self, ctrl): - """ - Refresh a field - @return: False - no such control - """ - return _idaapi.formchgcbfa_refresh_field(self.p_fa, ctrl.id) - - - def Close(self, close_normally): - """ - Close the form - @param close_normally: - 1: form is closed normally as if the user pressed Enter - 0: form is closed abnormally as if the user pressed Esc - @return: None - """ - return _idaapi.formchgcbfa_close(self.p_fa, close_normally) - - - def GetControlValue(self, ctrl): - """ - Returns the control's value depending on its type - @param ctrl: Form control instance - @return: - - color button, radio controls: integer - - file/dir input, string input and string label: string - - embedded chooser control (0-based indices of selected items): integer list - - for multilinetext control: textctrl_info_t - - dropdown list controls: string (when editable) or index (when readonly) - - None: on failure - """ - tid, sz = self.ControlToFieldTypeIdAndSize(ctrl) - r = _idaapi.formchgcbfa_get_field_value( - self.p_fa, - ctrl.id, - tid, - sz) - # Multilinetext? Unpack the tuple into a new textctrl_info_t instance - if r is not None and tid == 7: - return textctrl_info_t(text=r[0], flags=r[1], tabsize=r[2]) - else: - return r - - - def SetControlValue(self, ctrl, value): - """ - Set the control's value depending on its type - @param ctrl: Form control instance - @param value: - - embedded chooser: a 0-base indices list to select embedded chooser items - - multilinetext: a textctrl_info_t - - dropdown list: an integer designating the selection index if readonly - a string designating the edit control value if not readonly - @return: Boolean true on success - """ - tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) - return _idaapi.formchgcbfa_set_field_value( - self.p_fa, - ctrl.id, - tid, - value) - - - @staticmethod - def ControlToFieldTypeIdAndSize(ctrl): - """ - Converts a control object to a tuple containing the field id - and the associated buffer size - """ - # Input control depend on the associated buffer size (supplied by the user) - - # Make sure you check instances types taking into account inheritance - if isinstance(ctrl, Form.DropdownListControl): - return (8, 1 if ctrl.readonly else 0) - elif isinstance(ctrl, Form.MultiLineTextControl): - return (7, 0) - elif isinstance(ctrl, Form.EmbeddedChooserControl): - return (5, 0) - # Group items or controls - elif isinstance(ctrl, (Form.GroupItemControl, Form.GroupControl)): - return (2, 0) - elif isinstance(ctrl, Form.StringLabel): - return (3, min(_idaapi.MAXSTR, ctrl.size)) - elif isinstance(ctrl, Form.ColorInput): - return (4, 0) - elif isinstance(ctrl, Form.NumericInput): - # Pass the numeric control type - return (6, ord(ctrl.tp[0])) - elif isinstance(ctrl, Form.InputControl): - return (1, ctrl.size) - else: - raise NotImplementedError, "Not yet implemented" - -# -------------------------------------------------------------------------- -# Instantiate AskUsingForm function pointer -try: - import ctypes - # Setup the numeric argument size - Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL - AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm()) - OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_OpenForm()) -except: - def AskUsingForm__(*args): - warning("AskUsingForm() needs ctypes library in order to work") - return 0 - def OpenForm__(*args): - warning("OpenForm() needs ctypes library in order to work") - - -def AskUsingForm(*args): - """ - Calls AskUsingForm() - @param: Compiled Arguments obtain through the Form.Compile() function - @return: 1 = ok, 0 = cancel - """ - old = set_script_timeout(0) - r = AskUsingForm__(*args) - set_script_timeout(old) - return r - -def OpenForm(*args): - """ - Calls OpenForm() - @param: Compiled Arguments obtain through the Form.Compile() function - """ - old = set_script_timeout(0) - r = OpenForm__(*args) - set_script_timeout(old) - - -# - -# -# -------------------------------------------------------------------------- -class TestEmbeddedChooserClass(Choose2): - """ - A simple chooser to be used as an embedded chooser - """ - def __init__(self, title, nb = 5, flags=0): - Choose2.__init__(self, - title, - [ ["Address", 10], ["Name", 30] ], - embedded=True, width=30, height=20, flags=flags) - self.n = 0 - self.items = [ self.make_item() for x in xrange(0, nb+1) ] - self.icon = 5 - self.selcount = 0 - - def make_item(self): - r = [str(self.n), "func_%04d" % self.n] - self.n += 1 - return r - - def OnClose(self): - pass - - def OnGetLine(self, n): - print("getline %d" % n) - return self.items[n] - - def OnGetSize(self): - n = len(self.items) - print("getsize -> %d" % n) - return n - -# -------------------------------------------------------------------------- -class MyForm(Form): - def __init__(self): - self.invert = False - self.EChooser = TestEmbeddedChooserClass("E1", flags=Choose2.CH_MULTI) - Form.__init__(self, r"""STARTITEM {id:rNormal} -BUTTON YES* Yeah -BUTTON NO Nope -BUTTON CANCEL Nevermind -Form Test - -{FormChangeCb} -This is a string: +{cStr1}+ -This is an address: +{cAddr1}+ - -Escape\{control} -This is a string: '{cStr2}' -This is a number: {cVal1} - -<#Hint1#Enter name:{iStr1}> -<#Hint2#Select color:{iColor1}> -Browse test -<#Select a file to open#Browse to open:{iFileOpen}> -<#Select a file to save#Browse to save:{iFileSave}> -<#Select dir#Browse for dir:{iDir}> -Type -<#Select type#Write a type:{iType}> -Numbers -<##Enter a selector value:{iSegment}> -<##Enter a raw hex:{iRawHex}> -<##Enter a character:{iChar}> -<##Enter an address:{iAddr}> -Button test -<##Button1:{iButton1}> <##Button2:{iButton2}> - -Check boxes: - - -{cGroup1}> - -Radio boxes: - - -{cGroup2}> - -The end! -""", { - 'cStr1': Form.StringLabel("Hello"), - 'cStr2': Form.StringLabel("StringTest"), - 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR), - 'cVal1' : Form.NumericLabel(99, Form.FT_HEX), - 'iStr1': Form.StringInput(), - 'iColor1': Form.ColorInput(), - 'iFileOpen': Form.FileInput(open=True), - 'iFileSave': Form.FileInput(save=True), - 'iDir': Form.DirInput(), - 'iType': Form.StringInput(tp=Form.FT_TYPE), - 'iSegment': Form.NumericInput(tp=Form.FT_SEG), - 'iRawHex': Form.NumericInput(tp=Form.FT_RAWHEX), - 'iAddr': Form.NumericInput(tp=Form.FT_ADDR), - 'iChar': Form.NumericInput(tp=Form.FT_CHAR), - 'iButton1': Form.ButtonInput(self.OnButton1), - 'iButton2': Form.ButtonInput(self.OnButton2), - 'cGroup1': Form.ChkGroupControl(("rNormal", "rError", "rWarnings")), - 'cGroup2': Form.RadGroupControl(("rRed", "rGreen", "rBlue")), - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) - }) - - - def OnButton1(self, code=0): - print("Button1 pressed") - - - def OnButton2(self, code=0): - print("Button2 pressed") - - - def OnFormChange(self, fid): - if fid == self.iButton1.id: - print("Button1 fchg;inv=%s" % self.invert) - self.SetFocusedField(self.rNormal) - self.EnableField(self.rError, self.invert) - self.invert = not self.invert - elif fid == self.iButton2.id: - g1 = self.GetControlValue(self.cGroup1) - g2 = self.GetControlValue(self.cGroup2) - d = self.GetControlValue(self.iDir) - f = self.GetControlValue(self.iFileOpen) - print("cGroup2:%x;Dir=%s;fopen=%s;cGroup1:%x" % (g1, d, f, g2)) - elif fid == self.cEChooser.id: - l = self.GetControlValue(self.cEChooser) - print("Chooser: %s" % l) - else: - print(">>fid:%d" % fid) - return 1 - - - -# -------------------------------------------------------------------------- -def stdalone_main(): - f = MyForm() - f, args = f.Compile() - print args[0] - print args[1:] - f.rNormal.checked = True - f.rWarnings.checked = True - print hex(f.cGroup1.value) - - f.rGreen.selected = True - print f.cGroup2.value - print "Title: '%s'" % f.title - - f.Free() - -# -------------------------------------------------------------------------- -def ida_main(): - # Create form - global f - f = MyForm() - - # Compile (in order to populate the controls) - f.Compile() - - f.iColor1.value = 0x5bffff - f.iDir.value = os.getcwd() - f.rNormal.checked = True - f.rWarnings.checked = True - f.rGreen.selected = True - f.iStr1.value = "Hello" - f.iFileSave.value = "*.*" - f.iFileOpen.value = "*.*" - # Execute the form - ok = f.Execute() - print("r=%d" % ok) - if ok == 1: - print("f.str1=%s" % f.iStr1.value) - print("f.color1=%x" % f.iColor1.value) - print("f.openfile=%s" % f.iFileOpen.value) - print("f.savefile=%s" % f.iFileSave.value) - print("f.dir=%s" % f.iDir.value) - print("f.type=%s" % f.iType.value) - print("f.seg=%s" % f.iSegment.value) - print("f.rawhex=%x" % f.iRawHex.value) - print("f.char=%x" % f.iChar.value) - print("f.addr=%x" % f.iAddr.value) - print("f.cGroup1=%x" % f.cGroup1.value) - print("f.cGroup2=%x" % f.cGroup2.value) - - sel = f.EChooser.GetEmbSelection() - if sel is None: - print("No selection") - else: - print("Selection: %s" % sel) - - # Dispose the form - f.Free() - -# -------------------------------------------------------------------------- -def ida_main_legacy(): - # Here we simply show how to use the old style form format using Python - - # Sample form from kernwin.hpp - s = """Sample dialog box - - -This is sample dialog box for %A -using address %$ - -<~E~nter value:N:32:16::> -""" - - # Use either StringArgument or NumericArgument to pass values to the function - num = Form.NumericArgument('N', value=123) - ok = idaapi.AskUsingForm(s, - Form.StringArgument("PyAskUsingForm").arg, - Form.NumericArgument('$', 0x401000).arg, - num.arg) - if ok == 1: - print("You entered: %x" % num.value) - -# -------------------------------------------------------------------------- -def test_multilinetext_legacy(): - # Here we text the multi line text control in legacy mode - - # Sample form from kernwin.hpp - s = """Sample dialog box - -This is sample dialog box - -""" - # Use either StringArgument or NumericArgument to pass values to the function - ti = textctrl_info_t("Some initial value") - ok = idaapi.AskUsingForm(s, pointer(c_void_p.from_address(ti.clink_ptr))) - if ok == 1: - print("You entered: %s" % ti.text) - - del ti - -# -------------------------------------------------------------------------- -class MyForm2(Form): - """Simple Form to test multilinetext and combo box controls""" - def __init__(self): - Form.__init__(self, r"""STARTITEM 0 -BUTTON YES* Yeah -BUTTON NO Nope -BUTTON CANCEL NONE -Form Test - -{FormChangeCb} - -""", { - 'txtMultiLineText': Form.MultiLineTextControl(text="Hello"), - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - }) - - - def OnFormChange(self, fid): - if fid == self.txtMultiLineText.id: - pass - elif fid == -2: - ti = self.GetControlValue(self.txtMultiLineText) - print "ti.text = %s" % ti.text - else: - print(">>fid:%d" % fid) - return 1 - -# -------------------------------------------------------------------------- -def test_multilinetext(execute=True): - """Test the multilinetext and combobox controls""" - f = MyForm2() - f, args = f.Compile() - if execute: - ok = f.Execute() - else: - print args[0] - print args[1:] - ok = 0 - - if ok == 1: - assert f.txtMultiLineText.text == f.txtMultiLineText.value - print f.txtMultiLineText.text - - f.Free() - -# -------------------------------------------------------------------------- -class MyForm3(Form): - """Simple Form to test multilinetext and combo box controls""" - def __init__(self): - self.__n = 0 - Form.__init__(self, -r"""BUTTON YES* Yeah -BUTTON NO Nope -BUTTON CANCEL NONE -Dropdown list test - -{FormChangeCb} - - -""", { - 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), - 'cbReadonly': Form.DropdownListControl( - items=["red", "green", "blue"], - readonly=True, - selval=1), - 'cbEditable': Form.DropdownListControl( - items=["1MB", "2MB", "3MB", "4MB"], - readonly=False, - selval="4MB"), - 'iButtonAddelement': Form.ButtonInput(self.OnButtonNop), - 'iButtonSetIndex': Form.ButtonInput(self.OnButtonNop), - 'iButtonSetString': Form.ButtonInput(self.OnButtonNop), - }) - - - def OnButtonNop(self, code=0): - """Do nothing, we will handle events in the form callback""" - pass - - def OnFormChange(self, fid): - if fid == self.iButtonSetString.id: - s = idc.AskStr("none", "Enter value") - if s: - self.SetControlValue(self.cbEditable, s) - elif fid == self.iButtonSetIndex.id: - s = idc.AskStr("1", "Enter index value:") - if s: - try: - i = int(s) - except: - i = 0 - self.SetControlValue(self.cbReadonly, i) - elif fid == self.iButtonAddelement.id: - # add a value to the string list - self.__n += 1 - self.cbReadonly.add("some text #%d" % self.__n) - # Refresh the control - self.RefreshField(self.cbReadonly) - elif fid == -2: - s = self.GetControlValue(self.cbEditable) - print "user entered: %s" % s - sel_idx = self.GetControlValue(self.cbReadonly) - - return 1 - -# -------------------------------------------------------------------------- -def test_dropdown(execute=True): - """Test the combobox controls, in a modal dialog""" - f = MyForm3() - f, args = f.Compile() - if execute: - ok = f.Execute() - else: - print args[0] - print args[1:] - ok = 0 - - if ok == 1: - print "Editable: %s" % f.cbEditable.value - print "Readonly: %s" % f.cbReadonly.value - - f.Free() - -# -------------------------------------------------------------------------- -tdn_form = None -def test_dropdown_nomodal(): - """Test the combobox controls, in a non-modal form""" - global tdn_form - if tdn_form is None: - tdn_form = MyForm3() - tdn_form.modal = False - tdn_form.openform_flags = idaapi.PluginForm.FORM_TAB - tdn_form, _ = tdn_form.Compile() - tdn_form.Open() - -# -# -------------------------------------------------------------------------- - -# -# -------------------------------------------------------------------------- -class MainChooserClass(Choose2): - def __init__(self, title, icon): - Choose2.__init__(self, - title, - [ ["Item", 10] ], - icon=icon, - flags=Choose2.CH_NOIDB, - embedded=True, width=30, height=20) - - def OnClose(self): - pass - - def OnGetLine(self, n): - return ["Option %d" % n] - - def OnGetSize(self): - return 10 - - def OnCommand(self, n, cmd_id): - if cmd_id == self.cmd_id1: - print("Context menu on: %d" % n) - - return 0 - - -# -------------------------------------------------------------------------- -class AuxChooserClass(Choose2): - def __init__(self, title, icon): - Choose2.__init__(self, - title, - [ ["Item", 10] ], - icon=icon, - flags=Choose2.CH_NOIDB | Choose2.CH_MULTI, - embedded=True, width=30, height=20) - - def OnClose(self): - pass - - def OnGetLine(self, n): - return ["Item %d" % n] - - def OnGetSize(self): - t = self.form.main_current_index - return 0 if t < 0 else t+1 - - -# -------------------------------------------------------------------------- -class MyChooserForm(Form): - - # Custom icon data - icon_data = ( - "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" - "\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1F\xF3\xFF" - "\x61\x00\x00\x00\x7D\x49\x44\x41\x54\x78\xDA\x63\x64\xC0\x0E\xFE" - "\xE3\x10\x67\x24\x28\x00\xD2\xFC\xF3\xAF\x36\x56\xDD\xEC\xCC\x57" - "\x31\xF4\x20\x73\xC0\xB6\xE2\xD2\x8C\x66\x08\x5C\x2F\x8A\x01\x84" - "\x34\x63\x73\x09\x23\xA9\x9A\xD1\x0D\x61\x44\xD7\xCC\xCF\x02\x71" - "\xE2\xC7\x3F\xA8\x06\x62\x13\x07\x19\x42\x7D\x03\x48\xF5\xC6\x20" - "\x34\x00\xE4\x57\x74\xFF\xE3\x92\x83\x19\xC0\x40\x8C\x21\xD8\x34" - "\x33\x40\xA3\x91\x01\x97\x21\xC8\x00\x9B\x66\x38\x01\x33\x00\x44" - "\x50\x92\x94\xB1\xBA\x04\x8B\x66\x9C\x99\x09\xC5\x10\x1C\xE2\x18" - "\xEA\x01\xA3\x65\x55\x0B\x33\x14\x07\x63\x00\x00\x00\x00\x49\x45" - "\x4E\x44\xAE\x42\x60\x82") - - - def Free(self): - # Call the base - Form.Free(self) - - # Free icon - if self.icon_id != 0: - idaapi.free_custom_icon(self.icon_id) - self.icon_id = 0 - - - def __init__(self): - # Load custom icon - self.icon_id = idaapi.load_custom_icon(data=MyChooserForm.icon_data) - if self.icon_id == 0: - raise RuntimeError("Failed to load icon data!") - - self.main_current_index = -1 - self.EChMain = MainChooserClass("MainChooser", self.icon_id) - self.EChAux = AuxChooserClass("AuxChooser", self.icon_id) - - # Link the form to the EChooser - self.EChMain.form = self - self.EChAux.form = self - - Form.__init__(self, r"""STARTITEM 0 -Form with choosers - - {FormChangeCb} - Select an item in the main chooser: - -
- - - - -""", { - 'ctrlSelectionEdit' : Form.StringInput(), - 'FormChangeCb' : Form.FormChangeCb(self.OnFormChange), - 'ctrlMainChooser' : Form.EmbeddedChooserControl(self.EChMain), - 'ctrlAuxChooser' : Form.EmbeddedChooserControl(self.EChAux), - }) - - - def refresh_selection_edit(self): - if self.main_current_index < 0: - s = "No selection in the main chooser" - else: - s = "Main %d" % self.main_current_index - - # Get selection in the aux chooser - sel = self.GetControlValue(self.ctrlAuxChooser) - if sel: - s = "%s - Aux item(s): %s" % (s, ",".join(str(x) for x in sel)) - - # Update string input - self.SetControlValue(self.ctrlSelectionEdit, s) - - - def OnFormChange(self, fid): - if fid == -1: - print("Initialization") - self.refresh_selection_edit() - - # Add an item to the context menu of the main chooser - id = self.ctrlMainChooser.AddCommand("Test", icon=self.icon_id) - print "id=%d" % id - if id < 0: - print("Failed to install menu for main embedded chooser") - else: - self.EChMain.cmd_id1 = id - - elif fid == -2: - print("Terminating"); - - elif fid == self.ctrlMainChooser.id: - print("main chooser selection change"); - l = self.GetControlValue(self.ctrlMainChooser); - if not l: - self.main_current_index = -1 - else: - self.main_current_index = l[0] - - # Refresh auxiliar chooser - self.RefreshField(self.ctrlAuxChooser) - self.refresh_selection_edit() - - elif fid == self.ctrlAuxChooser.id: - self.refresh_selection_edit() - - elif fid == self.ctrlSelectionEdit.id: - pass - else: - print("unknown id %d" % fid) - - return 1 - -# - -# -------------------------------------------------------------------------- -def main_formchooser(): - global f - f = MyChooserForm() - try: - f.Compile() - r = f.Execute() - print("Execute returned: %d" % r) - f.Free() - except Exception as e: - print("Failed to show form: %s" % str(e)) - -# -------------------------------------------------------------------------- -if __name__=='__main__': - #stdalone_main() if stdalone else main_formchooser() - #stdalone_main() if stdalone else test_multilinetext() - test_dropdown() - #test_multilinetext(False) - +# -------------------------------------------------------------------------- +import os +import gc +import sys + +try: + import _idaapi + from _idaapi import set_script_timeout + import idaapi + from idaapi import py_clinked_object_t + from idaapi import _qstrvec_t + stdalone = False +except: + stdalone = True + +print("Standalone: %s" % stdalone) + +pywraps_there = False + +try: + if stdalone: + class object_t(object): pass + class py_clinked_object_t(object): pass + _idaapi = object_t() + _idaapi.choose2_get_embedded = lambda obj: (id(obj), 0) + _idaapi.choose2_get_embedded_selection = lambda obj: None + + _idaapi.textctrl_info_t_set_text = lambda self, v: None + _idaapi.CHOOSER_POPUP_MENU = 1 + pywraps = object_t() + pywraps.py_get_AskUsingForm = lambda: 0 + pywraps.py_get_OpenForm = lambda: 0 + + class Choose2(object): + CH_MULTI = 1 + def __init__(self, title, cols, flags=0, popup_names=None, + icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, + embedded=False, width=None, height=None): + pass + + def Close(self): + print("Chooser closing...") + + Embedded = lambda self: 1 + + set_script_timeout = lambda x: x + + else: + import pywraps + pywraps_there = True + from py_choose2 import * + py_clinked_object_t = idaapi.py_clinked_object_t + textctrl_info_t = idaapi.textctrl_info_t + _qstrvec_t = idaapi._qstrvec_t + + _idaapi.BADADDR = 0xFFFFFFFF + _idaapi.MAXSTR = 1024 + set_script_timeout = _idaapi.set_script_timeout + + if not hasattr(_idaapi, 'formchgcbfa_enable_field'): + _idaapi.formchgcbfa_enable_field = pywraps.py_formchgcbfa_enable_field + +except Exception as e: + pass +# print("exception: %s" % str(e)) + + +print("Using PyWraps: %s" % pywraps_there) + +# -------------------------------------------------------------------------- +# +#ICON WARNING|QUESTION|INFO|NONE +#AUTOHIDE NONE|DATABASE|REGISTRY|SESSION +#HIDECANCEL +#BUTTON YES|NO|CANCEL "Value|NONE" +#STARTITEM {id:ItemName} +#HELP / ENDHELP +try: + import types + from ctypes import * + # On Windows, we use stdcall + + # Callback for buttons + # typedef void (idaapi *formcb_t)(TView *fields[], int code); + + _FORMCB_T = WINFUNCTYPE(None, c_void_p, c_int) + + # Callback for form change + # typedef int (idaapi *formchgcb_t)(int field_id, form_actions_t &fa); + _FORMCHGCB_T = WINFUNCTYPE(c_int, c_int, c_void_p) +except: + try: + _FORMCB_T = CFUNCTYPE(None, c_void_p, c_int) + _FORMCHGCB_T = CFUNCTYPE(c_int, c_int, c_void_p) + except: + _FORMCHGCB_T = _FORMCB_T = None + + +# ----------------------------------------------------------------------- +# textctrl_info_t clinked object +class textctrl_info_t(py_clinked_object_t): + """Class representing textctrl_info_t""" + + # Some constants + TXTF_AUTOINDENT = 0x0001 + """Auto-indent on new line""" + TXTF_ACCEPTTABS = 0x0002 + """Tab key inserts 'tabsize' spaces""" + TXTF_READONLY = 0x0004 + """Text cannot be edited (but can be selected and copied)""" + TXTF_SELECTED = 0x0008 + """Shows the field with its text selected""" + TXTF_MODIFIED = 0x0010 + """Gets/sets the modified status""" + TXTF_FIXEDFONT = 0x0020 + """The control uses IDA's fixed font""" + + def __init__(self, text="", flags=0, tabsize=0): + py_clinked_object_t.__init__(self) + if text: + self.text = text + if flags: + self.flags = flags + if tabsize: + self.tabsize = tabsize + + def _create_clink(self): + return _idaapi.textctrl_info_t_create() + + def _del_clink(self, lnk): + return _idaapi.textctrl_info_t_destroy(lnk) + + def _get_clink_ptr(self): + return _idaapi.textctrl_info_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.textctrl_info_t_assign(self, other) + + def __set_text(self, s): + """Sets the text value""" + return _idaapi.textctrl_info_t_set_text(self, s) + + def __get_text(self): + """Sets the text value""" + return _idaapi.textctrl_info_t_get_text(self) + + def __set_flags__(self, flags): + """Sets the flags value""" + return _idaapi.textctrl_info_t_set_flags(self, flags) + + def __get_flags__(self): + """Returns the flags value""" + return _idaapi.textctrl_info_t_get_flags(self) + + def __set_tabsize__(self, tabsize): + """Sets the tabsize value""" + return _idaapi.textctrl_info_t_set_tabsize(self, tabsize) + + def __get_tabsize__(self): + """Returns the tabsize value""" + return _idaapi.textctrl_info_t_get_tabsize(self) + + value = property(__get_text, __set_text) + """Alias for the text property""" + text = property(__get_text, __set_text) + """Text value""" + flags = property(__get_flags__, __set_flags__) + """Flags value""" + tabsize = property(__get_tabsize__, __set_tabsize__) + +# ----------------------------------------------------------------------- +class Form(object): + + FT_ASCII = 'A' + """Ascii string - char *""" + FT_SEG = 'S' + """Segment - sel_t *""" + FT_HEX = 'N' + """Hex number - uval_t *""" + FT_SHEX = 'n' + """Signed hex number - sval_t *""" + FT_COLOR = 'K' + """Color button - bgcolor_t *""" + FT_ADDR = '$' + """Address - ea_t *""" + FT_UINT64 = 'L' + """default base uint64 - uint64""" + FT_INT64 = 'l' + """default base int64 - int64""" + FT_RAWHEX = 'M' + """Hex number, no 0x prefix - uval_t *""" + FT_FILE = 'f' + """File browse - char * at least QMAXPATH""" + FT_DEC = 'D' + """Decimal number - sval_t *""" + FT_OCT = 'O' + """Octal number, C notation - sval_t *""" + FT_BIN = 'Y' + """Binary number, 0b prefix - sval_t *""" + FT_CHAR = 'H' + """Char value -- sval_t *""" + FT_IDENT = 'I' + """Identifier - char * at least MAXNAMELEN""" + FT_BUTTON = 'B' + """Button - def handler(code)""" + FT_DIR = 'F' + """Path to directory - char * at least QMAXPATH""" + FT_TYPE = 'T' + """Type declaration - char * at least MAXSTR""" + _FT_USHORT = '_US' + """Unsigned short""" + FT_FORMCHG = '%/' + """Form change callback - formchgcb_t""" + FT_ECHOOSER = 'E' + """Embedded chooser - idaapi.Choose2""" + FT_MULTI_LINE_TEXT = 't' + """Multi text control - textctrl_info_t""" + FT_DROPDOWN_LIST = 'b' + """Dropdown list control - Form.DropdownControl""" + + FT_CHKGRP = 'C' + FT_CHKGRP2= 'c' + FT_RADGRP = 'R' + FT_RADGRP2= 'r' + + @staticmethod + def fieldtype_to_ctype(tp, i64 = False): + """ + Factory method returning a ctype class corresponding to the field type string + """ + if tp in (Form.FT_SEG, Form.FT_HEX, Form.FT_RAWHEX, Form.FT_ADDR): + return c_ulonglong if i64 else c_ulong + elif tp in (Form.FT_SHEX, Form.FT_DEC, Form.FT_OCT, Form.FT_BIN, Form.FT_CHAR): + return c_longlong if i64 else c_long + elif tp == Form.FT_UINT64: + return c_ulonglong + elif tp == Form.FT_INT64: + return c_longlong + elif tp == Form.FT_COLOR: + return c_ulong + elif tp == Form._FT_USHORT: + return c_ushort + elif tp in (Form.FT_FORMCHG, Form.FT_ECHOOSER): + return c_void_p + else: + return None + + + # + # Generic argument helper classes + # + class NumericArgument(object): + """ + Argument representing various integer arguments (ushort, uint32, uint64, etc...) + @param tp: One of Form.FT_XXX + """ + DefI64 = False + def __init__(self, tp, value): + cls = Form.fieldtype_to_ctype(tp, self.DefI64) + if cls is None: + raise TypeError("Invalid numeric field type: %s" % tp) + # Get a pointer type to the ctype type + self.arg = pointer(cls(value)) + + def __set_value(self, v): + self.arg.contents.value = v + value = property(lambda self: self.arg.contents.value, __set_value) + + + class StringArgument(object): + """ + Argument representing a character buffer + """ + def __init__(self, size=None, value=None): + if size is None: + raise SyntaxError("The string size must be passed") + + if value is None: + self.arg = create_string_buffer(size) + else: + self.arg = create_string_buffer(value, size) + self.size = size + + def __set_value(self, v): + self.arg.value = v + value = property(lambda self: self.arg.value, __set_value) + + + # + # Base control class + # + class Control(object): + def __init__(self): + self.id = 0 + """Automatically assigned control ID""" + + self.arg = None + """Control argument value. This could be one element or a list/tuple (for multiple args per control)""" + + self.form = None + """Reference to the parent form. It is filled by Form.Add()""" + + + def get_tag(self): + """ + Control tag character. One of Form.FT_XXXX. + The form class will expand the {} notation and replace them with the tags + """ + pass + + def get_arg(self): + """ + Control returns the parameter to be pushed on the stack + (Of AskUsingForm()) + """ + return self.arg + + def free(self): + """ + Free the control + """ + # Release the parent form reference + self.form = None + + + # + # Label controls + # + class LabelControl(Control): + """ + Base class for static label control + """ + def __init__(self, tp): + Form.Control.__init__(self) + self.tp = tp + + def get_tag(self): + return '%%%d%s' % (self.id, self.tp) + + + class StringLabel(LabelControl): + """ + String label control + """ + def __init__(self, value, tp=None, sz=1024): + """ + Type field can be one of: + A - ascii string + T - type declaration + I - ident + F - folder + f - file + X - command + """ + if tp is None: + tp = Form.FT_ASCII + Form.LabelControl.__init__(self, tp) + self.size = sz + self.arg = create_string_buffer(value, sz) + + + class NumericLabel(LabelControl, NumericArgument): + """ + Numeric label control + """ + def __init__(self, value, tp=None): + if tp is None: + tp = Form.FT_HEX + Form.LabelControl.__init__(self, tp) + Form.NumericArgument.__init__(self, tp, value) + + + # + # Group controls + # + class GroupItemControl(Control): + """ + Base class for group control items + """ + def __init__(self, tag, parent): + Form.Control.__init__(self) + self.tag = tag + self.parent = parent + # Item position (filled when form is compiled) + self.pos = 0 + + def assign_pos(self): + self.pos = self.parent.next_child_pos() + + def get_tag(self): + return "%s%d" % (self.tag, self.id) + + + class ChkGroupItemControl(GroupItemControl): + """ + Checkbox group item control + """ + def __init__(self, tag, parent): + Form.GroupItemControl.__init__(self, tag, parent) + + def __get_value(self): + return (self.parent.value & (1 << self.pos)) != 0 + + def __set_value(self, v): + pv = self.parent.value + if v: + pv = pv | (1 << self.pos) + else: + pv = pv & ~(1 << self.pos) + + self.parent.value = pv + + checked = property(__get_value, __set_value) + """Get/Sets checkbox item check status""" + + + class RadGroupItemControl(GroupItemControl): + """ + Radiobox group item control + """ + def __init__(self, tag, parent): + Form.GroupItemControl.__init__(self, tag, parent) + + def __get_value(self): + return self.parent.value == self.pos + + def __set_value(self, v): + self.parent.value = self.pos + + selected = property(__get_value, __set_value) + """Get/Sets radiobox item selection status""" + + + class GroupControl(Control, NumericArgument): + """ + Base class for group controls + """ + def __init__(self, children_names, tag, value=0): + Form.Control.__init__(self) + self.children_names = children_names + self.tag = tag + self._reset() + Form.NumericArgument.__init__(self, Form._FT_USHORT, value) + + def _reset(self): + self.childpos = 0 + + def next_child_pos(self): + v = self.childpos + self.childpos += 1 + return v + + def get_tag(self): + return "%d" % self.id + + + class ChkGroupControl(GroupControl): + """ + Checkbox group control class. + It holds a set of checkbox controls + """ + ItemClass = None + """ + Group control item factory class instance + We need this because later we won't be treating ChkGroupControl or RadGroupControl + individually, instead we will be working with GroupControl in general. + """ + def __init__(self, children_names, value=0, secondary=False): + # Assign group item factory class + if Form.ChkGroupControl.ItemClass is None: + Form.ChkGroupControl.ItemClass = Form.ChkGroupItemControl + + Form.GroupControl.__init__( + self, + children_names, + Form.FT_CHKGRP2 if secondary else Form.FT_CHKGRP, + value) + + + class RadGroupControl(GroupControl): + """ + Radiobox group control class. + It holds a set of radiobox controls + """ + ItemClass = None + def __init__(self, children_names, value=0, secondary=False): + """ + Creates a radiogroup control. + @param children_names: A tuple containing group item names + @param value: Initial selected radio item + @param secondory: Allows rendering one the same line as the previous group control. + Use this if you have another group control on the same line. + """ + # Assign group item factory class + if Form.RadGroupControl.ItemClass is None: + Form.RadGroupControl.ItemClass = Form.RadGroupItemControl + + Form.GroupControl.__init__( + self, + children_names, + Form.FT_RADGRP2 if secondary else Form.FT_RADGRP, + value) + + + # + # Input controls + # + class InputControl(Control): + """ + Generic form input control. + It could be numeric control, string control, directory/file browsing, etc... + """ + def __init__(self, tp, width, swidth, hlp = None): + """ + @param width: Display width + @param swidth: String width + """ + Form.Control.__init__(self) + self.tp = tp + self.width = width + self.switdh = swidth + self.hlp = hlp + + def get_tag(self): + return "%s%d:%s:%s:%s" % ( + self.tp, self.id, + self.width, + self.switdh, + ":" if self.hlp is None else self.hlp) + + + class NumericInput(InputControl, NumericArgument): + """ + A composite class serving as a base numeric input control class + """ + def __init__(self, tp=None, value=0, width=50, swidth=10, hlp=None): + if tp is None: + tp = Form.FT_HEX + Form.InputControl.__init__(self, tp, width, swidth, hlp) + Form.NumericArgument.__init__(self, self.tp, value) + + + class ColorInput(NumericInput): + """ + Color button input control + """ + def __init__(self, value = 0): + """ + @param value: Initial color value in RGB + """ + Form.NumericInput.__init__(self, tp=Form.FT_COLOR, value=value) + + + class StringInput(InputControl, StringArgument): + """ + Base string input control class. + This class also constructs a StringArgument + """ + def __init__(self, + tp=None, + width=1024, + swidth=40, + hlp=None, + value=None, + size=None): + """ + @param width: String size. But in some cases it has special meaning. For example in FileInput control. + If you want to define the string buffer size then pass the 'size' argument + @param swidth: Control width + @param value: Initial value + @param size: String size + """ + if tp is None: + tp = Form.FT_ASCII + if not size: + size = width + Form.InputControl.__init__(self, tp, width, swidth, hlp) + Form.StringArgument.__init__(self, size=size, value=value) + + + class FileInput(StringInput): + """ + File Open/Save input control + """ + def __init__(self, + width=512, + swidth=80, + save=False, open=False, + hlp=None, value=None): + + if save == open: + raise ValueError("Invalid mode. Choose either open or save") + if width < 512: + raise ValueError("Invalid width. Must be greater than 512.") + + # The width field is overloaded in this control and is used + # to denote the type of the FileInput dialog (save or load) + # On the other hand it is passed as is to the StringArgument part + Form.StringInput.__init__( + self, + tp=Form.FT_FILE, + width="1" if save else "0", + swidth=swidth, + hlp=hlp, + size=width, + value=value) + + + class DirInput(StringInput): + """ + Directory browsing control + """ + def __init__(self, + width=512, + swidth=80, + hlp=None, + value=None): + + if width < 512: + raise ValueError("Invalid width. Must be greater than 512.") + + Form.StringInput.__init__( + self, + tp=Form.FT_DIR, + width=width, + swidth=swidth, + hlp=hlp, + size=width, + value=value) + + + class ButtonInput(InputControl): + """ + Button control. + A handler along with a 'code' (numeric value) can be associated with the button. + This way one handler can handle many buttons based on the button code (or in other terms id or tag) + """ + def __init__(self, handler, code="", swidth="", hlp=None): + """ + @param handler: Button handler. A callback taking one argument which is the code. + @param code: A code associated with the button and that is later passed to the handler. + """ + Form.InputControl.__init__( + self, + Form.FT_BUTTON, + code, + swidth, + hlp) + self.arg = _FORMCB_T(lambda view, code, h=handler: h(code)) + + + class FormChangeCb(Control): + """ + Form change handler. + This can be thought of like a dialog procedure. + Everytime a form action occurs, this handler will be called along with the control id. + The programmer can then call various form actions accordingly: + - EnableField + - ShowField + - MoveField + - GetFieldValue + - etc... + + Special control IDs: -1 (The form is initialized) and -2 (Ok has been clicked) + + """ + def __init__(self, handler): + """ + Constructs the handler. + @param handler: The handler (preferrably a member function of a class derived from the Form class). + """ + Form.Control.__init__(self) + + # Save the handler + self.handler = handler + + # Create a callback stub + # We use this mechanism to create an intermediate step + # where we can create an 'fa' adapter for use by Python + self.arg = _FORMCHGCB_T(self.helper_cb) + + def helper_cb(self, fid, p_fa): + # Remember the pointer to the forms_action in the parent form + self.form.p_fa = p_fa + + # Call user's handler + r = self.handler(fid) + return 0 if r is None else r + + def get_tag(self): + return Form.FT_FORMCHG + + def free(self): + Form.Control.free(self) + # Remove reference to the handler + # (Normally the handler is a member function in the parent form) + self.handler = None + + + class EmbeddedChooserControl(InputControl): + """ + Embedded chooser control. + This control links to a Chooser2 control created with the 'embedded=True' + """ + def __init__(self, + chooser=None, + swidth=40, + hlp=None): + """ + Embedded chooser control + + @param chooser: A chooser2 instance (must be constructed with 'embedded=True') + """ + + # !! Make sure a chooser instance is passed !! + if chooser is None or not isinstance(chooser, Choose2): + raise ValueError("Invalid chooser passed.") + + # Create an embedded chooser structure from the Choose2 instance + if chooser.Embedded() != 1: + raise ValueError("Failed to create embedded chooser instance.") + + # Construct input control + Form.InputControl.__init__(self, Form.FT_ECHOOSER, "", swidth) + + # Get a pointer to the chooser_info_t and the selection vector + # (These two parameters are the needed arguments for the AskUsingForm()) + emb, sel = _idaapi.choose2_get_embedded(chooser) + + # Get a pointer to a c_void_p constructed from an address + p_embedded = pointer(c_void_p.from_address(emb)) + p_sel = pointer(c_void_p.from_address(sel)) + + # - Create the embedded chooser info on control creation + # - Do not free the embeded chooser because after we get the args + # via Compile() the user can still call Execute() which relies + # on the already computed args + self.arg = (p_embedded, p_sel) + + # Save chooser instance + self.chooser = chooser + + # Add a bogus 'size' attribute + self.size = 0 + + + value = property(lambda self: self.chooser) + """Returns the embedded chooser instance""" + + + def AddCommand(self, + caption, + flags = _idaapi.CHOOSER_POPUP_MENU, + menu_index = -1, + icon = -1): + """ + Adds a new embedded chooser command + Save the returned value and later use it in the OnCommand handler + + @return: Returns a negative value on failure or the command index + """ + if not self.form.title: + raise ValueError("Form title is not set!") + + # Encode all information for the AddCommand() in the 'caption' parameter + caption = "%s:%d:%s" % (self.form.title, self.id, caption) + return self.chooser.AddCommand(caption, flags=flags, menu_index=menu_index, icon=icon, emb=2002) + + + def free(self): + """ + Frees the embedded chooser data + """ + self.chooser.Close() + self.chooser = None + Form.Control.free(self) + + + class DropdownListControl(InputControl, _qstrvec_t): + """ + Dropdown control + This control allows manipulating a dropdown control + """ + def __init__(self, items=[], readonly=True, selval=0, width=50, swidth=50, hlp = None): + """ + @param items: A string list of items used to prepopulate the control + @param readonly: Specifies whether the dropdown list is editable or not + @param selval: The preselected item index (when readonly) or text value (when editable) + @param width: the control width (n/a if the dropdown list is readonly) + @param swidth: string width + """ + + # Ignore the width if readonly was set + if readonly: + width = 0 + + # Init the input control base class + Form.InputControl.__init__( + self, + Form.FT_DROPDOWN_LIST, + width, + swidth, + hlp) + + # Init the associated qstrvec + _qstrvec_t.__init__(self, items) + + # Remember if readonly or not + self.readonly = readonly + + if readonly: + # Create a C integer and remember it + self.__selval = c_int(selval) + val_addr = addressof(self.__selval) + else: + # Create an strvec with one qstring + self.__selval = _qstrvec_t([selval]) + # Get address of the first element + val_addr = self.__selval.addressof(0) + + # Two arguments: + # - argument #1: a pointer to the qstrvec containing the items + # - argument #2: an integer to hold the selection + # or + # #2: a qstring to hold the dropdown text control value + self.arg = ( + pointer(c_void_p.from_address(self.clink_ptr)), + pointer(c_void_p.from_address(val_addr)) + ) + + + def __set_selval(self, val): + if self.readonly: + self.__selval.value = val + else: + self.__selval[0] = val + + def __get_selval(self): + # Return the selection index + # or the entered text value + return self.__selval.value if self.readonly else self.__selval[0] + + value = property(__get_selval, __set_selval) + selval = property(__get_selval, __set_selval) + """ + Read/write the selection value. + The value is used as an item index in readonly mode or text value in editable mode + This value can be used only after the form has been closed. + """ + + def free(self): + self._free() + + + def set_items(self, items): + """Sets the dropdown list items""" + self.from_list(items) + + + class MultiLineTextControl(InputControl, textctrl_info_t): + """ + Multi line text control. + This class inherits from textctrl_info_t. Thus the attributes are also inherited + This control allows manipulating a multilinetext control + """ + def __init__(self, text="", flags=0, tabsize=0, width=50, swidth=50, hlp = None): + """ + @param text: Initial text value + @param flags: One of textctrl_info_t.TXTF_.... values + @param tabsize: Tab size + @param width: Display width + @param swidth: String width + """ + # Init the input control base class + Form.InputControl.__init__(self, Form.FT_MULTI_LINE_TEXT, width, swidth, hlp) + + # Init the associated textctrl_info base class + textctrl_info_t.__init__(self, text=text, flags=flags, tabsize=tabsize) + + # Get the argument as a pointer from the embedded ti + self.arg = pointer(c_void_p.from_address(self.clink_ptr)) + + + def free(self): + self._free() + + + # + # Form class + # + def __init__(self, form, controls): + """ + Contruct a Form class. + This class wraps around AskUsingForm() or OpenForm() and provides an easier / alternative syntax for describing forms. + The form control names are wrapped inside the opening and closing curly braces and the control themselves are + defined and instantiated via various form controls (subclasses of Form). + + @param form: The form string + @param controls: A dictionary containing the control name as a _key_ and control object as _value_ + """ + self._reset() + self.form = form + """Form string""" + self.controls = controls + """Dictionary of controls""" + self.__args = None + + self.title = None + """The Form title. It will be filled when the form is compiled""" + + self.modal = True + """By default, forms are modal""" + + self.openform_flags = 0 + """ + If non-modal, these flags will be passed to OpenForm. + This is an OR'ed combination of the PluginForm.FORM_* values. + """ + + + def Free(self): + """ + Frees all resources associated with a compiled form. + Make sure you call this function when you finish using the form. + """ + + # Free all the controls + for ctrl in self.__controls.values(): + ctrl.free() + + # Reset the controls + # (Note that we are not removing the form control attributes, no need) + self._reset() + + # Unregister, so we don't try and free it again at closing-time. + _idaapi.py_unregister_compiled_form(self) + + + def _reset(self): + """ + Resets the Form class state variables + """ + self.__controls = {} + self.__ctrl_id = 1 + + + def __getitem__(self, name): + """Returns a control object by name""" + return self.__controls[name] + + + def Add(self, name, ctrl, mkattr = True): + """ + Low level function. Prefer AddControls() to this function. + This function adds one control to the form. + + @param name: Control name + @param ctrl: Control object + @param mkattr: Create control name / control object as a form attribute + """ + # Assign a unique ID + ctrl.id = self.__ctrl_id + self.__ctrl_id += 1 + + # Create attribute with control name + if mkattr: + setattr(self, name, ctrl) + + # Remember the control + self.__controls[name] = ctrl + + # Link the form to the control via its form attribute + ctrl.form = self + + # Is it a group? Add each child + if isinstance(ctrl, Form.GroupControl): + self._AddGroup(ctrl, mkattr) + + + def FindControlById(self, id): + """ + Finds a control instance given its id + """ + for ctrl in self.__controls.values(): + if ctrl.id == id: + return ctrl + return None + + + @staticmethod + def _ParseFormTitle(form): + """ + Parses the form's title from the form text + """ + help_state = 0 + for i, line in enumerate(form.split("\n")): + if line.startswith("STARTITEM ") or line.startswith("BUTTON "): + continue + # Skip "HELP" and remember state + elif help_state == 0 and line == "HELP": + help_state = 1 # Mark inside HELP + continue + elif help_state == 1 and line == "ENDHELP": + help_state = 2 # Mark end of HELP + continue + return line.strip() + + return None + + + def _AddGroup(self, Group, mkattr=True): + """ + Internal function. + This function expands the group item names and creates individual group item controls + + @param Group: The group class (checkbox or radio group class) + """ + + # Create group item controls for each child + for child_name in Group.children_names: + self.Add( + child_name, + # Use the class factory + Group.ItemClass(Group.tag, Group), + mkattr) + + + def AddControls(self, controls, mkattr=True): + """ + Adds controls from a dictionary. + The dictionary key is the control name and the value is a Form.Control object + @param controls: The control dictionary + """ + for name, ctrl in controls.items(): + # Add the control + self.Add(name, ctrl, mkattr) + + + def CompileEx(self, form): + """ + Low level function. + Compiles (parses the form syntax and adds the control) the form string and + returns the argument list to be passed the argument list to AskUsingForm(). + + The form controls are wrapped inside curly braces: {ControlName}. + + A special operator can be used to return the ID of a given control by its name: {id:ControlName}. + This is useful when you use the STARTITEM form keyword to set the initially focused control. + + @param form: Compiles the form and returns the arguments needed to be passed to AskUsingForm() + """ + # First argument is the form string + args = [None] + + # Second argument, if form is not modal, is the set of flags + if not self.modal: + args.append(self.openform_flags | 0x80) # Add FORM_QWIDGET + + ctrlcnt = 1 + + # Reset all group control internal flags + for ctrl in self.__controls.values(): + if isinstance(ctrl, Form.GroupControl): + ctrl._reset() + + p = 0 + while True: + i1 = form.find("{", p) + # No more items? + if i1 == -1: + break + + # Check if escaped + if (i1 != 0) and form[i1-1] == "\\": + # Remove escape sequence and restart search + form = form[:i1-1] + form[i1:] + + # Skip current marker + p = i1 + + # Continue search + continue + + i2 = form.find("}", i1) + if i2 == -1: + raise SyntaxError("No matching closing brace '}'") + + # Parse control name + ctrlname = form[i1+1:i2] + if not ctrlname: + raise ValueError("Control %d has an invalid name!" % ctrlcnt) + + # Is it the IDOF operator? + if ctrlname.startswith("id:"): + idfunc = True + # Take actual ctrlname + ctrlname = ctrlname[3:] + else: + idfunc = False + + # Find the control + ctrl = self.__controls.get(ctrlname, None) + if ctrl is None: + raise ValueError("No matching control '%s'" % ctrlname) + + # Replace control name by tag + if idfunc: + tag = str(ctrl.id) + else: + tag = ctrl.get_tag() + taglen = len(tag) + form = form[:i1] + tag + form[i2+1:] + + # Set new position + p = i1 + taglen + + # Was it an IDOF() ? No need to push parameters + # Just ID substitution is fine + if idfunc: + continue + + + # For GroupItem controls, there are no individual arguments + # The argument is assigned for the group itself + if isinstance(ctrl, Form.GroupItemControl): + # GroupItem controls will have their position dynamically set + ctrl.assign_pos() + else: + # Push argument(s) + # (Some controls need more than one argument) + arg = ctrl.get_arg() + if isinstance(arg, (types.ListType, types.TupleType)): + # Push all args + args.extend(arg) + else: + # Push one arg + args.append(arg) + + ctrlcnt += 1 + + # If no FormChangeCb instance was passed, and thus there's no '%/' + # in the resulting form string, let's provide a minimal one, so that + # we will retrieve 'p_fa', and thus actions that rely on it will work. + if form.find(Form.FT_FORMCHG) < 0: + form = form + Form.FT_FORMCHG + fccb = Form.FormChangeCb(lambda *args: 1) + self.Add("___dummyfchgcb", fccb) + # Regardless of the actual position of '%/' in the form + # string, a formchange callback _must_ be right after + # the form string. + if self.modal: + inspos = 1 + else: + inspos = 2 + args.insert(inspos, fccb.get_arg()) + + # Patch in the final form string + args[0] = form + + self.title = self._ParseFormTitle(form) + return args + + + def Compile(self): + """ + Compiles a form and returns the form object (self) and the argument list. + The form object will contain object names corresponding to the form elements + + @return: It will raise an exception on failure. Otherwise the return value is ignored + """ + + # Reset controls + self._reset() + + # Insert controls + self.AddControls(self.controls) + + # Compile form and get args + self.__args = self.CompileEx(self.form) + + # Register this form, to make sure it will be freed at closing-time. + _idaapi.py_register_compiled_form(self) + + return (self, self.__args) + + + def Compiled(self): + """ + Checks if the form has already been compiled + + @return: Boolean + """ + return self.__args is not None + + + def _ChkCompiled(self): + if not self.Compiled(): + raise SyntaxError("Form is not compiled") + + + def Execute(self): + """ + Displays a modal dialog containing the compiled form. + @return: 1 - ok ; 0 - cancel + """ + self._ChkCompiled() + if not self.modal: + raise SyntaxError("Form is not modal. Open() should be instead") + + return AskUsingForm(*self.__args) + + + def Open(self): + """ + Opens a widget containing the compiled form. + """ + self._ChkCompiled() + if self.modal: + raise SyntaxError("Form is modal. Execute() should be instead") + + OpenForm(*self.__args) + + + def EnableField(self, ctrl, enable): + """ + Enable or disable an input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) + + + def ShowField(self, ctrl, show): + """ + Show or hide an input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_show_field(self.p_fa, ctrl.id, show) + + + def MoveField(self, ctrl, x, y, w, h): + """ + Move/resize an input field + + @return: False - no such fiel + """ + return _idaapi.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) + + + def GetFocusedField(self): + """ + Get currently focused input field. + @return: None if no field is selected otherwise the control ID + """ + id = _idaapi.formchgcbfa_get_focused_field(self.p_fa) + return self.FindControlById(id) + + + def SetFocusedField(self, ctrl): + """ + Set currently focused input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) + + + def RefreshField(self, ctrl): + """ + Refresh a field + @return: False - no such control + """ + return _idaapi.formchgcbfa_refresh_field(self.p_fa, ctrl.id) + + + def Close(self, close_normally): + """ + Close the form + @param close_normally: + 1: form is closed normally as if the user pressed Enter + 0: form is closed abnormally as if the user pressed Esc + @return: None + """ + return _idaapi.formchgcbfa_close(self.p_fa, close_normally) + + + def GetControlValue(self, ctrl): + """ + Returns the control's value depending on its type + @param ctrl: Form control instance + @return: + - color button, radio controls: integer + - file/dir input, string input and string label: string + - embedded chooser control (0-based indices of selected items): integer list + - for multilinetext control: textctrl_info_t + - dropdown list controls: string (when editable) or index (when readonly) + - None: on failure + """ + tid, sz = self.ControlToFieldTypeIdAndSize(ctrl) + r = _idaapi.formchgcbfa_get_field_value( + self.p_fa, + ctrl.id, + tid, + sz) + # Multilinetext? Unpack the tuple into a new textctrl_info_t instance + if r is not None and tid == 7: + return textctrl_info_t(text=r[0], flags=r[1], tabsize=r[2]) + else: + return r + + + def SetControlValue(self, ctrl, value): + """ + Set the control's value depending on its type + @param ctrl: Form control instance + @param value: + - embedded chooser: a 0-base indices list to select embedded chooser items + - multilinetext: a textctrl_info_t + - dropdown list: an integer designating the selection index if readonly + a string designating the edit control value if not readonly + @return: Boolean true on success + """ + tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) + return _idaapi.formchgcbfa_set_field_value( + self.p_fa, + ctrl.id, + tid, + value) + + + @staticmethod + def ControlToFieldTypeIdAndSize(ctrl): + """ + Converts a control object to a tuple containing the field id + and the associated buffer size + """ + # Input control depend on the associated buffer size (supplied by the user) + + # Make sure you check instances types taking into account inheritance + if isinstance(ctrl, Form.DropdownListControl): + return (8, 1 if ctrl.readonly else 0) + elif isinstance(ctrl, Form.MultiLineTextControl): + return (7, 0) + elif isinstance(ctrl, Form.EmbeddedChooserControl): + return (5, 0) + # Group items or controls + elif isinstance(ctrl, (Form.GroupItemControl, Form.GroupControl)): + return (2, 0) + elif isinstance(ctrl, Form.StringLabel): + return (3, min(_idaapi.MAXSTR, ctrl.size)) + elif isinstance(ctrl, Form.ColorInput): + return (4, 0) + elif isinstance(ctrl, Form.NumericInput): + # Pass the numeric control type + return (6, ord(ctrl.tp[0])) + elif isinstance(ctrl, Form.InputControl): + return (1, ctrl.size) + else: + raise NotImplementedError, "Not yet implemented" + +# -------------------------------------------------------------------------- +# Instantiate AskUsingForm function pointer +try: + import ctypes + # Setup the numeric argument size + Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL + AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm()) + OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_OpenForm()) +except: + def AskUsingForm__(*args): + warning("AskUsingForm() needs ctypes library in order to work") + return 0 + def OpenForm__(*args): + warning("OpenForm() needs ctypes library in order to work") + + +def AskUsingForm(*args): + """ + Calls AskUsingForm() + @param: Compiled Arguments obtain through the Form.Compile() function + @return: 1 = ok, 0 = cancel + """ + old = set_script_timeout(0) + r = AskUsingForm__(*args) + set_script_timeout(old) + return r + +def OpenForm(*args): + """ + Calls OpenForm() + @param: Compiled Arguments obtain through the Form.Compile() function + """ + old = set_script_timeout(0) + r = OpenForm__(*args) + set_script_timeout(old) + + +# + +# +# -------------------------------------------------------------------------- +class TestEmbeddedChooserClass(Choose2): + """ + A simple chooser to be used as an embedded chooser + """ + def __init__(self, title, nb = 5, flags=0): + Choose2.__init__(self, + title, + [ ["Address", 10], ["Name", 30] ], + embedded=True, width=30, height=20, flags=flags) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnClose(self): + pass + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + print("getsize -> %d" % n) + return n + +# -------------------------------------------------------------------------- +class MyForm(Form): + def __init__(self): + self.invert = False + self.EChooser = TestEmbeddedChooserClass("E1", flags=Choose2.CH_MULTI) + Form.__init__(self, r"""STARTITEM {id:rNormal} +BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL Nevermind +Form Test + +{FormChangeCb} +This is a string: +{cStr1}+ +This is an address: +{cAddr1}+ + +Escape\{control} +This is a string: '{cStr2}' +This is a number: {cVal1} + +<#Hint1#Enter name:{iStr1}> +<#Hint2#Select color:{iColor1}> +Browse test +<#Select a file to open#Browse to open:{iFileOpen}> +<#Select a file to save#Browse to save:{iFileSave}> +<#Select dir#Browse for dir:{iDir}> +Type +<#Select type#Write a type:{iType}> +Numbers +<##Enter a selector value:{iSegment}> +<##Enter a raw hex:{iRawHex}> +<##Enter a character:{iChar}> +<##Enter an address:{iAddr}> +Button test +<##Button1:{iButton1}> <##Button2:{iButton2}> + +Check boxes: + + +{cGroup1}> + +Radio boxes: + + +{cGroup2}> + +The end! +""", { + 'cStr1': Form.StringLabel("Hello"), + 'cStr2': Form.StringLabel("StringTest"), + 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR), + 'cVal1' : Form.NumericLabel(99, Form.FT_HEX), + 'iStr1': Form.StringInput(), + 'iColor1': Form.ColorInput(), + 'iFileOpen': Form.FileInput(open=True), + 'iFileSave': Form.FileInput(save=True), + 'iDir': Form.DirInput(), + 'iType': Form.StringInput(tp=Form.FT_TYPE), + 'iSegment': Form.NumericInput(tp=Form.FT_SEG), + 'iRawHex': Form.NumericInput(tp=Form.FT_RAWHEX), + 'iAddr': Form.NumericInput(tp=Form.FT_ADDR), + 'iChar': Form.NumericInput(tp=Form.FT_CHAR), + 'iButton1': Form.ButtonInput(self.OnButton1), + 'iButton2': Form.ButtonInput(self.OnButton2), + 'cGroup1': Form.ChkGroupControl(("rNormal", "rError", "rWarnings")), + 'cGroup2': Form.RadGroupControl(("rRed", "rGreen", "rBlue")), + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) + }) + + + def OnButton1(self, code=0): + print("Button1 pressed") + + + def OnButton2(self, code=0): + print("Button2 pressed") + + + def OnFormChange(self, fid): + if fid == self.iButton1.id: + print("Button1 fchg;inv=%s" % self.invert) + self.SetFocusedField(self.rNormal) + self.EnableField(self.rError, self.invert) + self.invert = not self.invert + elif fid == self.iButton2.id: + g1 = self.GetControlValue(self.cGroup1) + g2 = self.GetControlValue(self.cGroup2) + d = self.GetControlValue(self.iDir) + f = self.GetControlValue(self.iFileOpen) + print("cGroup2:%x;Dir=%s;fopen=%s;cGroup1:%x" % (g1, d, f, g2)) + elif fid == self.cEChooser.id: + l = self.GetControlValue(self.cEChooser) + print("Chooser: %s" % l) + else: + print(">>fid:%d" % fid) + return 1 + + + +# -------------------------------------------------------------------------- +def stdalone_main(): + f = MyForm() + f, args = f.Compile() + print args[0] + print args[1:] + f.rNormal.checked = True + f.rWarnings.checked = True + print hex(f.cGroup1.value) + + f.rGreen.selected = True + print f.cGroup2.value + print "Title: '%s'" % f.title + + f.Free() + +# -------------------------------------------------------------------------- +def ida_main(): + # Create form + global f + f = MyForm() + + # Compile (in order to populate the controls) + f.Compile() + + f.iColor1.value = 0x5bffff + f.iDir.value = os.getcwd() + f.rNormal.checked = True + f.rWarnings.checked = True + f.rGreen.selected = True + f.iStr1.value = "Hello" + f.iFileSave.value = "*.*" + f.iFileOpen.value = "*.*" + # Execute the form + ok = f.Execute() + print("r=%d" % ok) + if ok == 1: + print("f.str1=%s" % f.iStr1.value) + print("f.color1=%x" % f.iColor1.value) + print("f.openfile=%s" % f.iFileOpen.value) + print("f.savefile=%s" % f.iFileSave.value) + print("f.dir=%s" % f.iDir.value) + print("f.type=%s" % f.iType.value) + print("f.seg=%s" % f.iSegment.value) + print("f.rawhex=%x" % f.iRawHex.value) + print("f.char=%x" % f.iChar.value) + print("f.addr=%x" % f.iAddr.value) + print("f.cGroup1=%x" % f.cGroup1.value) + print("f.cGroup2=%x" % f.cGroup2.value) + + sel = f.EChooser.GetEmbSelection() + if sel is None: + print("No selection") + else: + print("Selection: %s" % sel) + + # Dispose the form + f.Free() + +# -------------------------------------------------------------------------- +def ida_main_legacy(): + # Here we simply show how to use the old style form format using Python + + # Sample form from kernwin.hpp + s = """Sample dialog box + + +This is sample dialog box for %A +using address %$ + +<~E~nter value:N:32:16::> +""" + + # Use either StringArgument or NumericArgument to pass values to the function + num = Form.NumericArgument('N', value=123) + ok = idaapi.AskUsingForm(s, + Form.StringArgument("PyAskUsingForm").arg, + Form.NumericArgument('$', 0x401000).arg, + num.arg) + if ok == 1: + print("You entered: %x" % num.value) + +# -------------------------------------------------------------------------- +def test_multilinetext_legacy(): + # Here we text the multi line text control in legacy mode + + # Sample form from kernwin.hpp + s = """Sample dialog box + +This is sample dialog box + +""" + # Use either StringArgument or NumericArgument to pass values to the function + ti = textctrl_info_t("Some initial value") + ok = idaapi.AskUsingForm(s, pointer(c_void_p.from_address(ti.clink_ptr))) + if ok == 1: + print("You entered: %s" % ti.text) + + del ti + +# -------------------------------------------------------------------------- +class MyForm2(Form): + """Simple Form to test multilinetext and combo box controls""" + def __init__(self): + Form.__init__(self, r"""STARTITEM 0 +BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL NONE +Form Test + +{FormChangeCb} + +""", { + 'txtMultiLineText': Form.MultiLineTextControl(text="Hello"), + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + }) + + + def OnFormChange(self, fid): + if fid == self.txtMultiLineText.id: + pass + elif fid == -2: + ti = self.GetControlValue(self.txtMultiLineText) + print "ti.text = %s" % ti.text + else: + print(">>fid:%d" % fid) + return 1 + +# -------------------------------------------------------------------------- +def test_multilinetext(execute=True): + """Test the multilinetext and combobox controls""" + f = MyForm2() + f, args = f.Compile() + if execute: + ok = f.Execute() + else: + print args[0] + print args[1:] + ok = 0 + + if ok == 1: + assert f.txtMultiLineText.text == f.txtMultiLineText.value + print f.txtMultiLineText.text + + f.Free() + +# -------------------------------------------------------------------------- +class MyForm3(Form): + """Simple Form to test multilinetext and combo box controls""" + def __init__(self): + self.__n = 0 + Form.__init__(self, +r"""BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL NONE +Dropdown list test + +{FormChangeCb} + + +""", { + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'cbReadonly': Form.DropdownListControl( + items=["red", "green", "blue"], + readonly=True, + selval=1), + 'cbEditable': Form.DropdownListControl( + items=["1MB", "2MB", "3MB", "4MB"], + readonly=False, + selval="4MB"), + 'iButtonAddelement': Form.ButtonInput(self.OnButtonNop), + 'iButtonSetIndex': Form.ButtonInput(self.OnButtonNop), + 'iButtonSetString': Form.ButtonInput(self.OnButtonNop), + }) + + + def OnButtonNop(self, code=0): + """Do nothing, we will handle events in the form callback""" + pass + + def OnFormChange(self, fid): + if fid == self.iButtonSetString.id: + s = idc.AskStr("none", "Enter value") + if s: + self.SetControlValue(self.cbEditable, s) + elif fid == self.iButtonSetIndex.id: + s = idc.AskStr("1", "Enter index value:") + if s: + try: + i = int(s) + except: + i = 0 + self.SetControlValue(self.cbReadonly, i) + elif fid == self.iButtonAddelement.id: + # add a value to the string list + self.__n += 1 + self.cbReadonly.add("some text #%d" % self.__n) + # Refresh the control + self.RefreshField(self.cbReadonly) + elif fid == -2: + s = self.GetControlValue(self.cbEditable) + print "user entered: %s" % s + sel_idx = self.GetControlValue(self.cbReadonly) + + return 1 + +# -------------------------------------------------------------------------- +def test_dropdown(execute=True): + """Test the combobox controls, in a modal dialog""" + f = MyForm3() + f, args = f.Compile() + if execute: + ok = f.Execute() + else: + print args[0] + print args[1:] + ok = 0 + + if ok == 1: + print "Editable: %s" % f.cbEditable.value + print "Readonly: %s" % f.cbReadonly.value + + f.Free() + +# -------------------------------------------------------------------------- +tdn_form = None +def test_dropdown_nomodal(): + """Test the combobox controls, in a non-modal form""" + global tdn_form + if tdn_form is None: + tdn_form = MyForm3() + tdn_form.modal = False + tdn_form.openform_flags = idaapi.PluginForm.FORM_TAB + tdn_form, _ = tdn_form.Compile() + tdn_form.Open() + +# +# -------------------------------------------------------------------------- + +# +# -------------------------------------------------------------------------- +class MainChooserClass(Choose2): + def __init__(self, title, icon): + Choose2.__init__(self, + title, + [ ["Item", 10] ], + icon=icon, + flags=Choose2.CH_NOIDB, + embedded=True, width=30, height=20) + + def OnClose(self): + pass + + def OnGetLine(self, n): + return ["Option %d" % n] + + def OnGetSize(self): + return 10 + + def OnCommand(self, n, cmd_id): + if cmd_id == self.cmd_id1: + print("Context menu on: %d" % n) + + return 0 + + +# -------------------------------------------------------------------------- +class AuxChooserClass(Choose2): + def __init__(self, title, icon): + Choose2.__init__(self, + title, + [ ["Item", 10] ], + icon=icon, + flags=Choose2.CH_NOIDB | Choose2.CH_MULTI, + embedded=True, width=30, height=20) + + def OnClose(self): + pass + + def OnGetLine(self, n): + return ["Item %d" % n] + + def OnGetSize(self): + t = self.form.main_current_index + return 0 if t < 0 else t+1 + + +# -------------------------------------------------------------------------- +class MyChooserForm(Form): + + # Custom icon data + icon_data = ( + "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" + "\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1F\xF3\xFF" + "\x61\x00\x00\x00\x7D\x49\x44\x41\x54\x78\xDA\x63\x64\xC0\x0E\xFE" + "\xE3\x10\x67\x24\x28\x00\xD2\xFC\xF3\xAF\x36\x56\xDD\xEC\xCC\x57" + "\x31\xF4\x20\x73\xC0\xB6\xE2\xD2\x8C\x66\x08\x5C\x2F\x8A\x01\x84" + "\x34\x63\x73\x09\x23\xA9\x9A\xD1\x0D\x61\x44\xD7\xCC\xCF\x02\x71" + "\xE2\xC7\x3F\xA8\x06\x62\x13\x07\x19\x42\x7D\x03\x48\xF5\xC6\x20" + "\x34\x00\xE4\x57\x74\xFF\xE3\x92\x83\x19\xC0\x40\x8C\x21\xD8\x34" + "\x33\x40\xA3\x91\x01\x97\x21\xC8\x00\x9B\x66\x38\x01\x33\x00\x44" + "\x50\x92\x94\xB1\xBA\x04\x8B\x66\x9C\x99\x09\xC5\x10\x1C\xE2\x18" + "\xEA\x01\xA3\x65\x55\x0B\x33\x14\x07\x63\x00\x00\x00\x00\x49\x45" + "\x4E\x44\xAE\x42\x60\x82") + + + def Free(self): + # Call the base + Form.Free(self) + + # Free icon + if self.icon_id != 0: + idaapi.free_custom_icon(self.icon_id) + self.icon_id = 0 + + + def __init__(self): + # Load custom icon + self.icon_id = idaapi.load_custom_icon(data=MyChooserForm.icon_data) + if self.icon_id == 0: + raise RuntimeError("Failed to load icon data!") + + self.main_current_index = -1 + self.EChMain = MainChooserClass("MainChooser", self.icon_id) + self.EChAux = AuxChooserClass("AuxChooser", self.icon_id) + + # Link the form to the EChooser + self.EChMain.form = self + self.EChAux.form = self + + Form.__init__(self, r"""STARTITEM 0 +Form with choosers + + {FormChangeCb} + Select an item in the main chooser: + +
+ + + + +""", { + 'ctrlSelectionEdit' : Form.StringInput(), + 'FormChangeCb' : Form.FormChangeCb(self.OnFormChange), + 'ctrlMainChooser' : Form.EmbeddedChooserControl(self.EChMain), + 'ctrlAuxChooser' : Form.EmbeddedChooserControl(self.EChAux), + }) + + + def refresh_selection_edit(self): + if self.main_current_index < 0: + s = "No selection in the main chooser" + else: + s = "Main %d" % self.main_current_index + + # Get selection in the aux chooser + sel = self.GetControlValue(self.ctrlAuxChooser) + if sel: + s = "%s - Aux item(s): %s" % (s, ",".join(str(x) for x in sel)) + + # Update string input + self.SetControlValue(self.ctrlSelectionEdit, s) + + + def OnFormChange(self, fid): + if fid == -1: + print("Initialization") + self.refresh_selection_edit() + + # Add an item to the context menu of the main chooser + id = self.ctrlMainChooser.AddCommand("Test", icon=self.icon_id) + print "id=%d" % id + if id < 0: + print("Failed to install menu for main embedded chooser") + else: + self.EChMain.cmd_id1 = id + + elif fid == -2: + print("Terminating"); + + elif fid == self.ctrlMainChooser.id: + print("main chooser selection change"); + l = self.GetControlValue(self.ctrlMainChooser); + if not l: + self.main_current_index = -1 + else: + self.main_current_index = l[0] + + # Refresh auxiliar chooser + self.RefreshField(self.ctrlAuxChooser) + self.refresh_selection_edit() + + elif fid == self.ctrlAuxChooser.id: + self.refresh_selection_edit() + + elif fid == self.ctrlSelectionEdit.id: + pass + else: + print("unknown id %d" % fid) + + return 1 + +# + +# -------------------------------------------------------------------------- +def main_formchooser(): + global f + f = MyChooserForm() + try: + f.Compile() + r = f.Execute() + print("Execute returned: %d" % r) + f.Free() + except Exception as e: + print("Failed to show form: %s" % str(e)) + +# -------------------------------------------------------------------------- +if __name__=='__main__': + #stdalone_main() if stdalone else main_formchooser() + #stdalone_main() if stdalone else test_multilinetext() + test_dropdown() + #test_multilinetext(False) + diff --git a/pywraps/py_choose.hpp b/pywraps/py_choose.hpp index 2ac54b6d..b7723b19 100644 --- a/pywraps/py_choose.hpp +++ b/pywraps/py_choose.hpp @@ -1,78 +1,78 @@ -#ifndef __PY_CHOOSE__ -#define __PY_CHOOSE__ - -//--------------------------------------------------------------------------- -// -//--------------------------------------------------------------------------- -uint32 idaapi choose_sizer(void *self) -{ - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod((PyObject *)self, "sizer", "")); - return PyInt_AsLong(pyres.o); -} - -//--------------------------------------------------------------------------- -char *idaapi choose_getl(void *self, uint32 n, char *buf) -{ - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - (PyObject *)self, - "getl", - "l", - n)); - - const char *res; - if (pyres == NULL || (res = PyString_AsString(pyres.o)) == NULL ) - qstrncpy(buf, "", MAXSTR); - else - qstrncpy(buf, res, MAXSTR); - return buf; -} - -//--------------------------------------------------------------------------- -void idaapi choose_enter(void *self, uint32 n) -{ - PYW_GIL_GET; - newref_t res(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); -} - -//--------------------------------------------------------------------------- -uint32 choose_choose( - void *self, - int flags, - int x0,int y0, - int x1,int y1, - int width, - int deflt, - int icon) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); - const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; - - int r = choose( - flags, - x0, y0, - x1, y1, - self, - width, - choose_sizer, - choose_getl, - title, - icon, - deflt, - NULL, /* del */ - NULL, /* inst */ - NULL, /* update */ - NULL, /* edit */ - choose_enter, - NULL, /* destroy */ - NULL, /* popup_names */ - NULL);/* get_icon */ - - return r; -} -// - -#endif // __PY_CHOOSE__ +#ifndef __PY_CHOOSE__ +#define __PY_CHOOSE__ + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +uint32 idaapi choose_sizer(void *self) +{ + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod((PyObject *)self, "sizer", "")); + return PyInt_AsLong(pyres.o); +} + +//--------------------------------------------------------------------------- +char *idaapi choose_getl(void *self, uint32 n, char *buf) +{ + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + (PyObject *)self, + "getl", + "l", + n)); + + const char *res; + if (pyres == NULL || (res = PyString_AsString(pyres.o)) == NULL ) + qstrncpy(buf, "", MAXSTR); + else + qstrncpy(buf, res, MAXSTR); + return buf; +} + +//--------------------------------------------------------------------------- +void idaapi choose_enter(void *self, uint32 n) +{ + PYW_GIL_GET; + newref_t res(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); +} + +//--------------------------------------------------------------------------- +uint32 choose_choose( + void *self, + int flags, + int x0,int y0, + int x1,int y1, + int width, + int deflt, + int icon) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); + const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; + + int r = choose( + flags, + x0, y0, + x1, y1, + self, + width, + choose_sizer, + choose_getl, + title, + icon, + deflt, + NULL, /* del */ + NULL, /* inst */ + NULL, /* update */ + NULL, /* edit */ + choose_enter, + NULL, /* destroy */ + NULL, /* popup_names */ + NULL);/* get_icon */ + + return r; +} +// + +#endif // __PY_CHOOSE__ diff --git a/pywraps/py_choose2.hpp b/pywraps/py_choose2.hpp index 37abeac9..e536645a 100644 --- a/pywraps/py_choose2.hpp +++ b/pywraps/py_choose2.hpp @@ -1,915 +1,902 @@ -#ifndef __PY_CHOOSE2__ -#define __PY_CHOOSE2__ - -// - -//------------------------------------------------------------------------ -// Some defines -#define POPUP_NAMES_COUNT 4 -#define MAX_CHOOSER_MENU_COMMANDS 20 -#define thisobj ((py_choose2_t *) obj) -#define thisdecl py_choose2_t *_this = thisobj -#define MENU_COMMAND_CB(id) \ - static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) \ - { \ - return thisobj->on_command(id, int(n)); \ - } - -//------------------------------------------------------------------------ -// Helper functions -class py_choose2_t; -typedef std::map pychoose2_to_choose2_map_t; -static pychoose2_to_choose2_map_t choosers; - -py_choose2_t *choose2_find_instance(PyObject *self) -{ - pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - return it == choosers.end() ? NULL : it->second; -} - -void choose2_add_instance(PyObject *self, py_choose2_t *c2) -{ - choosers[self] = c2; -} - -void choose2_del_instance(PyObject *self) -{ - pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - if ( it != choosers.end() ) - choosers.erase(it); -} - -//------------------------------------------------------------------------ -class py_choose2_t -{ -private: - enum - { - CHOOSE2_HAVE_DEL = 0x0001, - CHOOSE2_HAVE_INS = 0x0002, - CHOOSE2_HAVE_UPDATE = 0x0004, - CHOOSE2_HAVE_EDIT = 0x0008, - CHOOSE2_HAVE_ENTER = 0x0010, - CHOOSE2_HAVE_GETICON = 0x0020, - CHOOSE2_HAVE_GETATTR = 0x0040, - CHOOSE2_HAVE_COMMAND = 0x0080, - CHOOSE2_HAVE_ONCLOSE = 0x0100, - CHOOSE2_HAVE_SELECT = 0x0200, - CHOOSE2_HAVE_REFRESHED = 0x0400, - }; - // Chooser flags - int flags; - - // Callback flags (to tell which callback exists and which not) - // One of CHOOSE2_HAVE_xxxx - unsigned int cb_flags; - chooser_info_t *embedded; - intvec_t embedded_sel; - - // Menu callback index (in the menu_cbs array) - int menu_cb_idx; - - // Chooser title - qstring title; - - // Column widths - intvec_t widths; - - // Python object link - PyObject *self; - // Chooser columns - qstrvec_t cols; - const char **popup_names; - bool ui_cb_hooked; - - // The number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value - MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) - MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) - MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) - MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) - MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) - MENU_COMMAND_CB(10) MENU_COMMAND_CB(11) - MENU_COMMAND_CB(12) MENU_COMMAND_CB(13) - MENU_COMMAND_CB(14) MENU_COMMAND_CB(15) - MENU_COMMAND_CB(16) MENU_COMMAND_CB(17) - MENU_COMMAND_CB(18) MENU_COMMAND_CB(19) - static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; - - //------------------------------------------------------------------------ - // Static methods to dispatch to member functions - //------------------------------------------------------------------------ - static int idaapi ui_cb(void *obj, int notification_code, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - // UI callback to handle chooser items with attributes - if ( notification_code != ui_get_chooser_item_attrs ) - return 0; - - // Pass events that belong to our chooser only - void *chooser_obj = va_arg(va, void *); - if ( obj != chooser_obj ) - return 0; - - int n = int(va_arg(va, uint32)); - chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); - thisobj->on_get_line_attr(n, attr); - return 1; - } - - static void idaapi s_select(void *obj, const intvec_t &sel) - { - thisobj->on_select(sel); - } - - static void idaapi s_refreshed(void *obj) - { - thisobj->on_refreshed(); - } - - static uint32 idaapi s_sizer(void *obj) - { - return (uint32)thisobj->on_get_size(); - } - - static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) - { - thisobj->on_get_line(int(n), arrptr); - } - - static uint32 idaapi s_del(void *obj, uint32 n) - { - return uint32(thisobj->on_delete_line(int(n))); - } - - static void idaapi s_ins(void *obj) - { - thisobj->on_insert_line(); - } - - static uint32 idaapi s_update(void *obj, uint32 n) - { - return uint32(thisobj->on_refresh(int(n))); - } - - static void idaapi s_edit(void *obj, uint32 n) - { - thisobj->on_edit_line(int(n)); - } - - static void idaapi s_enter(void * obj, uint32 n) - { - thisobj->on_enter(int(n)); - } - - static int idaapi s_get_icon(void *obj, uint32 n) - { - return thisobj->on_get_icon(int(n)); - } - - static void idaapi s_destroy(void *obj) - { - thisobj->on_close(); - } - - //------------------------------------------------------------------------ - // Member functions corresponding to each chooser2() callback - //------------------------------------------------------------------------ - void clear_popup_names() - { - if ( popup_names == NULL ) - return; - - for ( int i=0; i=0; i-- ) - line_arr[i][0] = '\0'; - - // Call Python - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1)); - if ( list == NULL ) - return; - - // Go over the List returned by Python and convert to C strings - for ( int i=ncols-1; i>=0; i-- ) - { - borref_t item(PyList_GetItem(list.o, Py_ssize_t(i))); - if ( item == NULL ) - continue; - - const char *str = PyString_AsString(item.o); - if ( str != NULL ) - qstrncpy(line_arr[i], str, MAXSTR); - } - } - - size_t on_get_size() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL)); - if ( pyres == NULL ) - return 0; - - return PyInt_AsLong(pyres.o); - } - - void on_refreshed() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL)); - } - - void on_select(const intvec_t &intvec) - { - PYW_GIL_GET; - ref_t py_list(PyW_IntVecToPyList(intvec)); - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list.o)); - } - - void on_close() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL)); - - // Delete this instance if none modal and not embedded - if ( !is_modal() && get_embedded() == NULL ) - delete this; - } - - int on_delete_line(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_DELETE_LINE, - "i", - IS_CHOOSER_EVENT(lineno) ? lineno : lineno-1)); - return pyres == NULL ? 1 : PyInt_AsLong(pyres.o); - } - - int on_refresh(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_REFRESH, - "i", - lineno - 1)); - return pyres == NULL ? lineno : PyInt_AsLong(pyres.o) + 1; - } - - void on_insert_line() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL)); - } - - void on_enter(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_SELECT_LINE, - "i", - lineno - 1)); - } - - void on_edit_line(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_EDIT_LINE, - "i", - lineno - 1)); - } - - int on_command(int cmd_id, int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_COMMAND, - "ii", - lineno - 1, - cmd_id)); - return pyres == NULL ? lineno : PyInt_AsLong(pyres.o); - } - - int on_get_icon(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_GET_ICON, - "i", - lineno - 1)); - return PyInt_AsLong(pyres.o); - } - - void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1)); - if ( pyres != NULL ) - { - if ( PyList_Check(pyres.o) ) - { - PyObject *item; - if ( (item = PyList_GetItem(pyres.o, 0)) != NULL ) - attr->color = PyInt_AsLong(item); - if ( (item = PyList_GetItem(pyres.o, 1)) != NULL ) - attr->flags = PyInt_AsLong(item); - } - } - } - - bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const - { - if ( get_embedded() != NULL ) - { - // For embedded chooser, the "caption" will be overloaded to encode - // the AskUsingForm's title, caption and embedded chooser id - // Title:EmbeddedChooserID:Caption - - char title_buf[MAXSTR]; - const char *ptitle; - - static const char delimiter[] = ":"; - char temp[MAXSTR]; - qstrncpy(temp, caption, sizeof(temp)); - - char *ctx; - char *p = qstrtok(temp, delimiter, &ctx); - if ( p == NULL ) - return false; - - // Copy the title - char title_str[MAXSTR]; - qstrncpy(title_str, p, sizeof(title_str)); - - // Copy the echooser ID - p = qstrtok(NULL, delimiter, &ctx); - if ( p == NULL ) - return false; - - char id_str[10]; - qstrncpy(id_str, p, sizeof(id_str)); - - // Form the new title of the form: "AskUsingFormTitle:EchooserId" - qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str); - - // Adjust the title - *out_title = title_buf; - - // Adjust the caption - p = qstrtok(NULL, delimiter, &ctx); - *out_caption = caption + (p - temp); - } - else - { - *out_title = title; - *out_caption = caption; - } - return true; - } - -public: - //------------------------------------------------------------------------ - // Public methods - //------------------------------------------------------------------------ - py_choose2_t(): flags(0), cb_flags(0), - embedded(NULL), menu_cb_idx(0), - self(NULL), popup_names(NULL), ui_cb_hooked(false) - { - } - - ~py_choose2_t() - { - // Remove from list - choose2_del_instance(self); - - // Uninstall hooks - install_hooks(false); - - delete embedded; - Py_XDECREF(self); - clear_popup_names(); - } - - static py_choose2_t *find_chooser(const char *title) - { - return (py_choose2_t *) get_chooser_obj(title); - } - - void close() - { - // Will trigger on_close() - close_chooser(title.c_str()); - } - - bool activate() - { - TForm *frm = find_tform(title.c_str()); - if ( frm == NULL ) - return false; - - switchto_tform(frm, true); - return true; - } - - int add_command( - const char *_caption, - int flags=0, - int menu_index=-1, - int icon=-1) - { - if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) - return -1; - - qstring title, caption; - if ( !split_chooser_caption(&title, &caption, _caption) - || !add_chooser_command( - title.c_str(), - caption.c_str(), - menu_cbs[menu_cb_idx], - menu_index, - icon, - flags) ) - return -1; - - return menu_cb_idx++; - } - - // Create a chooser. - // If it detects the "embedded" attribute, then it will create a chooser_info_t structure - // Otherwise the chooser window is created and displayed - int create(PyObject *self) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Get flags - ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS)); - if ( flags_attr == NULL ) - return -1; - flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0; - - // Get the title - if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) - return -1; - - // Get columns - ref_t cols_attr(PyW_TryGetAttrString(self, "cols")); - if ( cols_attr == NULL ) - return -1; - - // Get col count - int ncols = int(PyList_Size(cols_attr.o)); - - // Get cols caption and widthes - cols.qclear(); - for ( int i=0; iself = self; - - // Hook to notification point (to handle chooser item attributes) - install_hooks(true); - - // Check if *embedded - ref_t emb_attr(PyW_TryGetAttrString(self, S_EMBEDDED)); - if ( emb_attr != NULL && PyObject_IsTrue(emb_attr.o) == 1 ) - { - // Create an embedded chooser structure - embedded = new chooser_info_t(); - embedded->obj = this; - embedded->cb = sizeof(chooser_info_t); - embedded->title = title.c_str(); - embedded->columns = ncols; - embedded->deflt = deflt; - embedded->flags = flags; - embedded->width = pts[0]; // Take x1 - embedded->height = pts[1]; // Take y1 - embedded->icon = icon; - embedded->popup_names = popup_names; - embedded->widths = widths.begin(); - embedded->destroyer = s_destroy; - embedded->getl = s_getl; - embedded->sizer = s_sizer; - embedded->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL; - embedded->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL; - embedded->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL; - embedded->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL; - embedded->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL; - embedded->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL; - embedded->get_attrs = NULL; - // Fill callbacks that are only present in idaq - if ( is_idaq() ) - { - embedded->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL; - embedded->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL; - } - else - { - embedded->select = NULL; - embedded->refresh = NULL; - } - } - - // Create the chooser (if not embedded) - int r; - if ( embedded == NULL ) - { - r = ::choose2( - flags, - pts[0], pts[1], pts[2], pts[3], - this, - ncols, - &widths[0], - s_sizer, - s_getl, - title.c_str(), - icon, - deflt, - (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL, - (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL, - (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL, - (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL, - (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL, - s_destroy, - popup_names, - (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL); - - clear_popup_names(); - - // Modal chooser return the index of the selected item - if ( is_modal() ) - r--; - } - // Embedded chooser? - else - { - // Return success - r = 1; - } - - return r; - } - - inline PyObject *get_self() - { - return self; - } - - void refresh() - { - refresh_chooser(title.c_str()); - } - - bool is_modal() - { - return (flags & CH_MODAL) != 0; - } - - intvec_t *get_sel_vec() - { - return &embedded_sel; - } - - chooser_info_t *get_embedded() const - { - return embedded; - } -}; - -//------------------------------------------------------------------------ -// Initialize the callback pointers -#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id -chooser_cb_t *py_choose2_t::menu_cbs[MAX_CHOOSER_MENU_COMMANDS] = -{ - DECL_MENU_COMMAND_CB(0), DECL_MENU_COMMAND_CB(1), - DECL_MENU_COMMAND_CB(2), DECL_MENU_COMMAND_CB(3), - DECL_MENU_COMMAND_CB(4), DECL_MENU_COMMAND_CB(5), - DECL_MENU_COMMAND_CB(6), DECL_MENU_COMMAND_CB(7), - DECL_MENU_COMMAND_CB(8), DECL_MENU_COMMAND_CB(9), - DECL_MENU_COMMAND_CB(10), DECL_MENU_COMMAND_CB(11), - DECL_MENU_COMMAND_CB(12), DECL_MENU_COMMAND_CB(13), - DECL_MENU_COMMAND_CB(14), DECL_MENU_COMMAND_CB(15), - DECL_MENU_COMMAND_CB(16), DECL_MENU_COMMAND_CB(17), - DECL_MENU_COMMAND_CB(18), DECL_MENU_COMMAND_CB(19) -}; -#undef DECL_MENU_COMMAND_CB - -#undef POPUP_NAMES_COUNT -#undef MAX_CHOOSER_MENU_COMMANDS -#undef thisobj -#undef thisdecl -#undef MENU_COMMAND_CB - -//------------------------------------------------------------------------ -int choose2_create(PyObject *self, bool embedded) -{ - py_choose2_t *c2; - - c2 = choose2_find_instance(self); - if ( c2 != NULL ) - { - if ( !embedded ) - c2->activate(); - return 1; - } - - c2 = new py_choose2_t(); - - choose2_add_instance(self, c2); - - int r = c2->create(self); - // Non embedded chooser? Return immediately - if ( !embedded ) - return r; - - // Embedded chooser was not created? - if ( c2->get_embedded() == NULL || r != 1 ) - { - delete c2; - r = 0; - } - return r; -} - -//------------------------------------------------------------------------ -void choose2_close(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 == NULL ) - return; - - // Modal or embedded chooser? - if ( c2->get_embedded() != NULL || c2->is_modal() ) - { - // Then simply delete the instance - delete c2; - } - else - { - // Close the chooser. - // In turn this will lead to the deletion of the object - c2->close(); - } -} - -//------------------------------------------------------------------------ -void choose2_refresh(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->refresh(); -} - -//------------------------------------------------------------------------ -void choose2_activate(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->activate(); -} - -//------------------------------------------------------------------------ -PyObject *choose2_get_embedded_selection(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - py_choose2_t *c2 = choose2_find_instance(self); - chooser_info_t *embedded; - - if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) - Py_RETURN_NONE; - - // Returned as 1-based - intvec_t &intvec = *c2->get_sel_vec(); - - // Make 0-based - for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) - (*it)--; - - ref_t ret(PyW_IntVecToPyList(intvec)); - ret.incref(); - return ret.o; -} - -//------------------------------------------------------------------------ -// Return the C instances as 64bit numbers -PyObject *choose2_get_embedded(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - py_choose2_t *c2 = choose2_find_instance(self); - chooser_info_t *embedded; - - if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) - Py_RETURN_NONE; - else - return Py_BuildValue("(KK)", - PY_ULONG_LONG(embedded), - PY_ULONG_LONG(c2->get_sel_vec())); -} - -//------------------------------------------------------------------------ -int choose2_add_command( - PyObject *self, - const char *caption, - int flags=0, - int menu_index=-1, - int icon=-1) -{ - py_choose2_t *c2 = choose2_find_instance(self); - return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon); -} - -//------------------------------------------------------------------------ -PyObject *choose2_find(const char *title) -{ - py_choose2_t *c2 = py_choose2_t::find_chooser(title); - return c2 == NULL ? NULL : c2->get_self(); -} -// - -//--------------------------------------------------------------------------- -// -PyObject *choose2_find(const char *title); -int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon); -void choose2_refresh(PyObject *self); -void choose2_close(PyObject *self); -int choose2_create(PyObject *self, bool embedded); -void choose2_activate(PyObject *self); -PyObject *choose2_get_embedded(PyObject *self); -PyObject *choose2_get_embedded_selection(PyObject *self); -// - -//--------------------------------------------------------------------------- -// Testing functions. They belong to PyWraps and won't be copied to IDAPython -//--------------------------------------------------------------------------- - -static void NT_CDECL choose2_test_embedded(chooser_info_t *embedded) -{ - msg("cb=%d -> looks %valid\n", - embedded->cb, - embedded->cb == sizeof(chooser_info_t) ? "" : "in"); -} -static size_t choose2_get_test_embedded() -{ - return (size_t)choose2_test_embedded; -} -#endif // __PY_CHOOSE2__ +#ifndef __PY_CHOOSE2__ +#define __PY_CHOOSE2__ + +// + +//------------------------------------------------------------------------ +// Some defines +#define POPUP_NAMES_COUNT 4 +#define MAX_CHOOSER_MENU_COMMANDS 20 +#define thisobj ((py_choose2_t *) obj) +#define thisdecl py_choose2_t *_this = thisobj +#define MENU_COMMAND_CB(id) \ + static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) \ + { \ + return thisobj->on_command(id, int(n)); \ + } + +//------------------------------------------------------------------------ +// Helper functions +class py_choose2_t; +typedef std::map pychoose2_to_choose2_map_t; +static pychoose2_to_choose2_map_t choosers; + +py_choose2_t *choose2_find_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + return it == choosers.end() ? NULL : it->second; +} + +void choose2_add_instance(PyObject *self, py_choose2_t *c2) +{ + choosers[self] = c2; +} + +void choose2_del_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + if ( it != choosers.end() ) + choosers.erase(it); +} + +//------------------------------------------------------------------------ +class py_choose2_t +{ +private: + enum + { + CHOOSE2_HAVE_DEL = 0x0001, + CHOOSE2_HAVE_INS = 0x0002, + CHOOSE2_HAVE_UPDATE = 0x0004, + CHOOSE2_HAVE_EDIT = 0x0008, + CHOOSE2_HAVE_ENTER = 0x0010, + CHOOSE2_HAVE_GETICON = 0x0020, + CHOOSE2_HAVE_GETATTR = 0x0040, + CHOOSE2_HAVE_COMMAND = 0x0080, + CHOOSE2_HAVE_ONCLOSE = 0x0100, + CHOOSE2_HAVE_SELECT = 0x0200, + CHOOSE2_HAVE_REFRESHED = 0x0400, + }; + // Chooser flags + int flags; + + // Callback flags (to tell which callback exists and which not) + // One of CHOOSE2_HAVE_xxxx + unsigned int cb_flags; + chooser_info_t *embedded; + intvec_t embedded_sel; + + // Menu callback index (in the menu_cbs array) + int menu_cb_idx; + + // Chooser title + qstring title; + + // Column widths + intvec_t widths; + + // Python object link + PyObject *self; + // Chooser columns + qstrvec_t cols; + const char **popup_names; + bool ui_cb_hooked; + + // The number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value + MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) + MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) + MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) + MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) + MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) + MENU_COMMAND_CB(10) MENU_COMMAND_CB(11) + MENU_COMMAND_CB(12) MENU_COMMAND_CB(13) + MENU_COMMAND_CB(14) MENU_COMMAND_CB(15) + MENU_COMMAND_CB(16) MENU_COMMAND_CB(17) + MENU_COMMAND_CB(18) MENU_COMMAND_CB(19) + static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; + + //------------------------------------------------------------------------ + // Static methods to dispatch to member functions + //------------------------------------------------------------------------ + static int idaapi ui_cb(void *obj, int notification_code, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + // UI callback to handle chooser items with attributes + if ( notification_code != ui_get_chooser_item_attrs ) + return 0; + + // Pass events that belong to our chooser only + void *chooser_obj = va_arg(va, void *); + if ( obj != chooser_obj ) + return 0; + + int n = int(va_arg(va, uint32)); + chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); + thisobj->on_get_line_attr(n, attr); + return 1; + } + + static void idaapi s_select(void *obj, const intvec_t &sel) + { + thisobj->on_select(sel); + } + + static void idaapi s_refreshed(void *obj) + { + thisobj->on_refreshed(); + } + + static uint32 idaapi s_sizer(void *obj) + { + return (uint32)thisobj->on_get_size(); + } + + static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) + { + thisobj->on_get_line(int(n), arrptr); + } + + static uint32 idaapi s_del(void *obj, uint32 n) + { + return uint32(thisobj->on_delete_line(int(n))); + } + + static void idaapi s_ins(void *obj) + { + thisobj->on_insert_line(); + } + + static uint32 idaapi s_update(void *obj, uint32 n) + { + return uint32(thisobj->on_refresh(int(n))); + } + + static void idaapi s_edit(void *obj, uint32 n) + { + thisobj->on_edit_line(int(n)); + } + + static void idaapi s_enter(void * obj, uint32 n) + { + thisobj->on_enter(int(n)); + } + + static int idaapi s_get_icon(void *obj, uint32 n) + { + return thisobj->on_get_icon(int(n)); + } + + static void idaapi s_destroy(void *obj) + { + thisobj->on_close(); + } + + //------------------------------------------------------------------------ + // Member functions corresponding to each chooser2() callback + //------------------------------------------------------------------------ + void clear_popup_names() + { + if ( popup_names == NULL ) + return; + + for ( int i=0; i=0; i-- ) + line_arr[i][0] = '\0'; + + // Call Python + PYW_GIL_CHECK_LOCKED_SCOPE(); + pycall_res_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1)); + if ( list.result == NULL ) + return; + + // Go over the List returned by Python and convert to C strings + for ( int i=ncols-1; i>=0; i-- ) + { + borref_t item(PyList_GetItem(list.result.o, Py_ssize_t(i))); + if ( item == NULL ) + continue; + + const char *str = PyString_AsString(item.o); + if ( str != NULL ) + qstrncpy(line_arr[i], str, MAXSTR); + } + } + + size_t on_get_size() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL)); + if ( pyres.result == NULL ) + return 0; + + return PyInt_AsLong(pyres.result.o); + } + + void on_refreshed() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL)); + } + + void on_select(const intvec_t &intvec) + { + PYW_GIL_GET; + ref_t py_list(PyW_IntVecToPyList(intvec)); + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECTION_CHANGE, "O", py_list.o)); + } + + void on_close() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL)); + + // Delete this instance if none modal and not embedded + if ( !is_modal() && get_embedded() == NULL ) + delete this; + } + + int on_delete_line(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_DELETE_LINE, + "i", + IS_CHOOSER_EVENT(lineno) ? lineno : lineno-1)); + return pyres.result == NULL ? 1 : PyInt_AsLong(pyres.result.o); + } + + int on_refresh(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_REFRESH, + "i", + lineno - 1)); + return pyres.result == NULL ? lineno : PyInt_AsLong(pyres.result.o) + 1; + } + + void on_insert_line() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL)); + } + + void on_enter(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_SELECT_LINE, + "i", + lineno - 1)); + } + + void on_edit_line(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_EDIT_LINE, + "i", + lineno - 1)); + } + + int on_command(int cmd_id, int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_COMMAND, + "ii", + lineno - 1, + cmd_id)); + return pyres.result == NULL ? lineno : PyInt_AsLong(pyres.result.o); + } + + int on_get_icon(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_GET_ICON, + "i", + lineno - 1)); + return PyInt_AsLong(pyres.result.o); + } + + void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1)); + if ( pyres.result != NULL ) + { + if ( PyList_Check(pyres.result.o) ) + { + PyObject *item; + if ( (item = PyList_GetItem(pyres.result.o, 0)) != NULL ) + attr->color = PyInt_AsLong(item); + if ( (item = PyList_GetItem(pyres.result.o, 1)) != NULL ) + attr->flags = PyInt_AsLong(item); + } + } + } + + bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const + { + if ( get_embedded() != NULL ) + { + // For embedded chooser, the "caption" will be overloaded to encode + // the AskUsingForm's title, caption and embedded chooser id + // Title:EmbeddedChooserID:Caption + + char title_buf[MAXSTR]; + const char *ptitle; + + static const char delimiter[] = ":"; + char temp[MAXSTR]; + qstrncpy(temp, caption, sizeof(temp)); + + char *ctx; + char *p = qstrtok(temp, delimiter, &ctx); + if ( p == NULL ) + return false; + + // Copy the title + char title_str[MAXSTR]; + qstrncpy(title_str, p, sizeof(title_str)); + + // Copy the echooser ID + p = qstrtok(NULL, delimiter, &ctx); + if ( p == NULL ) + return false; + + char id_str[10]; + qstrncpy(id_str, p, sizeof(id_str)); + + // Form the new title of the form: "AskUsingFormTitle:EchooserId" + qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str); + + // Adjust the title + *out_title = title_buf; + + // Adjust the caption + p = qstrtok(NULL, delimiter, &ctx); + *out_caption = caption + (p - temp); + } + else + { + *out_title = title; + *out_caption = caption; + } + return true; + } + + // This must be called at the end of create(), when many dependencies + // have been computed (title, widths, popup_names, [cb_]flags, ...) + void fill_chooser_info( + chooser_info_t *out, + int deflt, + int desired_width, + int desired_height, + int icon) + { + memset(out, 0, sizeof(*out)); + out->obj = this; + out->cb = sizeof(*out); + out->title = title.c_str(); + out->columns = widths.size(); + out->deflt = deflt; + out->flags = flags; + out->width = desired_width; + out->height = desired_height; + out->icon = icon; + out->popup_names = popup_names; + out->widths = widths.begin(); + out->destroyer = s_destroy; + out->getl = s_getl; + out->sizer = s_sizer; + out->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL; + out->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL; + out->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL; + out->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL; + out->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL; + out->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL; + out->get_attrs = NULL; + out->initializer = NULL; + // Fill callbacks that are only present in idaq + if ( is_idaq() ) + { + out->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL; + out->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL; + } + else + { + out->select = NULL; + out->refresh = NULL; + } + } + +public: + //------------------------------------------------------------------------ + // Public methods + //------------------------------------------------------------------------ + py_choose2_t(): flags(0), cb_flags(0), + embedded(NULL), menu_cb_idx(0), + self(NULL), popup_names(NULL), ui_cb_hooked(false) + { + } + + ~py_choose2_t() + { + // Remove from list + choose2_del_instance(self); + + // Uninstall hooks + install_hooks(false); + + delete embedded; + Py_XDECREF(self); + clear_popup_names(); + } + + static py_choose2_t *find_chooser(const char *title) + { + return (py_choose2_t *) get_chooser_obj(title); + } + + void close() + { + // Will trigger on_close() + close_chooser(title.c_str()); + } + + bool activate() + { + TForm *frm = find_tform(title.c_str()); + if ( frm == NULL ) + return false; + + switchto_tform(frm, true); + return true; + } + + int add_command( + const char *_caption, + int flags=0, + int menu_index=-1, + int icon=-1) + { + if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) + return -1; + + qstring title, caption; + if ( !split_chooser_caption(&title, &caption, _caption) + || !add_chooser_command( + title.c_str(), + caption.c_str(), + menu_cbs[menu_cb_idx], + menu_index, + icon, + flags) ) + return -1; + + return menu_cb_idx++; + } + + // Create a chooser. + // If it detects the "embedded" attribute, then it will create a chooser_info_t structure + // Otherwise the chooser window is created and displayed + int create(PyObject *self) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Get flags + ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS)); + if ( flags_attr == NULL ) + return -1; + flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0; + + // Get the title + if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) + return -1; + + // Get columns + ref_t cols_attr(PyW_TryGetAttrString(self, "cols")); + if ( cols_attr == NULL ) + return -1; + + // Get col count + int ncols = int(PyList_Size(cols_attr.o)); + + // Get cols caption and widthes + cols.qclear(); + for ( int i=0; iself = self; + + // Hook to notification point (to handle chooser item attributes) + install_hooks(true); + + // Check if *embedded + ref_t emb_attr(PyW_TryGetAttrString(self, S_EMBEDDED)); + int rc; + if ( emb_attr != NULL && PyObject_IsTrue(emb_attr.o) == 1 ) + { + // Create an embedded chooser structure + embedded = new chooser_info_t(); + fill_chooser_info(embedded, deflt, pts[0], pts[1], icon); + rc = 1; // success + } + else + { + chooser_info_t ci; + fill_chooser_info(&ci, deflt, -1, -1, icon); + rc = choose3(&ci); + clear_popup_names(); + if ( is_modal() ) + --rc; // modal chooser return the index of the selected item + } + return rc; + } + + inline PyObject *get_self() + { + return self; + } + + void refresh() + { + refresh_chooser(title.c_str()); + } + + bool is_modal() + { + return (flags & CH_MODAL) != 0; + } + + intvec_t *get_sel_vec() + { + return &embedded_sel; + } + + chooser_info_t *get_embedded() const + { + return embedded; + } +}; + +//------------------------------------------------------------------------ +// Initialize the callback pointers +#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id +chooser_cb_t *py_choose2_t::menu_cbs[MAX_CHOOSER_MENU_COMMANDS] = +{ + DECL_MENU_COMMAND_CB(0), DECL_MENU_COMMAND_CB(1), + DECL_MENU_COMMAND_CB(2), DECL_MENU_COMMAND_CB(3), + DECL_MENU_COMMAND_CB(4), DECL_MENU_COMMAND_CB(5), + DECL_MENU_COMMAND_CB(6), DECL_MENU_COMMAND_CB(7), + DECL_MENU_COMMAND_CB(8), DECL_MENU_COMMAND_CB(9), + DECL_MENU_COMMAND_CB(10), DECL_MENU_COMMAND_CB(11), + DECL_MENU_COMMAND_CB(12), DECL_MENU_COMMAND_CB(13), + DECL_MENU_COMMAND_CB(14), DECL_MENU_COMMAND_CB(15), + DECL_MENU_COMMAND_CB(16), DECL_MENU_COMMAND_CB(17), + DECL_MENU_COMMAND_CB(18), DECL_MENU_COMMAND_CB(19) +}; +#undef DECL_MENU_COMMAND_CB + +#undef POPUP_NAMES_COUNT +#undef MAX_CHOOSER_MENU_COMMANDS +#undef thisobj +#undef thisdecl +#undef MENU_COMMAND_CB + +//------------------------------------------------------------------------ +int choose2_create(PyObject *self, bool embedded) +{ + py_choose2_t *c2; + + c2 = choose2_find_instance(self); + if ( c2 != NULL ) + { + if ( !embedded ) + c2->activate(); + return 1; + } + + c2 = new py_choose2_t(); + + choose2_add_instance(self, c2); + + int r = c2->create(self); + // Non embedded chooser? Return immediately + if ( !embedded ) + return r; + + // Embedded chooser was not created? + if ( c2->get_embedded() == NULL || r != 1 ) + { + delete c2; + r = 0; + } + return r; +} + +//------------------------------------------------------------------------ +void choose2_close(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 == NULL ) + return; + + // Modal or embedded chooser? + if ( c2->get_embedded() != NULL || c2->is_modal() ) + { + // Then simply delete the instance + delete c2; + } + else + { + // Close the chooser. + // In turn this will lead to the deletion of the object + c2->close(); + } +} + +//------------------------------------------------------------------------ +void choose2_refresh(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->refresh(); +} + +//------------------------------------------------------------------------ +void choose2_activate(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->activate(); +} + +//------------------------------------------------------------------------ +PyObject *choose2_get_embedded_selection(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_choose2_t *c2 = choose2_find_instance(self); + chooser_info_t *embedded; + + if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) + Py_RETURN_NONE; + + // Returned as 1-based + intvec_t &intvec = *c2->get_sel_vec(); + + // Make 0-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)--; + + ref_t ret(PyW_IntVecToPyList(intvec)); + ret.incref(); + return ret.o; +} + +//------------------------------------------------------------------------ +// Return the C instances as 64bit numbers +PyObject *choose2_get_embedded(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_choose2_t *c2 = choose2_find_instance(self); + chooser_info_t *embedded; + + if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) + Py_RETURN_NONE; + else + return Py_BuildValue("(KK)", + PY_ULONG_LONG(embedded), + PY_ULONG_LONG(c2->get_sel_vec())); +} + +//------------------------------------------------------------------------ +int choose2_add_command( + PyObject *self, + const char *caption, + int flags=0, + int menu_index=-1, + int icon=-1) +{ + py_choose2_t *c2 = choose2_find_instance(self); + return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon); +} + +//------------------------------------------------------------------------ +PyObject *choose2_find(const char *title) +{ + py_choose2_t *c2 = py_choose2_t::find_chooser(title); + return c2 == NULL ? NULL : c2->get_self(); +} +// + +//--------------------------------------------------------------------------- +// +PyObject *choose2_find(const char *title); +int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon); +void choose2_refresh(PyObject *self); +void choose2_close(PyObject *self); +int choose2_create(PyObject *self, bool embedded); +void choose2_activate(PyObject *self); +PyObject *choose2_get_embedded(PyObject *self); +PyObject *choose2_get_embedded_selection(PyObject *self); +// + +//--------------------------------------------------------------------------- +// Testing functions. They belong to PyWraps and won't be copied to IDAPython +//--------------------------------------------------------------------------- + +static void NT_CDECL choose2_test_embedded(chooser_info_t *embedded) +{ + msg("cb=%d -> looks %valid\n", + embedded->cb, + embedded->cb == sizeof(chooser_info_t) ? "" : "in"); +} +static size_t choose2_get_test_embedded() +{ + return (size_t)choose2_test_embedded; +} +#endif // __PY_CHOOSE2__ diff --git a/pywraps/py_choose2.py b/pywraps/py_choose2.py index dbce4d63..f809f092 100644 --- a/pywraps/py_choose2.py +++ b/pywraps/py_choose2.py @@ -1,408 +1,408 @@ -# ----------------------------------------------------------------------- -# Standalone and testing code -from ctypes import * - -try: - import _idaapi -except: - print("Please try me from inside IDA") - sys.exit(0) - -try: - import pywraps - pywraps_there = True - print("Choose2: using pywraps") - - _idaapi.choose2_create = pywraps.py_choose2_create - _idaapi.choose2_activate = pywraps.py_choose2_activate - _idaapi.choose2_refresh = pywraps.py_choose2_refresh - _idaapi.choose2_close = pywraps.py_choose2_close - _idaapi.choose2_add_command = pywraps.py_choose2_add_command - _idaapi.choose2_get_embedded = pywraps.py_choose2_get_embedded - _idaapi.choose2_get_embedded_selection = pywraps.py_choose2_get_embedded_selection - - try: - # Get function address - # void test_embedded(chooser_info_t *) - TEST_EMBEDDED = CFUNCTYPE(c_void_p, c_void_p) - test_embedded = TEST_EMBEDDED(pywraps.py_choose2_get_test_embedded()) - except Exception as e: - test_embedded = None - print("Choose2: Exception: %s" % str(e)) - -except Exception as e: - pywraps_there = False - print("Choose2: Not using pywraps: %s" % str(e)) - -# ----------------------------------------------------------------------- -# -class Choose2(object): - """ - Choose2 wrapper class. - - Some constants are defined in this class. Please refer to kernwin.hpp for more information. - """ - - CH_MODAL = 0x01 - """Modal chooser""" - - CH_MULTI = 0x02 - """Allow multi selection""" - - CH_MULTI_EDIT = 0x04 - CH_NOBTNS = 0x08 - CH_ATTRS = 0x10 - CH_NOIDB = 0x20 - """use the chooser even without an open database, same as x0=-2""" - CH_UTF8 = 0x40 - """string encoding is utf-8""" - - CH_BUILTIN_MASK = 0xF80000 - - # column flags (are specified in the widths array) - CHCOL_PLAIN = 0x00000000 - CHCOL_PATH = 0x00010000 - CHCOL_HEX = 0x00020000 - CHCOL_DEC = 0x00030000 - CHCOL_FORMAT = 0x00070000 - - - def __init__(self, title, cols, flags=0, popup_names=None, - icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, - embedded=False, width=None, height=None): - """ - Constructs a chooser window. - @param title: The chooser title - @param cols: a list of colums; each list item is a list of two items - example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ] - @param flags: One of CH_XXXX constants - @param deflt: Default starting item - @param popup_names: list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] - @param icon: Icon index (the icon should exist in ida resources or an index to a custom loaded icon) - @param x1, y1, x2, y2: The default location - @param embedded: Create as embedded chooser - @param width: Embedded chooser width - @param height: Embedded chooser height - """ - self.title = title - self.flags = flags - self.cols = cols - self.deflt = deflt - self.popup_names = popup_names - self.icon = icon - self.x1 = x1 - self.y1 = y1 - self.x2 = x2 - self.y2 = y2 - self.embedded = embedded - if embedded: - self.x1 = width - self.y1 = height - - - def Embedded(self): - """ - Creates an embedded chooser (as opposed to Show()) - @return: Returns 1 on success - """ - return _idaapi.choose2_create(self, True) - - - def GetEmbSelection(self): - """ - Returns the selection associated with an embedded chooser - - @return: - - None if chooser is not embedded - - A list with selection indices (0-based) - """ - return _idaapi.choose2_get_embedded_selection(self) - - - def Show(self, modal=False): - """ - Activates or creates a chooser window - @param modal: Display as modal dialog - @return: For modal choosers it will return the selected item index (0-based) - """ - if modal: - self.flags |= Choose2.CH_MODAL - - # Disable the timeout - old = _idaapi.set_script_timeout(0) - n = _idaapi.choose2_create(self, False) - _idaapi.set_script_timeout(old) - - # Delete the modal chooser instance - self.Close() - - return n - else: - self.flags &= ~Choose2.CH_MODAL - return _idaapi.choose2_create(self, False) - - - def Activate(self): - """Activates a visible chooser""" - return _idaapi.choose2_activate(self) - - - def Refresh(self): - """Causes the refresh callback to trigger""" - return _idaapi.choose2_refresh(self) - - - def Close(self): - """Closes the chooser""" - return _idaapi.choose2_close(self) - - - def AddCommand(self, - caption, - flags = _idaapi.CHOOSER_POPUP_MENU, - menu_index = -1, - icon = -1, - emb=None): - """ - Deprecated: Use - - register_action() - - attach_action_to_menu() - - attach_action_to_popup() - """ - # Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl - if self.embedded and ((emb is None) or (emb != 2002)): - raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()") - return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) - - # - # Implement these methods in the subclass: - # -# -# def OnClose(self): -# """ -# Called when the window is being closed. -# This callback is mandatory. -# @return: nothing -# """ -# pass -# -# def OnGetLine(self, n): -# """Called when the chooser window requires lines. -# This callback is mandatory. -# @param n: Line number (0-based) -# @return: The user should return a list with ncols elements. -# example: a list [col1, col2, col3, ...] describing the n-th line -# """ -# return ["col1 val", "col2 val"] -# -# def OnGetSize(self): -# """Returns the element count. -# This callback is mandatory. -# @return: Number of elements -# """ -# return len(self.the_list) -# -# def OnEditLine(self, n): -# """ -# Called when an item is being edited. -# @param n: Line number (0-based) -# @return: Nothing -# """ -# pass -# -# def OnInsertLine(self): -# """ -# Called when 'Insert' is selected either via the hotkey or popup menu. -# @return: Nothing -# """ -# pass -# -# def OnSelectLine(self, n): -# """ -# Called when a line is selected and then Ok or double click was pressed -# @param n: Line number (0-based) -# """ -# pass -# -# def OnSelectionChange(self, sel_list): -# """ -# Called when the selection changes -# @param sel_list: A list of selected item indices -# """ -# pass -# -# def OnDeleteLine(self, n): -# """ -# Called when a line is about to be deleted -# @param n: Line number (0-based) -# """ -# return self.n -# -# def OnRefresh(self, n): -# """ -# Triggered when the 'Refresh' is called from the popup menu item. -# -# @param n: The currently selected line (0-based) at the time of the refresh call -# @return: Return the number of elements -# """ -# return self.n -# -# def OnRefreshed(self): -# """ -# Triggered when a refresh happens (for example due to column sorting) -# @param n: Line number (0-based) -# @return: Return the number of elements -# """ -# return self.n -# -# def OnCommand(self, n, cmd_id): -# """Return int ; check add_chooser_command()""" -# return 0 -# -# def OnGetIcon(self, n): -# """ -# Return icon number for a given item (or -1 if no icon is avail) -# @param n: Line number (0-based) -# """ -# return -1 -# -# def OnGetLineAttr(self, n): -# """ -# Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t -# @param n: Line number (0-based) -# """ -# return [0x0, CHITEM_BOLD] -# -# - -# ----------------------------------------------------------------------- -# - - -class chooser_handler_t(idaapi.action_handler_t): - def __init__(self, thing): - idaapi.action_handler_t.__init__(self) - self.thing = thing - - def activate(self, ctx): - sel = [] - for i in xrange(len(ctx.chooser_selection)): - sel.append(str(ctx.chooser_selection.at(i))) - print "command %s selected @ %s" % (self.thing, ", ".join(sel)) - - def update(self, ctx): - return idaapi.AST_ENABLE_FOR_FORM if idaapi.is_chooser_tform(ctx.form_type) else idaapi.AST_DISABLE_FOR_FORM - - -class MyChoose2(Choose2): - - def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False): - Choose2.__init__( - self, - title, - [ ["Address", 10], ["Name", 30] ], - flags = flags, - width = width, - height = height, - embedded = embedded) - self.n = 0 - self.items = [ self.make_item() for x in xrange(0, nb+1) ] - self.icon = 5 - self.selcount = 0 - self.modal = modal - self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"] - - print("created %s" % str(self)) - - def OnClose(self): - print "closed", str(self) - - def OnEditLine(self, n): - self.items[n][1] = self.items[n][1] + "*" - print("editing %d" % n) - - def OnInsertLine(self): - self.items.append(self.make_item()) - print("insert line") - - def OnSelectLine(self, n): - self.selcount += 1 - Warning("[%02d] selectline '%s'" % (self.selcount, n)) - - def OnGetLine(self, n): - print("getline %d" % n) - return self.items[n] - - def OnGetSize(self): - n = len(self.items) - print("getsize -> %d" % n) - return n - - def OnDeleteLine(self, n): - print("del %d " % n) - del self.items[n] - return n - - def OnRefresh(self, n): - print("refresh %d" % n) - return n - - def OnGetIcon(self, n): - r = self.items[n] - t = self.icon + r[1].count("*") - print "geticon", n, t - return t - - def show(self): - return self.Show(self.modal) >= 0 - - def make_item(self): - r = [str(self.n), "func_%04d" % self.n] - self.n += 1 - return r - - def OnGetLineAttr(self, n): - print("getlineattr %d" % n) - if n == 1: - return [0xFF0000, 0] - - -# ----------------------------------------------------------------------- -def test_choose2(modal=False): - global c - c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal) - r = c.show() - form = idaapi.get_current_tform() - for thing in ["A", "B"]: - idaapi.attach_action_to_popup(form, None, "choose2:act%s" % thing) - -# ----------------------------------------------------------------------- -def test_choose2_embedded(): - global c - c = MyChoose2("Choose2 - embedded", nb=12, embedded = True, width=123, height=222) - r = c.Embedded() - if r == 1: - try: - if test_embedded: - o, sel = _idaapi.choose2_get_embedded(c) - print("o=%s, type(o)=%s" % (str(o), type(o))) - test_embedded(o) - finally: - c.Close() - -# ----------------------------------------------------------------------- -if __name__ == '__main__': - - # Register actions - for thing in ["A", "B"]: - actname = "choose2:act%s" % thing - idaapi.register_action( - idaapi.action_desc_t( - actname, - "command %s" % thing, - chooser_handler_t(thing))) - - #test_choose2_embedded() - test_choose2(False) - -# +# ----------------------------------------------------------------------- +# Standalone and testing code +from ctypes import * + +try: + import _idaapi +except: + print("Please try me from inside IDA") + sys.exit(0) + +try: + import pywraps + pywraps_there = True + print("Choose2: using pywraps") + + _idaapi.choose2_create = pywraps.py_choose2_create + _idaapi.choose2_activate = pywraps.py_choose2_activate + _idaapi.choose2_refresh = pywraps.py_choose2_refresh + _idaapi.choose2_close = pywraps.py_choose2_close + _idaapi.choose2_add_command = pywraps.py_choose2_add_command + _idaapi.choose2_get_embedded = pywraps.py_choose2_get_embedded + _idaapi.choose2_get_embedded_selection = pywraps.py_choose2_get_embedded_selection + + try: + # Get function address + # void test_embedded(chooser_info_t *) + TEST_EMBEDDED = CFUNCTYPE(c_void_p, c_void_p) + test_embedded = TEST_EMBEDDED(pywraps.py_choose2_get_test_embedded()) + except Exception as e: + test_embedded = None + print("Choose2: Exception: %s" % str(e)) + +except Exception as e: + pywraps_there = False + print("Choose2: Not using pywraps: %s" % str(e)) + +# ----------------------------------------------------------------------- +# +class Choose2(object): + """ + Choose2 wrapper class. + + Some constants are defined in this class. Please refer to kernwin.hpp for more information. + """ + + CH_MODAL = 0x01 + """Modal chooser""" + + CH_MULTI = 0x02 + """Allow multi selection""" + + CH_MULTI_EDIT = 0x04 + CH_NOBTNS = 0x08 + CH_ATTRS = 0x10 + CH_NOIDB = 0x20 + """use the chooser even without an open database, same as x0=-2""" + CH_UTF8 = 0x40 + """string encoding is utf-8""" + + CH_BUILTIN_MASK = 0xF80000 + + # column flags (are specified in the widths array) + CHCOL_PLAIN = 0x00000000 + CHCOL_PATH = 0x00010000 + CHCOL_HEX = 0x00020000 + CHCOL_DEC = 0x00030000 + CHCOL_FORMAT = 0x00070000 + + + def __init__(self, title, cols, flags=0, popup_names=None, + icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, + embedded=False, width=None, height=None): + """ + Constructs a chooser window. + @param title: The chooser title + @param cols: a list of colums; each list item is a list of two items + example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ] + @param flags: One of CH_XXXX constants + @param deflt: Default starting item + @param popup_names: list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] + @param icon: Icon index (the icon should exist in ida resources or an index to a custom loaded icon) + @param x1, y1, x2, y2: The default location + @param embedded: Create as embedded chooser + @param width: Embedded chooser width + @param height: Embedded chooser height + """ + self.title = title + self.flags = flags + self.cols = cols + self.deflt = deflt + self.popup_names = popup_names + self.icon = icon + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + self.embedded = embedded + if embedded: + self.x1 = width + self.y1 = height + + + def Embedded(self): + """ + Creates an embedded chooser (as opposed to Show()) + @return: Returns 1 on success + """ + return _idaapi.choose2_create(self, True) + + + def GetEmbSelection(self): + """ + Returns the selection associated with an embedded chooser + + @return: + - None if chooser is not embedded + - A list with selection indices (0-based) + """ + return _idaapi.choose2_get_embedded_selection(self) + + + def Show(self, modal=False): + """ + Activates or creates a chooser window + @param modal: Display as modal dialog + @return: For modal choosers it will return the selected item index (0-based) + """ + if modal: + self.flags |= Choose2.CH_MODAL + + # Disable the timeout + old = _idaapi.set_script_timeout(0) + n = _idaapi.choose2_create(self, False) + _idaapi.set_script_timeout(old) + + # Delete the modal chooser instance + self.Close() + + return n + else: + self.flags &= ~Choose2.CH_MODAL + return _idaapi.choose2_create(self, False) + + + def Activate(self): + """Activates a visible chooser""" + return _idaapi.choose2_activate(self) + + + def Refresh(self): + """Causes the refresh callback to trigger""" + return _idaapi.choose2_refresh(self) + + + def Close(self): + """Closes the chooser""" + return _idaapi.choose2_close(self) + + + def AddCommand(self, + caption, + flags = _idaapi.CHOOSER_POPUP_MENU, + menu_index = -1, + icon = -1, + emb=None): + """ + Deprecated: Use + - register_action() + - attach_action_to_menu() + - attach_action_to_popup() + """ + # Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl + if self.embedded and ((emb is None) or (emb != 2002)): + raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()") + return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) + + # + # Implement these methods in the subclass: + # +# +# def OnClose(self): +# """ +# Called when the window is being closed. +# This callback is mandatory. +# @return: nothing +# """ +# pass +# +# def OnGetLine(self, n): +# """Called when the chooser window requires lines. +# This callback is mandatory. +# @param n: Line number (0-based) +# @return: The user should return a list with ncols elements. +# example: a list [col1, col2, col3, ...] describing the n-th line +# """ +# return ["col1 val", "col2 val"] +# +# def OnGetSize(self): +# """Returns the element count. +# This callback is mandatory. +# @return: Number of elements +# """ +# return len(self.the_list) +# +# def OnEditLine(self, n): +# """ +# Called when an item is being edited. +# @param n: Line number (0-based) +# @return: Nothing +# """ +# pass +# +# def OnInsertLine(self): +# """ +# Called when 'Insert' is selected either via the hotkey or popup menu. +# @return: Nothing +# """ +# pass +# +# def OnSelectLine(self, n): +# """ +# Called when a line is selected and then Ok or double click was pressed +# @param n: Line number (0-based) +# """ +# pass +# +# def OnSelectionChange(self, sel_list): +# """ +# Called when the selection changes +# @param sel_list: A list of selected item indices +# """ +# pass +# +# def OnDeleteLine(self, n): +# """ +# Called when a line is about to be deleted +# @param n: Line number (0-based) +# """ +# return self.n +# +# def OnRefresh(self, n): +# """ +# Triggered when the 'Refresh' is called from the popup menu item. +# +# @param n: The currently selected line (0-based) at the time of the refresh call +# @return: Return the number of elements +# """ +# return self.n +# +# def OnRefreshed(self): +# """ +# Triggered when a refresh happens (for example due to column sorting) +# @param n: Line number (0-based) +# @return: Return the number of elements +# """ +# return self.n +# +# def OnCommand(self, n, cmd_id): +# """Return int ; check add_chooser_command()""" +# return 0 +# +# def OnGetIcon(self, n): +# """ +# Return icon number for a given item (or -1 if no icon is avail) +# @param n: Line number (0-based) +# """ +# return -1 +# +# def OnGetLineAttr(self, n): +# """ +# Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t +# @param n: Line number (0-based) +# """ +# return [0x0, CHITEM_BOLD] +# +# + +# ----------------------------------------------------------------------- +# + + +class chooser_handler_t(idaapi.action_handler_t): + def __init__(self, thing): + idaapi.action_handler_t.__init__(self) + self.thing = thing + + def activate(self, ctx): + sel = [] + for i in xrange(len(ctx.chooser_selection)): + sel.append(str(ctx.chooser_selection.at(i))) + print "command %s selected @ %s" % (self.thing, ", ".join(sel)) + + def update(self, ctx): + return idaapi.AST_ENABLE_FOR_FORM if idaapi.is_chooser_tform(ctx.form_type) else idaapi.AST_DISABLE_FOR_FORM + + +class MyChoose2(Choose2): + + def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False): + Choose2.__init__( + self, + title, + [ ["Address", 10], ["Name", 30] ], + flags = flags, + width = width, + height = height, + embedded = embedded) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + self.modal = modal + self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"] + + print("created %s" % str(self)) + + def OnClose(self): + print "closed", str(self) + + def OnEditLine(self, n): + self.items[n][1] = self.items[n][1] + "*" + print("editing %d" % n) + + def OnInsertLine(self): + self.items.append(self.make_item()) + print("insert line") + + def OnSelectLine(self, n): + self.selcount += 1 + Warning("[%02d] selectline '%s'" % (self.selcount, n)) + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + print("getsize -> %d" % n) + return n + + def OnDeleteLine(self, n): + print("del %d " % n) + del self.items[n] + return n + + def OnRefresh(self, n): + print("refresh %d" % n) + return n + + def OnGetIcon(self, n): + r = self.items[n] + t = self.icon + r[1].count("*") + print "geticon", n, t + return t + + def show(self): + return self.Show(self.modal) >= 0 + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnGetLineAttr(self, n): + print("getlineattr %d" % n) + if n == 1: + return [0xFF0000, 0] + + +# ----------------------------------------------------------------------- +def test_choose2(modal=False): + global c + c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal) + r = c.show() + form = idaapi.get_current_tform() + for thing in ["A", "B"]: + idaapi.attach_action_to_popup(form, None, "choose2:act%s" % thing) + +# ----------------------------------------------------------------------- +def test_choose2_embedded(): + global c + c = MyChoose2("Choose2 - embedded", nb=12, embedded = True, width=123, height=222) + r = c.Embedded() + if r == 1: + try: + if test_embedded: + o, sel = _idaapi.choose2_get_embedded(c) + print("o=%s, type(o)=%s" % (str(o), type(o))) + test_embedded(o) + finally: + c.Close() + +# ----------------------------------------------------------------------- +if __name__ == '__main__': + + # Register actions + for thing in ["A", "B"]: + actname = "choose2:act%s" % thing + idaapi.register_action( + idaapi.action_desc_t( + actname, + "command %s" % thing, + chooser_handler_t(thing))) + + #test_choose2_embedded() + test_choose2(False) + +# diff --git a/pywraps/py_cli.hpp b/pywraps/py_cli.hpp index 7c241927..7cc3a567 100644 --- a/pywraps/py_cli.hpp +++ b/pywraps/py_cli.hpp @@ -1,295 +1,295 @@ -#ifndef __PYWRAPS_CLI__ -#define __PYWRAPS_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, - int *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, int *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) - { - PYW_GIL_GET; - newref_t result( - PyObject_CallMethod( - self, - (char *)S_ON_EXECUTE_LINE, - "s", - line)); - PyW_ShowCbErr(S_ON_EXECUTE_LINE); - return result != NULL && PyObject_IsTrue(result.o); - } - - //-------------------------------------------------------------------------- - // 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, - int *vk_key, - int shift) - { - PYW_GIL_GET; - newref_t 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.o); - - PyW_ShowCbErr(S_ON_KEYDOWN); - - if ( ok ) - { - Py_ssize_t sz = PyTuple_Size(result.o); - PyObject *item; - -#define GET_TUPLE_ENTRY(col, PyThingy, AsThingy, out) \ - do \ - { \ - if ( sz > col ) \ - { \ - borref_t _r(PyTuple_GetItem(result.o, col)); \ - if ( _r != NULL && PyThingy##_Check(_r.o) ) \ - *out = PyThingy##_##AsThingy(_r.o); \ - } \ - } while ( false ) - - GET_TUPLE_ENTRY(0, PyString, AsString, line); - GET_TUPLE_ENTRY(1, PyInt, AsLong, p_x); - GET_TUPLE_ENTRY(2, PyInt, AsLong, p_sellen); - GET_TUPLE_ENTRY(3, PyInt, AsLong, vk_key); - *vk_key &= 0xffff; -#undef GET_TUPLE_ENTRY - } - 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) - { - PYW_GIL_GET; - newref_t result( - PyObject_CallMethod( - self, - (char *)S_ON_COMPLETE_LINE, - "sisi", - prefix, - n, - line, - x)); - - bool ok = result != NULL && PyString_Check(result.o); - PyW_ShowCbErr(S_ON_COMPLETE_LINE); - if ( ok ) - *completion = PyString_AsString(result.o); - return ok; - } - - // Private ctor (use bind()) - py_cli_t() - { - } - -public: - //--------------------------------------------------------------------------- - static int bind(PyObject *py_obj) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - 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' - { - ref_t flags_attr(PyW_TryGetAttrString(py_obj, S_FLAGS)); - if ( flags_attr == NULL ) - py_cli->cli.flags = 0; - else - py_cli->cli.flags = PyLong_AsLong(flags_attr.o); - } - - // 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); - - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - 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 -// - -//-------------------------------------------------------------------------- - -// -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); -} -// -//--------------------------------------------------------------------------- -#endif // __PYWRAPS_CLI__ +#ifndef __PYWRAPS_CLI__ +#define __PYWRAPS_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, + int *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, int *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) + { + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_EXECUTE_LINE, + "s", + line)); + PyW_ShowCbErr(S_ON_EXECUTE_LINE); + return result != NULL && PyObject_IsTrue(result.o); + } + + //-------------------------------------------------------------------------- + // 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, + int *vk_key, + int shift) + { + PYW_GIL_GET; + newref_t 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.o); + + PyW_ShowCbErr(S_ON_KEYDOWN); + + if ( ok ) + { + Py_ssize_t sz = PyTuple_Size(result.o); + PyObject *item; + +#define GET_TUPLE_ENTRY(col, PyThingy, AsThingy, out) \ + do \ + { \ + if ( sz > col ) \ + { \ + borref_t _r(PyTuple_GetItem(result.o, col)); \ + if ( _r != NULL && PyThingy##_Check(_r.o) ) \ + *out = PyThingy##_##AsThingy(_r.o); \ + } \ + } while ( false ) + + GET_TUPLE_ENTRY(0, PyString, AsString, line); + GET_TUPLE_ENTRY(1, PyInt, AsLong, p_x); + GET_TUPLE_ENTRY(2, PyInt, AsLong, p_sellen); + GET_TUPLE_ENTRY(3, PyInt, AsLong, vk_key); + *vk_key &= 0xffff; +#undef GET_TUPLE_ENTRY + } + 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) + { + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_COMPLETE_LINE, + "sisi", + prefix, + n, + line, + x)); + + bool ok = result != NULL && PyString_Check(result.o); + PyW_ShowCbErr(S_ON_COMPLETE_LINE); + if ( ok ) + *completion = PyString_AsString(result.o); + return ok; + } + + // Private ctor (use bind()) + py_cli_t() + { + } + +public: + //--------------------------------------------------------------------------- + static int bind(PyObject *py_obj) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + 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' + { + ref_t flags_attr(PyW_TryGetAttrString(py_obj, S_FLAGS)); + if ( flags_attr == NULL ) + py_cli->cli.flags = 0; + else + py_cli->cli.flags = PyLong_AsLong(flags_attr.o); + } + + // 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); + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + 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 +// + +//-------------------------------------------------------------------------- + +// +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); +} +// +//--------------------------------------------------------------------------- +#endif // __PYWRAPS_CLI__ diff --git a/pywraps/py_cli.py b/pywraps/py_cli.py index 354f13f5..2f6f4cdf 100644 --- a/pywraps/py_cli.py +++ b/pywraps/py_cli.py @@ -1,187 +1,187 @@ -# ----------------------------------------------------------------------- -# Standalone and testing code -import sys -try: - import pywraps - pywraps_there = True - print "Using pywraps" -except: - pywraps_there = False - print "Using IDAPython" - -try: - import _idaapi - from idaapi import pyidc_opaque_object_t -except: - print "Please run this script from inside IDA" - sys.exit(0) - -if pywraps_there: - _idaapi.install_command_interpreter = pywraps.install_command_interpreter - _idaapi.remove_command_interpreter = pywraps.remove_command_interpreter - -# ----------------------------------------------------------------------- -# -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: - # -# -# 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 prefix_start (string) -# @param n: completion number (int) -# @param line: the current line (string) -# @param prefix_start: the index where PREFIX starts in LINE (int) -# -# @return: None if no completion could be generated otherwise a String with the completion suggestion -# """ -# return None -# - -# - -# ----------------------------------------------------------------------- -# -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 prefix_start (string) - @param n: completion number (int) - @param line: the current line (string) - @param prefix_start: the index where PREFIX starts in LINE (int) - - @return: None if no completion could be generated otherwise a String with the completion suggestion - """ - print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start) - return None -# -# ----------------------------------------------------------------------- +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Using IDAPython" + +try: + import _idaapi + from idaapi import pyidc_opaque_object_t +except: + print "Please run this script from inside IDA" + sys.exit(0) + +if pywraps_there: + _idaapi.install_command_interpreter = pywraps.install_command_interpreter + _idaapi.remove_command_interpreter = pywraps.remove_command_interpreter + +# ----------------------------------------------------------------------- +# +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: + # +# +# 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 prefix_start (string) +# @param n: completion number (int) +# @param line: the current line (string) +# @param prefix_start: the index where PREFIX starts in LINE (int) +# +# @return: None if no completion could be generated otherwise a String with the completion suggestion +# """ +# return None +# + +# + +# ----------------------------------------------------------------------- +# +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 prefix_start (string) + @param n: completion number (int) + @param line: the current line (string) + @param prefix_start: the index where PREFIX starts in LINE (int) + + @return: None if no completion could be generated otherwise a String with the completion suggestion + """ + print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start) + return None +# +# ----------------------------------------------------------------------- diff --git a/pywraps/py_custdata.hpp b/pywraps/py_custdata.hpp index 6f4bbeca..f578b48a 100644 --- a/pywraps/py_custdata.hpp +++ b/pywraps/py_custdata.hpp @@ -1,683 +1,683 @@ -#ifndef __PY_IDA_CUSTDATA__ -#define __PY_IDA_CUSTDATA__ - -// - -//------------------------------------------------------------------------ -class py_custom_data_type_t -{ - data_type_t dt; - qstring dt_name, dt_menu_name, dt_hotkey, dt_asm_keyword; - int dtid; // The data format id - PyObject *py_self; // Associated Python object - - // may create data? NULL means always may - static bool idaapi s_may_create_at( - void *ud, // user-defined data - ea_t ea, // address of the future item - size_t nbytes) // size of the future item - { - py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - - PYW_GIL_GET; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_MAY_CREATE_AT, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(nbytes))); - - PyW_ShowCbErr(S_MAY_CREATE_AT); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - // !=NULL means variable size datatype - static asize_t idaapi s_calc_item_size( - // This function is used to determine - // size of the (possible) item at 'ea' - void *ud, // user-defined data - ea_t ea, // address of the item - asize_t maxsize) // maximal size of the item - { - PYW_GIL_GET; - // Returns: 0-no such item can be created/displayed - // this callback is required only for varsize datatypes - py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_CALC_ITEM_SIZE, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(maxsize))); - - if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) - return 0; - - uint64 num = 0; - PyW_GetNumber(py_result.o, &num); - return asize_t(num); - } - -public: - const char *get_name() const - { - return dt_name.c_str(); - } - - py_custom_data_type_t() - { - dtid = -1; - py_self = NULL; - } - - int register_dt(PyObject *py_obj) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Already registered? - if ( dtid >= 0 ) - return dtid; - - memset(&dt, 0, sizeof(dt)); - dt.cbsize = sizeof(dt); - dt.ud = this; - - do - { - ref_t py_attr; - - // name - if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) - break; - - dt.name = dt_name.c_str(); - - // menu_name (optional) - if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &dt_menu_name) ) - dt.menu_name = dt_menu_name.c_str(); - - // asm_keyword (optional) - if ( PyW_GetStringAttr(py_obj, S_ASM_KEYWORD, &dt_asm_keyword) ) - dt.asm_keyword = dt_asm_keyword.c_str(); - - // hotkey (optional) - if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &dt_hotkey) ) - dt.hotkey = dt_hotkey.c_str(); - - // value_size - py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - dt.value_size = PyInt_AsLong(py_attr.o); - py_attr = ref_t(); - - // props - py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - dt.props = PyInt_AsLong(py_attr.o); - py_attr = ref_t(); - - // may_create_at - py_attr = PyW_TryGetAttrString(py_obj, S_MAY_CREATE_AT); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - dt.may_create_at = s_may_create_at; - py_attr = ref_t(); - - // calc_item_size - py_attr = PyW_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - dt.calc_item_size = s_calc_item_size; - py_attr = ref_t(); - - // Now try to register - dtid = register_custom_data_type(&dt); - if ( dtid < 0 ) - break; - - // Hold reference to the PyObject - Py_INCREF(py_obj); - py_self = py_obj; - - py_attr = newref_t(PyInt_FromLong(dtid)); - PyObject_SetAttrString(py_obj, S_ID, py_attr.o); - } while ( false ); - return dtid; - } - - bool unregister_dt() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( dtid < 0 ) - return true; - - if ( !unregister_custom_data_type(dtid) ) - return false; - - // Release reference of Python object - Py_XDECREF(py_self); - py_self = NULL; - dtid = -1; - return true; - } - - ~py_custom_data_type_t() - { - unregister_dt(); - } -}; -typedef std::map py_custom_data_type_map_t; -static py_custom_data_type_map_t py_dt_map; - -//------------------------------------------------------------------------ -class py_custom_data_format_t -{ -private: - data_format_t df; - int dfid; - PyObject *py_self; - qstring df_name, df_menu_name, df_hotkey; - - static bool idaapi s_print( // convert to colored string - void *ud, // user-defined data - qstring *out, // output buffer. may be NULL - const void *value, // value to print. may not be NULL - asize_t size, // size of value in bytes - ea_t current_ea, // current address (BADADDR if unknown) - int operand_num, // current operand number - int dtid) // custom data type id - { - PYW_GIL_GET; - - // Build a string from the buffer - newref_t py_value(PyString_FromStringAndSize( - (const char *)value, - Py_ssize_t(size))); - if ( py_value == NULL ) - return false; - - py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - newref_t py_result(PyObject_CallMethod( - _this->py_self, - (char *)S_PRINTF, - "O" PY_FMT64 "ii", - py_value.o, - pyul_t(current_ea), - operand_num, - dtid)); - - // Error while calling the function? - if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL ) - return false; - - bool ok = false; - if ( PyString_Check(py_result.o) ) - { - Py_ssize_t len; - char *buf; - if ( out != NULL && PyString_AsStringAndSize(py_result.o, &buf, &len) != -1 ) - { - out->qclear(); - out->append(buf, len); - } - ok = true; - } - return ok; - } - - static bool idaapi s_scan( // convert from uncolored string - void *ud, // user-defined data - bytevec_t *value, // output buffer. may be NULL - const char *input, // input string. may not be NULL - ea_t current_ea, // current address (BADADDR if unknown) - int operand_num, // current operand number (-1 if unknown) - qstring *errstr) // buffer for error message - { - PYW_GIL_GET; - - py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_SCAN, - "s" PY_FMT64, - input, - pyul_t(current_ea), - operand_num)); - - // Error while calling the function? - if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL) - return false; - - bool ok = false; - do - { - // We expect a tuple(bool, string|None) - if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) - break; - - borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); - borref_t py_val(PyTuple_GetItem(py_result.o, 1)); - - // Get return code from Python - ok = PyObject_IsTrue(py_bool.o); - - // We expect None or the value (depending on probe) - if ( ok ) - { - // Probe-only? Then okay, no need to extract the 'value' - if ( value == NULL ) - break; - - Py_ssize_t len; - char *buf; - if ( PyString_AsStringAndSize(py_val.o, &buf, &len) != -1 ) - { - value->qclear(); - value->append(buf, len); - } - } - // An error occured? - else - { - // Make sure the user returned (False, String) - if ( py_bool.o != Py_False || !PyString_Check(py_val.o) ) - { - *errstr = "Invalid return value returned from the Python callback!"; - break; - } - // Get the error message - *errstr = PyString_AsString(py_val.o); - } - } while ( false ); - return ok; - } - - static void idaapi s_analyze( // analyze custom data format occurrence - void *ud, // user-defined data - ea_t current_ea, // current address (BADADDR if unknown) - int operand_num) // current operand number - // this callback can be used to create - // xrefs from the current item. - // this callback may be missing. - { - PYW_GIL_GET; - - py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_ANALYZE, - PY_FMT64 "i", - pyul_t(current_ea), - operand_num)); - - PyW_ShowCbErr(S_ANALYZE); - } -public: - py_custom_data_format_t() - { - dfid = -1; - py_self = NULL; - } - - const char *get_name() const - { - return df_name.c_str(); - } - - int register_df(int dtid, PyObject *py_obj) - { - // Already registered? - if ( dfid >= 0 ) - return dfid; - - memset(&df, 0, sizeof(df)); - df.cbsize = sizeof(df); - df.ud = this; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - ref_t py_attr; - - // name - if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) - break; - df.name = df_name.c_str(); - - // menu_name (optional) - if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &df_menu_name) ) - df.menu_name = df_menu_name.c_str(); - - // props - py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - df.props = PyInt_AsLong(py_attr.o); - - // hotkey - if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) - df.hotkey = df_hotkey.c_str(); - - // value_size - py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - df.value_size = PyInt_AsLong(py_attr.o); - - // text_width - py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - df.text_width = PyInt_AsLong(py_attr.o); - - // print cb - py_attr = PyW_TryGetAttrString(py_obj, S_PRINTF); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - df.print = s_print; - - // scan cb - py_attr = PyW_TryGetAttrString(py_obj, S_SCAN); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - df.scan = s_scan; - - // analyze cb - py_attr = PyW_TryGetAttrString(py_obj, S_ANALYZE); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - df.analyze = s_analyze; - - // Now try to register - dfid = register_custom_data_format(dtid, &df); - if ( dfid < 0 ) - break; - - // Hold reference to the PyObject - Py_INCREF(py_obj); - py_self = py_obj; - - // Update the format ID - py_attr = newref_t(PyInt_FromLong(dfid)); - PyObject_SetAttrString(py_obj, S_ID, py_attr.o); - } while ( false ); - return dfid; - } - - bool unregister_df(int dtid) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Never registered? - if ( dfid < 0 ) - return true; - - if ( !unregister_custom_data_format(dtid, dfid) ) - return false; - - // Release reference of Python object - Py_XDECREF(py_self); - py_self = NULL; - dfid = -1; - return true; - } - - ~py_custom_data_format_t() - { - } -}; - -//------------------------------------------------------------------------ -// Helper class to bind pairs to py_custom_data_format_t -class py_custom_data_format_list_t -{ - struct py_custom_data_format_entry_t - { - int dtid; - int dfid; - py_custom_data_format_t *df; - }; - typedef qvector ENTRY; - ENTRY entries; -public: - typedef ENTRY::iterator POS; - void add(int dtid, int dfid, py_custom_data_format_t *df) - { - py_custom_data_format_entry_t &e = entries.push_back(); - e.dtid = dtid; - e.dfid = dfid; - e.df = df; - } - py_custom_data_format_t *find(int dtid, int dfid, POS *loc = NULL) - { - for ( POS it=entries.begin(), it_end = entries.end(); it!=it_end; ++it ) - { - if ( it->dfid == dfid && it->dtid == dtid ) - { - if ( loc != NULL ) - *loc = it; - return it->df; - } - } - return NULL; - } - void erase(POS &pos) - { - entries.erase(pos); - } -}; -static py_custom_data_format_list_t py_df_list; - -//------------------------------------------------------------------------ -static PyObject *py_data_type_to_py_dict(const data_type_t *dt) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", - S_VALUE_SIZE, pyul_t(dt->value_size), - S_PROPS, dt->props, - S_CBSIZE, dt->cbsize, - S_NAME, dt->name == NULL ? "" : dt->name, - S_MENU_NAME, dt->menu_name == NULL ? "" : dt->menu_name, - S_HOTKEY, dt->hotkey == NULL ? "" : dt->hotkey, - S_ASM_KEYWORD, dt->asm_keyword == NULL ? "" : dt->asm_keyword); -} - -//------------------------------------------------------------------------ -static PyObject *py_data_format_to_py_dict(const data_format_t *df) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", - S_PROPS, df->props, - S_CBSIZE, df->cbsize, - S_TEXT_WIDTH, df->text_width, - S_VALUE_SIZE, pyul_t(df->value_size), - S_NAME, df->name == NULL ? "" : df->name, - S_MENU_NAME, df->menu_name == NULL ? "" : df->menu_name, - S_HOTKEY, df->hotkey == NULL ? "" : df->hotkey); -} -// - -//------------------------------------------------------------------------ -// - -//------------------------------------------------------------------------ -/* -# -def register_custom_data_type(dt): - """ - Registers a custom data type. - @param dt: an instance of the data_type_t class - @return: - < 0 if failed to register - > 0 data type id - """ - pass -# -*/ -// Given a py.data_format_t object, this function will register a datatype -static int py_register_custom_data_type(PyObject *py_dt) -{ - py_custom_data_type_t *inst = new py_custom_data_type_t(); - int r = inst->register_dt(py_dt); - if ( r < 0 ) - { - delete inst; - return r; - } - // Insert the instance to the map - py_dt_map[r] = inst; - return r; -} - -//------------------------------------------------------------------------ -/* -# -def unregister_custom_data_type(dtid): - """ - Unregisters a custom data type. - @param dtid: the data type id - @return: Boolean - """ - pass -# -*/ -static bool py_unregister_custom_data_type(int dtid) -{ - py_custom_data_type_map_t::iterator it = py_dt_map.find(dtid); - - // Maybe the user is trying to unregister a C api dt? - if ( it == py_dt_map.end() ) - return unregister_custom_data_type(dtid); - - py_custom_data_type_t *inst = it->second; - bool ok = inst->unregister_dt(); - - // Perhaps it was automatically unregistered because the idb was close? - if ( !ok ) - { - // Is this type still registered with IDA? - // If not found then mark the context for deletion - ok = find_custom_data_type(inst->get_name()) < 0; - } - - if ( ok ) - { - py_dt_map.erase(it); - delete inst; - } - return ok; -} - -//------------------------------------------------------------------------ -/* -# -def register_custom_data_format(dtid, df): - """ - Registers a custom data format with a given data type. - @param dtid: data type id - @param df: an instance of data_format_t - @return: - < 0 if failed to register - > 0 data format id - """ - pass -# -*/ -static int py_register_custom_data_format(int dtid, PyObject *py_df) -{ - py_custom_data_format_t *inst = new py_custom_data_format_t(); - int r = inst->register_df(dtid, py_df); - if ( r < 0 ) - { - delete inst; - return r; - } - // Insert the instance - py_df_list.add(dtid, r, inst); - return r; -} - -//------------------------------------------------------------------------ -/* -# -def unregister_custom_data_format(dtid, dfid): - """ - Unregisters a custom data format - @param dtid: data type id - @param dfid: data format id - @return: Boolean - """ - pass -# -*/ -static bool py_unregister_custom_data_format(int dtid, int dfid) -{ - py_custom_data_format_list_t::POS pos; - py_custom_data_format_t *inst = py_df_list.find(dtid, dfid, &pos); - // Maybe the user is trying to unregister a C api data format? - if ( inst == NULL ) - return unregister_custom_data_format(dtid, dfid); - - bool ok = inst->unregister_df(dtid); - - // Perhaps it was automatically unregistered because the type was unregistered? - if ( !ok ) - { - // Is this format still registered with IDA? - // If not, mark the context for deletion - ok = find_custom_data_format(inst->get_name()) < 0; - } - - if ( ok ) - { - py_df_list.erase(pos); - delete inst; - } - return ok; -} - -//------------------------------------------------------------------------ -/* -# -def get_custom_data_format(dtid, dfid): - """ - Returns a dictionary populated with the data format values or None on failure. - @param dtid: data type id - @param dfid: data format id - """ - pass -# -*/ -// Get definition of a registered custom data format and returns a dictionary -static PyObject *py_get_custom_data_format(int dtid, int fid) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - const data_format_t *df = get_custom_data_format(dtid, fid); - if ( df == NULL ) - Py_RETURN_NONE; - return py_data_format_to_py_dict(df); -} - -//------------------------------------------------------------------------ -/* -# -def get_custom_data_type(dtid): - """ - Returns a dictionary populated with the data type values or None on failure. - @param dtid: data type id - """ - pass -# -*/ -// Get definition of a registered custom data format and returns a dictionary -static PyObject *py_get_custom_data_type(int dtid) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - const data_type_t *dt = get_custom_data_type(dtid); - if ( dt == NULL ) - Py_RETURN_NONE; - return py_data_type_to_py_dict(dt); -} - -// - -#endif +#ifndef __PY_IDA_CUSTDATA__ +#define __PY_IDA_CUSTDATA__ + +// + +//------------------------------------------------------------------------ +class py_custom_data_type_t +{ + data_type_t dt; + qstring dt_name, dt_menu_name, dt_hotkey, dt_asm_keyword; + int dtid; // The data format id + PyObject *py_self; // Associated Python object + + // may create data? NULL means always may + static bool idaapi s_may_create_at( + void *ud, // user-defined data + ea_t ea, // address of the future item + size_t nbytes) // size of the future item + { + py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; + + PYW_GIL_GET; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_MAY_CREATE_AT, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(nbytes))); + + PyW_ShowCbErr(S_MAY_CREATE_AT); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + // !=NULL means variable size datatype + static asize_t idaapi s_calc_item_size( + // This function is used to determine + // size of the (possible) item at 'ea' + void *ud, // user-defined data + ea_t ea, // address of the item + asize_t maxsize) // maximal size of the item + { + PYW_GIL_GET; + // Returns: 0-no such item can be created/displayed + // this callback is required only for varsize datatypes + py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_CALC_ITEM_SIZE, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(maxsize))); + + if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) + return 0; + + uint64 num = 0; + PyW_GetNumber(py_result.o, &num); + return asize_t(num); + } + +public: + const char *get_name() const + { + return dt_name.c_str(); + } + + py_custom_data_type_t() + { + dtid = -1; + py_self = NULL; + } + + int register_dt(PyObject *py_obj) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Already registered? + if ( dtid >= 0 ) + return dtid; + + memset(&dt, 0, sizeof(dt)); + dt.cbsize = sizeof(dt); + dt.ud = this; + + do + { + ref_t py_attr; + + // name + if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) + break; + + dt.name = dt_name.c_str(); + + // menu_name (optional) + if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &dt_menu_name) ) + dt.menu_name = dt_menu_name.c_str(); + + // asm_keyword (optional) + if ( PyW_GetStringAttr(py_obj, S_ASM_KEYWORD, &dt_asm_keyword) ) + dt.asm_keyword = dt_asm_keyword.c_str(); + + // hotkey (optional) + if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &dt_hotkey) ) + dt.hotkey = dt_hotkey.c_str(); + + // value_size + py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.value_size = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); + + // props + py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.props = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); + + // may_create_at + py_attr = PyW_TryGetAttrString(py_obj, S_MAY_CREATE_AT); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + dt.may_create_at = s_may_create_at; + py_attr = ref_t(); + + // calc_item_size + py_attr = PyW_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + dt.calc_item_size = s_calc_item_size; + py_attr = ref_t(); + + // Now try to register + dtid = register_custom_data_type(&dt); + if ( dtid < 0 ) + break; + + // Hold reference to the PyObject + Py_INCREF(py_obj); + py_self = py_obj; + + py_attr = newref_t(PyInt_FromLong(dtid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); + } while ( false ); + return dtid; + } + + bool unregister_dt() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( dtid < 0 ) + return true; + + if ( !unregister_custom_data_type(dtid) ) + return false; + + // Release reference of Python object + Py_XDECREF(py_self); + py_self = NULL; + dtid = -1; + return true; + } + + ~py_custom_data_type_t() + { + unregister_dt(); + } +}; +typedef std::map py_custom_data_type_map_t; +static py_custom_data_type_map_t py_dt_map; + +//------------------------------------------------------------------------ +class py_custom_data_format_t +{ +private: + data_format_t df; + int dfid; + PyObject *py_self; + qstring df_name, df_menu_name, df_hotkey; + + static bool idaapi s_print( // convert to colored string + void *ud, // user-defined data + qstring *out, // output buffer. may be NULL + const void *value, // value to print. may not be NULL + asize_t size, // size of value in bytes + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num, // current operand number + int dtid) // custom data type id + { + PYW_GIL_GET; + + // Build a string from the buffer + newref_t py_value(PyString_FromStringAndSize( + (const char *)value, + Py_ssize_t(size))); + if ( py_value == NULL ) + return false; + + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + newref_t py_result(PyObject_CallMethod( + _this->py_self, + (char *)S_PRINTF, + "O" PY_FMT64 "ii", + py_value.o, + pyul_t(current_ea), + operand_num, + dtid)); + + // Error while calling the function? + if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL ) + return false; + + bool ok = false; + if ( PyString_Check(py_result.o) ) + { + Py_ssize_t len; + char *buf; + if ( out != NULL && PyString_AsStringAndSize(py_result.o, &buf, &len) != -1 ) + { + out->qclear(); + out->append(buf, len); + } + ok = true; + } + return ok; + } + + static bool idaapi s_scan( // convert from uncolored string + void *ud, // user-defined data + bytevec_t *value, // output buffer. may be NULL + const char *input, // input string. may not be NULL + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num, // current operand number (-1 if unknown) + qstring *errstr) // buffer for error message + { + PYW_GIL_GET; + + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_SCAN, + "s" PY_FMT64, + input, + pyul_t(current_ea), + operand_num)); + + // Error while calling the function? + if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL) + return false; + + bool ok = false; + do + { + // We expect a tuple(bool, string|None) + if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) + break; + + borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); + borref_t py_val(PyTuple_GetItem(py_result.o, 1)); + + // Get return code from Python + ok = PyObject_IsTrue(py_bool.o); + + // We expect None or the value (depending on probe) + if ( ok ) + { + // Probe-only? Then okay, no need to extract the 'value' + if ( value == NULL ) + break; + + Py_ssize_t len; + char *buf; + if ( PyString_AsStringAndSize(py_val.o, &buf, &len) != -1 ) + { + value->qclear(); + value->append(buf, len); + } + } + // An error occured? + else + { + // Make sure the user returned (False, String) + if ( py_bool.o != Py_False || !PyString_Check(py_val.o) ) + { + *errstr = "Invalid return value returned from the Python callback!"; + break; + } + // Get the error message + *errstr = PyString_AsString(py_val.o); + } + } while ( false ); + return ok; + } + + static void idaapi s_analyze( // analyze custom data format occurrence + void *ud, // user-defined data + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num) // current operand number + // this callback can be used to create + // xrefs from the current item. + // this callback may be missing. + { + PYW_GIL_GET; + + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_ANALYZE, + PY_FMT64 "i", + pyul_t(current_ea), + operand_num)); + + PyW_ShowCbErr(S_ANALYZE); + } +public: + py_custom_data_format_t() + { + dfid = -1; + py_self = NULL; + } + + const char *get_name() const + { + return df_name.c_str(); + } + + int register_df(int dtid, PyObject *py_obj) + { + // Already registered? + if ( dfid >= 0 ) + return dfid; + + memset(&df, 0, sizeof(df)); + df.cbsize = sizeof(df); + df.ud = this; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + ref_t py_attr; + + // name + if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) + break; + df.name = df_name.c_str(); + + // menu_name (optional) + if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &df_menu_name) ) + df.menu_name = df_menu_name.c_str(); + + // props + py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.props = PyInt_AsLong(py_attr.o); + + // hotkey + if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) + df.hotkey = df_hotkey.c_str(); + + // value_size + py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.value_size = PyInt_AsLong(py_attr.o); + + // text_width + py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.text_width = PyInt_AsLong(py_attr.o); + + // print cb + py_attr = PyW_TryGetAttrString(py_obj, S_PRINTF); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + df.print = s_print; + + // scan cb + py_attr = PyW_TryGetAttrString(py_obj, S_SCAN); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + df.scan = s_scan; + + // analyze cb + py_attr = PyW_TryGetAttrString(py_obj, S_ANALYZE); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + df.analyze = s_analyze; + + // Now try to register + dfid = register_custom_data_format(dtid, &df); + if ( dfid < 0 ) + break; + + // Hold reference to the PyObject + Py_INCREF(py_obj); + py_self = py_obj; + + // Update the format ID + py_attr = newref_t(PyInt_FromLong(dfid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); + } while ( false ); + return dfid; + } + + bool unregister_df(int dtid) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Never registered? + if ( dfid < 0 ) + return true; + + if ( !unregister_custom_data_format(dtid, dfid) ) + return false; + + // Release reference of Python object + Py_XDECREF(py_self); + py_self = NULL; + dfid = -1; + return true; + } + + ~py_custom_data_format_t() + { + } +}; + +//------------------------------------------------------------------------ +// Helper class to bind pairs to py_custom_data_format_t +class py_custom_data_format_list_t +{ + struct py_custom_data_format_entry_t + { + int dtid; + int dfid; + py_custom_data_format_t *df; + }; + typedef qvector ENTRY; + ENTRY entries; +public: + typedef ENTRY::iterator POS; + void add(int dtid, int dfid, py_custom_data_format_t *df) + { + py_custom_data_format_entry_t &e = entries.push_back(); + e.dtid = dtid; + e.dfid = dfid; + e.df = df; + } + py_custom_data_format_t *find(int dtid, int dfid, POS *loc = NULL) + { + for ( POS it=entries.begin(), it_end = entries.end(); it!=it_end; ++it ) + { + if ( it->dfid == dfid && it->dtid == dtid ) + { + if ( loc != NULL ) + *loc = it; + return it->df; + } + } + return NULL; + } + void erase(POS &pos) + { + entries.erase(pos); + } +}; +static py_custom_data_format_list_t py_df_list; + +//------------------------------------------------------------------------ +static PyObject *py_data_type_to_py_dict(const data_type_t *dt) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", + S_VALUE_SIZE, pyul_t(dt->value_size), + S_PROPS, dt->props, + S_CBSIZE, dt->cbsize, + S_NAME, dt->name == NULL ? "" : dt->name, + S_MENU_NAME, dt->menu_name == NULL ? "" : dt->menu_name, + S_HOTKEY, dt->hotkey == NULL ? "" : dt->hotkey, + S_ASM_KEYWORD, dt->asm_keyword == NULL ? "" : dt->asm_keyword); +} + +//------------------------------------------------------------------------ +static PyObject *py_data_format_to_py_dict(const data_format_t *df) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", + S_PROPS, df->props, + S_CBSIZE, df->cbsize, + S_TEXT_WIDTH, df->text_width, + S_VALUE_SIZE, pyul_t(df->value_size), + S_NAME, df->name == NULL ? "" : df->name, + S_MENU_NAME, df->menu_name == NULL ? "" : df->menu_name, + S_HOTKEY, df->hotkey == NULL ? "" : df->hotkey); +} +// + +//------------------------------------------------------------------------ +// + +//------------------------------------------------------------------------ +/* +# +def register_custom_data_type(dt): + """ + Registers a custom data type. + @param dt: an instance of the data_type_t class + @return: + < 0 if failed to register + > 0 data type id + """ + pass +# +*/ +// Given a py.data_format_t object, this function will register a datatype +static int py_register_custom_data_type(PyObject *py_dt) +{ + py_custom_data_type_t *inst = new py_custom_data_type_t(); + int r = inst->register_dt(py_dt); + if ( r < 0 ) + { + delete inst; + return r; + } + // Insert the instance to the map + py_dt_map[r] = inst; + return r; +} + +//------------------------------------------------------------------------ +/* +# +def unregister_custom_data_type(dtid): + """ + Unregisters a custom data type. + @param dtid: the data type id + @return: Boolean + """ + pass +# +*/ +static bool py_unregister_custom_data_type(int dtid) +{ + py_custom_data_type_map_t::iterator it = py_dt_map.find(dtid); + + // Maybe the user is trying to unregister a C api dt? + if ( it == py_dt_map.end() ) + return unregister_custom_data_type(dtid); + + py_custom_data_type_t *inst = it->second; + bool ok = inst->unregister_dt(); + + // Perhaps it was automatically unregistered because the idb was close? + if ( !ok ) + { + // Is this type still registered with IDA? + // If not found then mark the context for deletion + ok = find_custom_data_type(inst->get_name()) < 0; + } + + if ( ok ) + { + py_dt_map.erase(it); + delete inst; + } + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def register_custom_data_format(dtid, df): + """ + Registers a custom data format with a given data type. + @param dtid: data type id + @param df: an instance of data_format_t + @return: + < 0 if failed to register + > 0 data format id + """ + pass +# +*/ +static int py_register_custom_data_format(int dtid, PyObject *py_df) +{ + py_custom_data_format_t *inst = new py_custom_data_format_t(); + int r = inst->register_df(dtid, py_df); + if ( r < 0 ) + { + delete inst; + return r; + } + // Insert the instance + py_df_list.add(dtid, r, inst); + return r; +} + +//------------------------------------------------------------------------ +/* +# +def unregister_custom_data_format(dtid, dfid): + """ + Unregisters a custom data format + @param dtid: data type id + @param dfid: data format id + @return: Boolean + """ + pass +# +*/ +static bool py_unregister_custom_data_format(int dtid, int dfid) +{ + py_custom_data_format_list_t::POS pos; + py_custom_data_format_t *inst = py_df_list.find(dtid, dfid, &pos); + // Maybe the user is trying to unregister a C api data format? + if ( inst == NULL ) + return unregister_custom_data_format(dtid, dfid); + + bool ok = inst->unregister_df(dtid); + + // Perhaps it was automatically unregistered because the type was unregistered? + if ( !ok ) + { + // Is this format still registered with IDA? + // If not, mark the context for deletion + ok = find_custom_data_format(inst->get_name()) < 0; + } + + if ( ok ) + { + py_df_list.erase(pos); + delete inst; + } + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def get_custom_data_format(dtid, dfid): + """ + Returns a dictionary populated with the data format values or None on failure. + @param dtid: data type id + @param dfid: data format id + """ + pass +# +*/ +// Get definition of a registered custom data format and returns a dictionary +static PyObject *py_get_custom_data_format(int dtid, int fid) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + const data_format_t *df = get_custom_data_format(dtid, fid); + if ( df == NULL ) + Py_RETURN_NONE; + return py_data_format_to_py_dict(df); +} + +//------------------------------------------------------------------------ +/* +# +def get_custom_data_type(dtid): + """ + Returns a dictionary populated with the data type values or None on failure. + @param dtid: data type id + """ + pass +# +*/ +// Get definition of a registered custom data format and returns a dictionary +static PyObject *py_get_custom_data_type(int dtid) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + const data_type_t *dt = get_custom_data_type(dtid); + if ( dt == NULL ) + Py_RETURN_NONE; + return py_data_type_to_py_dict(dt); +} + +// + +#endif diff --git a/pywraps/py_custdata.py b/pywraps/py_custdata.py index 563bbf22..b996097d 100644 --- a/pywraps/py_custdata.py +++ b/pywraps/py_custdata.py @@ -1,247 +1,247 @@ -# ----------------------------------------------------------------------- -# Standalone and testing code -import sys -try: - import pywraps - pywraps_there = True - print "Using pywraps" -except: - pywraps_there = False - print "Not using pywraps" - -try: - import _idaapi -except: - print "Please try me from inside IDA" - sys.exit(0) - -import struct - -if pywraps_there: - _idaapi.register_custom_data_type = pywraps.register_custom_data_type - _idaapi.unregister_custom_data_type = pywraps.unregister_custom_data_type - _idaapi.register_custom_data_format = pywraps.register_custom_data_format - _idaapi.unregister_custom_data_format = pywraps.unregister_custom_data_format - _idaapi.get_custom_data_format = pywraps.get_custom_data_format - _idaapi.get_custom_data_type = pywraps.get_custom_data_type - -# ----------------------------------------------------------------------- -# -DTP_NODUP = 0x0001 - -class data_type_t(object): - """ - Custom data type definition. All data types should inherit from this class. - """ - - def __init__(self, name, value_size = 0, menu_name = None, hotkey = None, asm_keyword = None, props = 0): - """Please refer to bytes.hpp / data_type_t in the SDK""" - self.name = name - self.props = props - self.menu_name = menu_name - self.hotkey = hotkey - self.asm_keyword = asm_keyword - self.value_size = value_size - - self.id = -1 # Will be initialized after registration - """Contains the data type id after the data type is registered""" - - def register(self): - """Registers the data type and returns the type id or < 0 on failure""" - return _idaapi.register_custom_data_type(self) - - def unregister(self): - """Unregisters the data type and returns True on success""" - # Not registered? - if self.id < 0: - return True - - # Try to unregister - r = _idaapi.unregister_custom_data_type(self.id) - - # Clear the ID - if r: - self.id = -1 - return r -# -# def may_create_at(self, ea, nbytes): -# """ -# (optional) If this callback is not defined then this means always may create data type at the given ea. -# @param ea: address of the future item -# @param nbytes: size of the future item -# @return: Boolean -# """ -# -# return False -# -# def calc_item_size(self, ea, maxsize): -# """ -# (optional) If this callback is defined it means variable size datatype -# This function is used to determine size of the (possible) item at 'ea' -# @param ea: address of the item -# @param maxsize: maximal size of the item -# @return: integer -# Returns: 0-no such item can be created/displayed -# this callback is required only for varsize datatypes -# """ -# return 0 -# -# ----------------------------------------------------------------------- -# Uncomment the corresponding callbacks in the inherited class -class data_format_t(object): - """Information about a data format""" - def __init__(self, name, value_size = 0, menu_name = None, props = 0, hotkey = None, text_width = 0): - """Custom data format definition. - @param name: Format name, must be unique - @param menu_name: Visible format name to use in menus - @param props: properties (currently 0) - @param hotkey: Hotkey for the corresponding menu item - @param value_size: size of the value in bytes. 0 means any size is ok - @text_width: Usual width of the text representation - """ - self.name = name - self.menu_name = menu_name - self.props = props - self.hotkey = hotkey - self.value_size = value_size - self.text_width = text_width - - self.id = -1 # Will be initialized after registration - """contains the format id after the format gets registered""" - - def register(self, dtid): - """Registers the data format with the given data type id and returns the type id or < 0 on failure""" - return _idaapi.register_custom_data_format(dtid, self) - - def unregister(self, dtid): - """Unregisters the data format with the given data type id""" - - # Not registered? - if self.id < 0: - return True - - # Unregister - r = _idaapi.unregister_custom_data_format(dtid, self.id) - - # Clear the ID - if r: - self.id = -1 - return r -# -# def printf(self, value, current_ea, operand_num, dtid): -# """ -# Convert a value buffer to colored string. -# -# @param value: The value to be printed -# @param current_ea: The ea of the value -# @param operand_num: The affected operand -# @param dtid: custom data type id (0-standard built-in data type) -# @return: a colored string representing the passed 'value' or None on failure -# """ -# return None -# -# def scan(self, input, current_ea, operand_num): -# """ -# Convert from uncolored string 'input' to byte value -# -# @param input: input string -# @param current_ea: current address (BADADDR if unknown) -# @param operand_num: current operand number (-1 if unknown) -# -# @return: tuple (Boolean, string) -# - (False, ErrorMessage) if conversion fails -# - (True, Value buffer) if conversion succeeds -# """ -# return (False, "Not implemented") -# -# def analyze(self, current_ea, operand_num): -# """ -# (optional) Analyze custom data format occurrence. -# It can be used to create xrefs from the current item. -# -# @param current_ea: current address (BADADDR if unknown) -# @param operand_num: current operand number -# @return: None -# """ -# -# pass -# -# ----------------------------------------------------------------------- -def __walk_types_and_formats(formats, type_action, format_action, installing): - broken = False - for f in formats: - if len(f) == 1: - if not format_action(f[0], 0): - broken = True - break - else: - dt = f[0] - dfs = f[1:] - # install data type before installing formats - if installing and not type_action(dt): - broken = True - break - # process formats using the correct dt.id - for df in dfs: - if not format_action(df, dt.id): - broken = True - break - # uninstall data type after uninstalling formats - if not installing and not type_action(dt): - broken = True - break - return not broken - -# ----------------------------------------------------------------------- -def register_data_types_and_formats(formats): - """ - Registers multiple data types and formats at once. - To register one type/format at a time use register_custom_data_type/register_custom_data_format - - It employs a special table of types and formats described below: - - The 'formats' is a list of tuples. 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, then tuple[0] is the data type and tuple[1:] are the data formats. For example: - many_formats = [ - (pascal_data_type(), pascal_data_format()), - (simplevm_data_type(), simplevm_data_format()), - (makedword_data_format(),), - (simplevm_data_format(),) - ] - The first two tuples describe data types and their associated formats. - The last two tuples describe two data formats to be used with built-in data types. - """ - def __reg_format(df, dtid): - df.register(dtid) - if dtid == 0: - print "Registered format '%s' with built-in types, ID=%d" % (df.name, df.id) - else: - print " Registered format '%s', ID=%d (dtid=%d)" % (df.name, df.id, dtid) - return df.id != -1 - - def __reg_type(dt): - dt.register() - print "Registered type '%s', ID=%d" % (dt.name, dt.id) - return dt.id != -1 - ok = __walk_types_and_formats(formats, __reg_type, __reg_format, True) - return 1 if ok else -1 - -# ----------------------------------------------------------------------- -def unregister_data_types_and_formats(formats): - """As opposed to register_data_types_and_formats(), this function - unregisters multiple data types and formats at once. - """ - def __unreg_format(df, dtid): - print "%snregistering format '%s'" % ("U" if dtid == 0 else " u", df.name) - df.unregister(dtid) - return True - - def __unreg_type(dt): - print "Unregistering type '%s', ID=%d" % (dt.name, dt.id) - dt.unregister() - return True - ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format, False) - return 1 if ok else -1 - -# -# ----------------------------------------------------------------------- +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Not using pywraps" + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +import struct + +if pywraps_there: + _idaapi.register_custom_data_type = pywraps.register_custom_data_type + _idaapi.unregister_custom_data_type = pywraps.unregister_custom_data_type + _idaapi.register_custom_data_format = pywraps.register_custom_data_format + _idaapi.unregister_custom_data_format = pywraps.unregister_custom_data_format + _idaapi.get_custom_data_format = pywraps.get_custom_data_format + _idaapi.get_custom_data_type = pywraps.get_custom_data_type + +# ----------------------------------------------------------------------- +# +DTP_NODUP = 0x0001 + +class data_type_t(object): + """ + Custom data type definition. All data types should inherit from this class. + """ + + def __init__(self, name, value_size = 0, menu_name = None, hotkey = None, asm_keyword = None, props = 0): + """Please refer to bytes.hpp / data_type_t in the SDK""" + self.name = name + self.props = props + self.menu_name = menu_name + self.hotkey = hotkey + self.asm_keyword = asm_keyword + self.value_size = value_size + + self.id = -1 # Will be initialized after registration + """Contains the data type id after the data type is registered""" + + def register(self): + """Registers the data type and returns the type id or < 0 on failure""" + return _idaapi.register_custom_data_type(self) + + def unregister(self): + """Unregisters the data type and returns True on success""" + # Not registered? + if self.id < 0: + return True + + # Try to unregister + r = _idaapi.unregister_custom_data_type(self.id) + + # Clear the ID + if r: + self.id = -1 + return r +# +# def may_create_at(self, ea, nbytes): +# """ +# (optional) If this callback is not defined then this means always may create data type at the given ea. +# @param ea: address of the future item +# @param nbytes: size of the future item +# @return: Boolean +# """ +# +# return False +# +# def calc_item_size(self, ea, maxsize): +# """ +# (optional) If this callback is defined it means variable size datatype +# This function is used to determine size of the (possible) item at 'ea' +# @param ea: address of the item +# @param maxsize: maximal size of the item +# @return: integer +# Returns: 0-no such item can be created/displayed +# this callback is required only for varsize datatypes +# """ +# return 0 +# +# ----------------------------------------------------------------------- +# Uncomment the corresponding callbacks in the inherited class +class data_format_t(object): + """Information about a data format""" + def __init__(self, name, value_size = 0, menu_name = None, props = 0, hotkey = None, text_width = 0): + """Custom data format definition. + @param name: Format name, must be unique + @param menu_name: Visible format name to use in menus + @param props: properties (currently 0) + @param hotkey: Hotkey for the corresponding menu item + @param value_size: size of the value in bytes. 0 means any size is ok + @text_width: Usual width of the text representation + """ + self.name = name + self.menu_name = menu_name + self.props = props + self.hotkey = hotkey + self.value_size = value_size + self.text_width = text_width + + self.id = -1 # Will be initialized after registration + """contains the format id after the format gets registered""" + + def register(self, dtid): + """Registers the data format with the given data type id and returns the type id or < 0 on failure""" + return _idaapi.register_custom_data_format(dtid, self) + + def unregister(self, dtid): + """Unregisters the data format with the given data type id""" + + # Not registered? + if self.id < 0: + return True + + # Unregister + r = _idaapi.unregister_custom_data_format(dtid, self.id) + + # Clear the ID + if r: + self.id = -1 + return r +# +# def printf(self, value, current_ea, operand_num, dtid): +# """ +# Convert a value buffer to colored string. +# +# @param value: The value to be printed +# @param current_ea: The ea of the value +# @param operand_num: The affected operand +# @param dtid: custom data type id (0-standard built-in data type) +# @return: a colored string representing the passed 'value' or None on failure +# """ +# return None +# +# def scan(self, input, current_ea, operand_num): +# """ +# Convert from uncolored string 'input' to byte value +# +# @param input: input string +# @param current_ea: current address (BADADDR if unknown) +# @param operand_num: current operand number (-1 if unknown) +# +# @return: tuple (Boolean, string) +# - (False, ErrorMessage) if conversion fails +# - (True, Value buffer) if conversion succeeds +# """ +# return (False, "Not implemented") +# +# def analyze(self, current_ea, operand_num): +# """ +# (optional) Analyze custom data format occurrence. +# It can be used to create xrefs from the current item. +# +# @param current_ea: current address (BADADDR if unknown) +# @param operand_num: current operand number +# @return: None +# """ +# +# pass +# +# ----------------------------------------------------------------------- +def __walk_types_and_formats(formats, type_action, format_action, installing): + broken = False + for f in formats: + if len(f) == 1: + if not format_action(f[0], 0): + broken = True + break + else: + dt = f[0] + dfs = f[1:] + # install data type before installing formats + if installing and not type_action(dt): + broken = True + break + # process formats using the correct dt.id + for df in dfs: + if not format_action(df, dt.id): + broken = True + break + # uninstall data type after uninstalling formats + if not installing and not type_action(dt): + broken = True + break + return not broken + +# ----------------------------------------------------------------------- +def register_data_types_and_formats(formats): + """ + Registers multiple data types and formats at once. + To register one type/format at a time use register_custom_data_type/register_custom_data_format + + It employs a special table of types and formats described below: + + The 'formats' is a list of tuples. 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, then tuple[0] is the data type and tuple[1:] are the data formats. For example: + many_formats = [ + (pascal_data_type(), pascal_data_format()), + (simplevm_data_type(), simplevm_data_format()), + (makedword_data_format(),), + (simplevm_data_format(),) + ] + The first two tuples describe data types and their associated formats. + The last two tuples describe two data formats to be used with built-in data types. + """ + def __reg_format(df, dtid): + df.register(dtid) + if dtid == 0: + print "Registered format '%s' with built-in types, ID=%d" % (df.name, df.id) + else: + print " Registered format '%s', ID=%d (dtid=%d)" % (df.name, df.id, dtid) + return df.id != -1 + + def __reg_type(dt): + dt.register() + print "Registered type '%s', ID=%d" % (dt.name, dt.id) + return dt.id != -1 + ok = __walk_types_and_formats(formats, __reg_type, __reg_format, True) + return 1 if ok else -1 + +# ----------------------------------------------------------------------- +def unregister_data_types_and_formats(formats): + """As opposed to register_data_types_and_formats(), this function + unregisters multiple data types and formats at once. + """ + def __unreg_format(df, dtid): + print "%snregistering format '%s'" % ("U" if dtid == 0 else " u", df.name) + df.unregister(dtid) + return True + + def __unreg_type(dt): + print "Unregistering type '%s', ID=%d" % (dt.name, dt.id) + dt.unregister() + return True + ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format, False) + return 1 if ok else -1 + +# +# ----------------------------------------------------------------------- diff --git a/pywraps/py_custview.hpp b/pywraps/py_custview.hpp index 40cf216d..2fe185b3 100644 --- a/pywraps/py_custview.hpp +++ b/pywraps/py_custview.hpp @@ -1,1133 +1,1133 @@ -#ifndef __PYWRAPS_CUSTVIEWER__ -#define __PYWRAPS_CUSTVIEWER__ -// -//--------------------------------------------------------------------------- -// Base class for all custviewer place_t providers -class custviewer_data_t -{ -public: - virtual void *get_ud() = 0; - virtual place_t *get_min() = 0; - virtual place_t *get_max() = 0; -}; - -//--------------------------------------------------------------------------- -class cvdata_simpleline_t: public custviewer_data_t -{ -private: - strvec_t lines; - simpleline_place_t pl_min, pl_max; -public: - - void *get_ud() - { - return &lines; - } - - place_t *get_min() - { - return &pl_min; - } - - place_t *get_max() - { - return &pl_max; - } - - strvec_t &get_lines() - { - return lines; - } - - void set_minmax(size_t start=0, size_t end=size_t(-1)) - { - if ( start == 0 && end == size_t(-1) ) - { - end = lines.size(); - pl_min.n = 0; - pl_max.n = end == 0 ? 0 : end - 1; - } - else - { - pl_min.n = start; - pl_max.n = end; - } - } - - bool set_line(size_t nline, simpleline_t &sl) - { - if ( nline >= lines.size() ) - return false; - lines[nline] = sl; - return true; - } - - bool del_line(size_t nline) - { - if ( nline >= lines.size() ) - return false; - lines.erase(lines.begin()+nline); - return true; - } - - void add_line(simpleline_t &line) - { - lines.push_back(line); - } - - void add_line(const char *str) - { - lines.push_back(simpleline_t(str)); - } - - bool insert_line(size_t nline, simpleline_t &line) - { - if ( nline >= lines.size() ) - return false; - lines.insert(lines.begin()+nline, line); - return true; - } - - bool patch_line(size_t nline, size_t offs, int value) - { - if ( nline >= lines.size() ) - return false; - qstring &L = lines[nline].line; - L[offs] = (uchar) value & 0xFF; - return true; - } - - const size_t to_lineno(place_t *pl) const - { - return ((simpleline_place_t *)pl)->n; - } - - bool curline(place_t *pl, size_t *n) - { - if ( pl == NULL ) - return false; - - *n = to_lineno(pl); - return true; - } - - simpleline_t *get_line(size_t nline) - { - return nline >= lines.size() ? NULL : &lines[nline]; - } - - simpleline_t *get_line(place_t *pl) - { - return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); - } - - const size_t count() const - { - return lines.size(); - } - - void clear_lines() - { - lines.clear(); - set_minmax(); - } -}; - -//--------------------------------------------------------------------------- -// FIXME: This should inherit py_view_base.hpp's py_customidamemo_t, -// just like py_graph.hpp's py_graph_t does. -// There should be a way to "merge" the two mechanisms; they are similar. -class customviewer_t -{ -protected: - qstring _title; - TForm *_form; - TCustomControl *_cv; - custviewer_data_t *_data; - int _features; - enum - { - HAVE_HINT = 0x0001, - HAVE_KEYDOWN = 0x0002, - HAVE_POPUP = 0x0004, - HAVE_DBLCLICK = 0x0008, - HAVE_CURPOS = 0x0010, - HAVE_CLICK = 0x0020, - HAVE_CLOSE = 0x0040 - }; -private: - struct cvw_popupctx_t - { - size_t menu_id; - customviewer_t *cv; - cvw_popupctx_t(): menu_id(0), cv(NULL) { } - cvw_popupctx_t(size_t mid, customviewer_t *v): menu_id(mid), cv(v) { } - }; - typedef std::map cvw_popupmap_t; - static cvw_popupmap_t _global_popup_map; - static size_t _global_popup_id; - qstring _curline; - intvec_t _installed_popups; - - static bool idaapi s_popup_menu_cb(void *ud) - { - size_t mid = (size_t)ud; - cvw_popupmap_t::iterator it = _global_popup_map.find(mid); - if ( it == _global_popup_map.end() ) - return false; - - PYW_GIL_GET; - return it->second.cv->on_popup_menu(it->second.menu_id); - } - - static bool idaapi s_cv_keydown( - TCustomControl * /*cv*/, - int vk_key, - int shift, - void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_keydown(vk_key, shift); - } - - // The popup menu is being constructed - static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - _this->on_popup(); - } - - // The user clicked - static bool idaapi s_cv_click(TCustomControl * /*cv*/, int shift, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_click(shift); - } - - // The user double clicked - static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_dblclick(shift); - } - - // Cursor position has been changed - static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - _this->on_curpos_changed(); - } - - //-------------------------------------------------------------------------- - static int idaapi s_ui_cb(void *ud, int code, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - switch ( code ) - { - case ui_get_custom_viewer_hint: - { - TCustomControl *viewer = va_arg(va, TCustomControl *); - place_t *place = va_arg(va, place_t *); - int *important_lines = va_arg(va, int *); - qstring &hint = *va_arg(va, qstring *); - if ( (_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer ) - return 0; - else - return _this->on_hint(place, important_lines, hint) ? 1 : 0; - } - - case ui_tform_invisible: - { - TForm *form = va_arg(va, TForm *); - if ( _this->_form != form ) - break; - } - // fallthrough... - case ui_term: - unhook_from_notification_point(HT_UI, s_ui_cb, _this); - _this->on_close(); - _this->on_post_close(); - break; - } - - return 0; - } - - void on_post_close() - { - init_vars(); - clear_popup_menu(); - } - -public: - - inline TForm *get_tform() { return _form; } - inline TCustomControl *get_tcustom_control() { return _cv; } - - // - // All the overridable callbacks - // - - // OnClick - virtual bool on_click(int /*shift*/) { return false; } - - // OnDblClick - virtual bool on_dblclick(int /*shift*/) { return false; } - - // OnCurorPositionChanged - virtual void on_curpos_changed() { } - - // OnHostFormClose - virtual void on_close() { } - - // OnKeyDown - virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } - - // OnPopupShow - virtual bool on_popup() { return false; } - - // OnHint - virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } - - // OnPopupMenuClick - virtual bool on_popup_menu(size_t menu_id) { return false; } - - void init_vars() - { - _data = NULL; - _features = 0; - _curline.clear(); - _cv = NULL; - _form = NULL; - } - - customviewer_t() - { - init_vars(); - } - - ~customviewer_t() - { - } - - //-------------------------------------------------------------------------- - void close() - { - if ( _form != NULL ) - close_tform(_form, FORM_SAVE | FORM_CLOSE_LATER); - } - - //-------------------------------------------------------------------------- - bool set_range( - const place_t *minplace = NULL, - const place_t *maxplace = NULL) - { - if ( _cv == NULL ) - return false; - - set_custom_viewer_range( - _cv, - minplace == NULL ? _data->get_min() : minplace, - maxplace == NULL ? _data->get_max() : maxplace); - return true; - } - - place_t *get_place( - bool mouse = false, - int *x = 0, - int *y = 0) - { - return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); - } - - //-------------------------------------------------------------------------- - bool refresh() - { - if ( _cv == NULL ) - return false; - - refresh_custom_viewer(_cv); - return true; - } - - //-------------------------------------------------------------------------- - bool refresh_current() - { - int x, y; - place_t *pl = get_place(false, &x, &y); - if ( pl == NULL ) - return false; - - return jumpto(pl, x, y); - } - - //-------------------------------------------------------------------------- - bool get_current_word(bool mouse, qstring &word) - { - // query the cursor position - int x, y; - if ( get_place(mouse, &x, &y) == NULL ) - return false; - - // query the line at the cursor - const char *line = get_current_line(mouse, true); - if ( line == NULL ) - return false; - - if ( x >= (int)strlen(line) ) - return false; - - // find the beginning of the word - const char *ptr = line + x; - while ( ptr > line && !qisspace(ptr[-1]) ) - ptr--; - - // find the end of the word - const char *begin = ptr; - ptr = line + x; - while ( !qisspace(*ptr) && *ptr != '\0' ) - ptr++; - - word.qclear(); - word.append(begin, ptr-begin); - return true; - } - - //-------------------------------------------------------------------------- - const char *get_current_line(bool mouse, bool notags) - { - const char *r = get_custom_viewer_curline(_cv, mouse); - if ( r == NULL || !notags ) - return r; - - size_t sz = strlen(r); - if ( sz == 0 ) - return r; - - _curline.resize(sz + 5, '\0'); - tag_remove(r, &_curline[0], sz + 1); - return _curline.c_str(); - } - - //-------------------------------------------------------------------------- - bool is_focused() - { - return get_current_viewer() == _cv; - } - - //-------------------------------------------------------------------------- - bool jumpto(place_t *place, int x, int y) - { - return ::jumpto(_cv, place, x, y); - } - - //-------------------------------------------------------------------------- - void clear_popup_menu() - { - if ( _cv != NULL ) - set_custom_viewer_popup_menu(_cv, NULL); - - for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); - it != it_end; - ++it) - { - _global_popup_map.erase(*it); - } - _installed_popups.clear(); - } - - //-------------------------------------------------------------------------- - size_t add_popup_menu( - const char *title, - const char *hotkey) - { - size_t menu_id = _global_popup_id + 1; - - // Overlap / already exists? - if (_cv == NULL || // No custviewer? - // Overlap? - menu_id == 0 || - // Already exists? - _global_popup_map.find(menu_id) != _global_popup_map.end()) - { - return 0; - } - add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); - - // Save global association - _global_popup_map[menu_id] = cvw_popupctx_t(menu_id, this); - _global_popup_id = menu_id; - - // Remember what menu IDs are set with this form - _installed_popups.push_back(menu_id); - return menu_id; - } - - //-------------------------------------------------------------------------- - bool create(const char *title, int features, custviewer_data_t *data) - { - // Already created? (in the instance) - if ( _form != NULL ) - return true; - - // Already created? (in IDA windows list) - HWND hwnd(NULL); - TForm *form = create_tform(title, &hwnd); - if ( hwnd == NULL ) - return false; - - _title = title; - _data = data; - _form = form; - _features = features; - - // Create the viewer - _cv = create_custom_viewer( - title, - (TWinControl *)_form, - _data->get_min(), - _data->get_max(), - _data->get_min(), - 0, - _data->get_ud()); - - // Set user-data - set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); - - // - // Set other optional callbacks - // - if ( (features & HAVE_KEYDOWN) != 0 ) - set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); - - if ( (features & HAVE_POPUP) != 0 ) - set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); - - if ( (features & HAVE_DBLCLICK) != 0 ) - set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); - - if ( (features & HAVE_CURPOS) != 0 ) - set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); - - if ( (features & HAVE_CLICK) != 0 ) - set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); - - // Hook to UI notifications (for TForm close event) - hook_to_notification_point(HT_UI, s_ui_cb, this); - - return true; - } - - //-------------------------------------------------------------------------- - bool show() - { - // Closed already? - if ( _form == NULL ) - return false; - - open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE|FORM_QWIDGET); - return true; - } -}; - -customviewer_t::cvw_popupmap_t customviewer_t::_global_popup_map; -size_t customviewer_t::_global_popup_id = 0; -//--------------------------------------------------------------------------- -class py_simplecustview_t: public customviewer_t -{ -private: - cvdata_simpleline_t data; - PyObject *py_self, *py_this, *py_last_link; - int features; - - //-------------------------------------------------------------------------- - // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t - static bool py_to_simpleline(PyObject *py, simpleline_t &sl) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( PyString_Check(py) ) - { - sl.line = PyString_AsString(py); - return true; - } - Py_ssize_t sz; - if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) - return false; - - PyObject *py_val = PyTuple_GetItem(py, 0); - if ( !PyString_Check(py_val) ) - return false; - - sl.line = PyString_AsString(py_val); - - if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) - sl.color = color_t(PyLong_AsUnsignedLong(py_val)); - - if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) - sl.bgcolor = PyLong_AsUnsignedLong(py_val); - - return true; - } - - // - // Callbacks - // - virtual bool on_click(int shift) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift)); - PyW_ShowCbErr(S_ON_CLICK); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - // OnDblClick - virtual bool on_dblclick(int shift) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift)); - PyW_ShowCbErr(S_ON_DBL_CLICK); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - // OnCurorPositionChanged - virtual void on_curpos_changed() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL)); - PyW_ShowCbErr(S_ON_CURSOR_POS_CHANGED); - } - - //-------------------------------------------------------------------------- - // OnHostFormClose - virtual void on_close() - { - // Call the close method if it is there and the object is still bound - if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL)); - PyW_ShowCbErr(S_ON_CLOSE); - - // Cleanup - Py_DECREF(py_self); - py_self = NULL; - } - } - - //-------------------------------------------------------------------------- - // OnKeyDown - virtual bool on_keydown(int vk_key, int shift) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_KEYDOWN, - "ii", - vk_key, - shift)); - - PyW_ShowCbErr(S_ON_KEYDOWN); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- -// OnPopupShow - virtual bool on_popup() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP, - NULL)); - PyW_ShowCbErr(S_ON_POPUP); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - // OnHint - virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) - { - size_t ln = data.to_lineno(place); - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_HINT, - PY_FMT64, - pyul_t(ln))); - - PyW_ShowCbErr(S_ON_HINT); - bool ok = py_result != NULL && PyTuple_Check(py_result.o) && PyTuple_Size(py_result.o) == 2; - if ( ok ) - { - if ( important_lines != NULL ) - *important_lines = PyInt_AsLong(PyTuple_GetItem(py_result.o, 0)); - hint = PyString_AsString(PyTuple_GetItem(py_result.o, 1)); - } - return ok; - } - - //-------------------------------------------------------------------------- - // OnPopupMenuClick - virtual bool on_popup_menu(size_t menu_id) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP_MENU, - PY_FMT64, - pyul_t(menu_id))); - PyW_ShowCbErr(S_ON_POPUP_MENU); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - void refresh_range() - { - data.set_minmax(); - set_range(); - } - -public: - py_simplecustview_t() - { - py_this = py_self = py_last_link = NULL; - } - ~py_simplecustview_t() - { - } - - //-------------------------------------------------------------------------- - // Edits an existing line - bool edit_line(size_t nline, PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - - return data.set_line(nline, sl); - } - - // Low level: patches a line string directly - bool patch_line(size_t nline, size_t offs, int value) - { - return data.patch_line(nline, offs, value); - } - - // Insert a line - bool insert_line(size_t nline, PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - return data.insert_line(nline, sl); - } - - // Adds a line tuple - bool add_line(PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - data.add_line(sl); - refresh_range(); - return true; - } - - //-------------------------------------------------------------------------- - bool del_line(size_t nline) - { - bool ok = data.del_line(nline); - if ( ok ) - refresh_range(); - return ok; - } - - //-------------------------------------------------------------------------- - // Gets the position and returns a tuple (lineno, x, y) - PyObject *get_pos(bool mouse) - { - place_t *pl; - int x, y; - pl = get_place(mouse, &x, &y); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( pl == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); - } - - //-------------------------------------------------------------------------- - // Returns the line tuple - PyObject *get_line(size_t nline) - { - simpleline_t *r = data.get_line(nline); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( r == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); - } - - // Returns the count of lines - const size_t count() const - { - return data.count(); - } - - // Clears lines - void clear() - { - data.clear_lines(); - refresh_range(); - } - - //-------------------------------------------------------------------------- - bool jumpto(size_t ln, int x, int y) - { - simpleline_place_t l(ln); - return customviewer_t::jumpto(&l, x, y); - } - - //-------------------------------------------------------------------------- - // Initializes and links the Python object to this class - bool init(PyObject *py_link, const char *title) - { - // Already created? - if ( _form != NULL ) - return true; - - // Probe callbacks - features = 0; - static struct - { - const char *cb_name; - int feature; - } const cbtable[] = - { - {S_ON_CLICK, HAVE_CLICK}, - {S_ON_CLOSE, HAVE_CLOSE}, - {S_ON_HINT, HAVE_HINT}, - {S_ON_KEYDOWN, HAVE_KEYDOWN}, - {S_ON_POPUP, HAVE_POPUP}, - {S_ON_DBL_CLICK, HAVE_DBLCLICK}, - {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} - }; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - for ( size_t i=0; i - -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- - -// -// -// Pywraps Simple Custom Viewer functions -// -PyObject *pyscv_init(PyObject *py_link, const char *title) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - py_simplecustview_t *_this = new py_simplecustview_t(); - bool ok = _this->init(py_link, title); - if ( !ok ) - { - delete _this; - Py_RETURN_NONE; - } - return _this->get_pythis(); -} -#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) - -//-------------------------------------------------------------------------- -bool pyscv_refresh(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->refresh(); -} - -//-------------------------------------------------------------------------- -bool pyscv_delete(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - _this->close(); - delete _this; - return true; -} - -//-------------------------------------------------------------------------- -bool pyscv_refresh_current(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->refresh_current(); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) -{ - DECL_THIS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - const char *line; - if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) - Py_RETURN_NONE; - return PyString_FromString(line); -} - -//-------------------------------------------------------------------------- -bool pyscv_is_focused(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->is_focused(); -} - -void pyscv_clear_popup_menu(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->clear_popup_menu(); -} - -size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) -{ - DECL_THIS; - return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); -} - -size_t pyscv_count(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? 0 : _this->count(); -} - -bool pyscv_show(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? false : _this->show(); -} - -void pyscv_close(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->close(); -} - -bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->jumpto(ln, x, y); -} - -// Returns the line tuple -PyObject *pyscv_get_line(PyObject *py_this, size_t nline) -{ - DECL_THIS; - if ( _this == NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } - return _this->get_line(nline); -} - -//-------------------------------------------------------------------------- -// Gets the position and returns a tuple (lineno, x, y) -PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) -{ - DECL_THIS; - if ( _this == NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } - return _this->get_pos(mouse); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_clear_lines(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->clear(); - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; -} - -//-------------------------------------------------------------------------- -// Adds a line tuple -bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->add_line(py_sl); -} - -//-------------------------------------------------------------------------- -bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->insert_line(nline, py_sl); -} - -//-------------------------------------------------------------------------- -bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) -{ - DECL_THIS; - return _this == NULL ? false : _this->patch_line(nline, offs, value); -} - -//-------------------------------------------------------------------------- -bool pyscv_del_line(PyObject *py_this, size_t nline) -{ - DECL_THIS; - return _this == NULL ? false : _this->del_line(nline); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_get_selection(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } - return _this->py_get_selection(); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) -{ - DECL_THIS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( _this != NULL ) - { - qstring word; - if ( _this->get_current_word(mouse, word) ) - return PyString_FromString(word.c_str()); - } - Py_RETURN_NONE; -} - -//-------------------------------------------------------------------------- -// Edits an existing line -bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->edit_line(nline, py_sl); -} - -//------------------------------------------------------------------------- -TForm *pyscv_get_tform(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? NULL : _this->get_tform(); -} - -//------------------------------------------------------------------------- -TCustomControl *pyscv_get_tcustom_control(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? NULL : _this->get_tcustom_control(); -} - - -#undef DECL_THIS -// -//--------------------------------------------------------------------------- -#endif // __PYWRAPS_CUSTVIEWER__ +#ifndef __PYWRAPS_CUSTVIEWER__ +#define __PYWRAPS_CUSTVIEWER__ +// +//--------------------------------------------------------------------------- +// Base class for all custviewer place_t providers +class custviewer_data_t +{ +public: + virtual void *get_ud() = 0; + virtual place_t *get_min() = 0; + virtual place_t *get_max() = 0; +}; + +//--------------------------------------------------------------------------- +class cvdata_simpleline_t: public custviewer_data_t +{ +private: + strvec_t lines; + simpleline_place_t pl_min, pl_max; +public: + + void *get_ud() + { + return &lines; + } + + place_t *get_min() + { + return &pl_min; + } + + place_t *get_max() + { + return &pl_max; + } + + strvec_t &get_lines() + { + return lines; + } + + void set_minmax(size_t start=0, size_t end=size_t(-1)) + { + if ( start == 0 && end == size_t(-1) ) + { + end = lines.size(); + pl_min.n = 0; + pl_max.n = end == 0 ? 0 : end - 1; + } + else + { + pl_min.n = start; + pl_max.n = end; + } + } + + bool set_line(size_t nline, simpleline_t &sl) + { + if ( nline >= lines.size() ) + return false; + lines[nline] = sl; + return true; + } + + bool del_line(size_t nline) + { + if ( nline >= lines.size() ) + return false; + lines.erase(lines.begin()+nline); + return true; + } + + void add_line(simpleline_t &line) + { + lines.push_back(line); + } + + void add_line(const char *str) + { + lines.push_back(simpleline_t(str)); + } + + bool insert_line(size_t nline, simpleline_t &line) + { + if ( nline >= lines.size() ) + return false; + lines.insert(lines.begin()+nline, line); + return true; + } + + bool patch_line(size_t nline, size_t offs, int value) + { + if ( nline >= lines.size() ) + return false; + qstring &L = lines[nline].line; + L[offs] = (uchar) value & 0xFF; + return true; + } + + const size_t to_lineno(place_t *pl) const + { + return ((simpleline_place_t *)pl)->n; + } + + bool curline(place_t *pl, size_t *n) + { + if ( pl == NULL ) + return false; + + *n = to_lineno(pl); + return true; + } + + simpleline_t *get_line(size_t nline) + { + return nline >= lines.size() ? NULL : &lines[nline]; + } + + simpleline_t *get_line(place_t *pl) + { + return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); + } + + const size_t count() const + { + return lines.size(); + } + + void clear_lines() + { + lines.clear(); + set_minmax(); + } +}; + +//--------------------------------------------------------------------------- +// FIXME: This should inherit py_view_base.hpp's py_customidamemo_t, +// just like py_graph.hpp's py_graph_t does. +// There should be a way to "merge" the two mechanisms; they are similar. +class customviewer_t +{ +protected: + qstring _title; + TForm *_form; + TCustomControl *_cv; + custviewer_data_t *_data; + int _features; + enum + { + HAVE_HINT = 0x0001, + HAVE_KEYDOWN = 0x0002, + HAVE_POPUP = 0x0004, + HAVE_DBLCLICK = 0x0008, + HAVE_CURPOS = 0x0010, + HAVE_CLICK = 0x0020, + HAVE_CLOSE = 0x0040 + }; +private: + struct cvw_popupctx_t + { + size_t menu_id; + customviewer_t *cv; + cvw_popupctx_t(): menu_id(0), cv(NULL) { } + cvw_popupctx_t(size_t mid, customviewer_t *v): menu_id(mid), cv(v) { } + }; + typedef std::map cvw_popupmap_t; + static cvw_popupmap_t _global_popup_map; + static size_t _global_popup_id; + qstring _curline; + intvec_t _installed_popups; + + static bool idaapi s_popup_menu_cb(void *ud) + { + size_t mid = (size_t)ud; + cvw_popupmap_t::iterator it = _global_popup_map.find(mid); + if ( it == _global_popup_map.end() ) + return false; + + PYW_GIL_GET; + return it->second.cv->on_popup_menu(it->second.menu_id); + } + + static bool idaapi s_cv_keydown( + TCustomControl * /*cv*/, + int vk_key, + int shift, + void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_keydown(vk_key, shift); + } + + // The popup menu is being constructed + static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + _this->on_popup(); + } + + // The user clicked + static bool idaapi s_cv_click(TCustomControl * /*cv*/, int shift, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_click(shift); + } + + // The user double clicked + static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_dblclick(shift); + } + + // Cursor position has been changed + static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + _this->on_curpos_changed(); + } + + //-------------------------------------------------------------------------- + static int idaapi s_ui_cb(void *ud, int code, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + switch ( code ) + { + case ui_get_custom_viewer_hint: + { + TCustomControl *viewer = va_arg(va, TCustomControl *); + place_t *place = va_arg(va, place_t *); + int *important_lines = va_arg(va, int *); + qstring &hint = *va_arg(va, qstring *); + if ( (_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer ) + return 0; + else + return _this->on_hint(place, important_lines, hint) ? 1 : 0; + } + + case ui_tform_invisible: + { + TForm *form = va_arg(va, TForm *); + if ( _this->_form != form ) + break; + } + // fallthrough... + case ui_term: + unhook_from_notification_point(HT_UI, s_ui_cb, _this); + _this->on_close(); + _this->on_post_close(); + break; + } + + return 0; + } + + void on_post_close() + { + init_vars(); + clear_popup_menu(); + } + +public: + + inline TForm *get_tform() { return _form; } + inline TCustomControl *get_tcustom_control() { return _cv; } + + // + // All the overridable callbacks + // + + // OnClick + virtual bool on_click(int /*shift*/) { return false; } + + // OnDblClick + virtual bool on_dblclick(int /*shift*/) { return false; } + + // OnCurorPositionChanged + virtual void on_curpos_changed() { } + + // OnHostFormClose + virtual void on_close() { } + + // OnKeyDown + virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } + + // OnPopupShow + virtual bool on_popup() { return false; } + + // OnHint + virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } + + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) { return false; } + + void init_vars() + { + _data = NULL; + _features = 0; + _curline.clear(); + _cv = NULL; + _form = NULL; + } + + customviewer_t() + { + init_vars(); + } + + ~customviewer_t() + { + } + + //-------------------------------------------------------------------------- + void close() + { + if ( _form != NULL ) + close_tform(_form, FORM_SAVE | FORM_CLOSE_LATER); + } + + //-------------------------------------------------------------------------- + bool set_range( + const place_t *minplace = NULL, + const place_t *maxplace = NULL) + { + if ( _cv == NULL ) + return false; + + set_custom_viewer_range( + _cv, + minplace == NULL ? _data->get_min() : minplace, + maxplace == NULL ? _data->get_max() : maxplace); + return true; + } + + place_t *get_place( + bool mouse = false, + int *x = 0, + int *y = 0) + { + return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); + } + + //-------------------------------------------------------------------------- + bool refresh() + { + if ( _cv == NULL ) + return false; + + refresh_custom_viewer(_cv); + return true; + } + + //-------------------------------------------------------------------------- + bool refresh_current() + { + int x, y; + place_t *pl = get_place(false, &x, &y); + if ( pl == NULL ) + return false; + + return jumpto(pl, x, y); + } + + //-------------------------------------------------------------------------- + bool get_current_word(bool mouse, qstring &word) + { + // query the cursor position + int x, y; + if ( get_place(mouse, &x, &y) == NULL ) + return false; + + // query the line at the cursor + const char *line = get_current_line(mouse, true); + if ( line == NULL ) + return false; + + if ( x >= (int)strlen(line) ) + return false; + + // find the beginning of the word + const char *ptr = line + x; + while ( ptr > line && !qisspace(ptr[-1]) ) + ptr--; + + // find the end of the word + const char *begin = ptr; + ptr = line + x; + while ( !qisspace(*ptr) && *ptr != '\0' ) + ptr++; + + word.qclear(); + word.append(begin, ptr-begin); + return true; + } + + //-------------------------------------------------------------------------- + const char *get_current_line(bool mouse, bool notags) + { + const char *r = get_custom_viewer_curline(_cv, mouse); + if ( r == NULL || !notags ) + return r; + + size_t sz = strlen(r); + if ( sz == 0 ) + return r; + + _curline.resize(sz + 5, '\0'); + tag_remove(r, &_curline[0], sz + 1); + return _curline.c_str(); + } + + //-------------------------------------------------------------------------- + bool is_focused() + { + return get_current_viewer() == _cv; + } + + //-------------------------------------------------------------------------- + bool jumpto(place_t *place, int x, int y) + { + return ::jumpto(_cv, place, x, y); + } + + //-------------------------------------------------------------------------- + void clear_popup_menu() + { + if ( _cv != NULL ) + set_custom_viewer_popup_menu(_cv, NULL); + + for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); + it != it_end; + ++it) + { + _global_popup_map.erase(*it); + } + _installed_popups.clear(); + } + + //-------------------------------------------------------------------------- + size_t add_popup_menu( + const char *title, + const char *hotkey) + { + size_t menu_id = _global_popup_id + 1; + + // Overlap / already exists? + if (_cv == NULL || // No custviewer? + // Overlap? + menu_id == 0 || + // Already exists? + _global_popup_map.find(menu_id) != _global_popup_map.end()) + { + return 0; + } + add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); + + // Save global association + _global_popup_map[menu_id] = cvw_popupctx_t(menu_id, this); + _global_popup_id = menu_id; + + // Remember what menu IDs are set with this form + _installed_popups.push_back(menu_id); + return menu_id; + } + + //-------------------------------------------------------------------------- + bool create(const char *title, int features, custviewer_data_t *data) + { + // Already created? (in the instance) + if ( _form != NULL ) + return true; + + // Already created? (in IDA windows list) + HWND hwnd(NULL); + TForm *form = create_tform(title, &hwnd); + if ( hwnd == NULL ) + return false; + + _title = title; + _data = data; + _form = form; + _features = features; + + // Create the viewer + _cv = create_custom_viewer( + title, + (TWinControl *)_form, + _data->get_min(), + _data->get_max(), + _data->get_min(), + 0, + _data->get_ud()); + + // Set user-data + set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); + + // + // Set other optional callbacks + // + if ( (features & HAVE_KEYDOWN) != 0 ) + set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); + + if ( (features & HAVE_POPUP) != 0 ) + set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); + + if ( (features & HAVE_DBLCLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); + + if ( (features & HAVE_CURPOS) != 0 ) + set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); + + if ( (features & HAVE_CLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); + + // Hook to UI notifications (for TForm close event) + hook_to_notification_point(HT_UI, s_ui_cb, this); + + return true; + } + + //-------------------------------------------------------------------------- + bool show() + { + // Closed already? + if ( _form == NULL ) + return false; + + open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE|FORM_QWIDGET); + return true; + } +}; + +customviewer_t::cvw_popupmap_t customviewer_t::_global_popup_map; +size_t customviewer_t::_global_popup_id = 0; +//--------------------------------------------------------------------------- +class py_simplecustview_t: public customviewer_t +{ +private: + cvdata_simpleline_t data; + PyObject *py_self, *py_this, *py_last_link; + int features; + + //-------------------------------------------------------------------------- + // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t + static bool py_to_simpleline(PyObject *py, simpleline_t &sl) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( PyString_Check(py) ) + { + sl.line = PyString_AsString(py); + return true; + } + Py_ssize_t sz; + if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) + return false; + + PyObject *py_val = PyTuple_GetItem(py, 0); + if ( !PyString_Check(py_val) ) + return false; + + sl.line = PyString_AsString(py_val); + + if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) + sl.color = color_t(PyLong_AsUnsignedLong(py_val)); + + if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) + sl.bgcolor = PyLong_AsUnsignedLong(py_val); + + return true; + } + + // + // Callbacks + // + virtual bool on_click(int shift) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift)); + PyW_ShowCbErr(S_ON_CLICK); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + // OnDblClick + virtual bool on_dblclick(int shift) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift)); + PyW_ShowCbErr(S_ON_DBL_CLICK); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + // OnCurorPositionChanged + virtual void on_curpos_changed() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL)); + PyW_ShowCbErr(S_ON_CURSOR_POS_CHANGED); + } + + //-------------------------------------------------------------------------- + // OnHostFormClose + virtual void on_close() + { + // Call the close method if it is there and the object is still bound + if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL)); + PyW_ShowCbErr(S_ON_CLOSE); + + // Cleanup + Py_DECREF(py_self); + py_self = NULL; + } + } + + //-------------------------------------------------------------------------- + // OnKeyDown + virtual bool on_keydown(int vk_key, int shift) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_KEYDOWN, + "ii", + vk_key, + shift)); + + PyW_ShowCbErr(S_ON_KEYDOWN); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- +// OnPopupShow + virtual bool on_popup() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP, + NULL)); + PyW_ShowCbErr(S_ON_POPUP); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + // OnHint + virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) + { + size_t ln = data.to_lineno(place); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_HINT, + PY_FMT64, + pyul_t(ln))); + + PyW_ShowCbErr(S_ON_HINT); + bool ok = py_result != NULL && PyTuple_Check(py_result.o) && PyTuple_Size(py_result.o) == 2; + if ( ok ) + { + if ( important_lines != NULL ) + *important_lines = PyInt_AsLong(PyTuple_GetItem(py_result.o, 0)); + hint = PyString_AsString(PyTuple_GetItem(py_result.o, 1)); + } + return ok; + } + + //-------------------------------------------------------------------------- + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP_MENU, + PY_FMT64, + pyul_t(menu_id))); + PyW_ShowCbErr(S_ON_POPUP_MENU); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + void refresh_range() + { + data.set_minmax(); + set_range(); + } + +public: + py_simplecustview_t() + { + py_this = py_self = py_last_link = NULL; + } + ~py_simplecustview_t() + { + } + + //-------------------------------------------------------------------------- + // Edits an existing line + bool edit_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + + return data.set_line(nline, sl); + } + + // Low level: patches a line string directly + bool patch_line(size_t nline, size_t offs, int value) + { + return data.patch_line(nline, offs, value); + } + + // Insert a line + bool insert_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + return data.insert_line(nline, sl); + } + + // Adds a line tuple + bool add_line(PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + data.add_line(sl); + refresh_range(); + return true; + } + + //-------------------------------------------------------------------------- + bool del_line(size_t nline) + { + bool ok = data.del_line(nline); + if ( ok ) + refresh_range(); + return ok; + } + + //-------------------------------------------------------------------------- + // Gets the position and returns a tuple (lineno, x, y) + PyObject *get_pos(bool mouse) + { + place_t *pl; + int x, y; + pl = get_place(mouse, &x, &y); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( pl == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); + } + + //-------------------------------------------------------------------------- + // Returns the line tuple + PyObject *get_line(size_t nline) + { + simpleline_t *r = data.get_line(nline); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( r == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); + } + + // Returns the count of lines + const size_t count() const + { + return data.count(); + } + + // Clears lines + void clear() + { + data.clear_lines(); + refresh_range(); + } + + //-------------------------------------------------------------------------- + bool jumpto(size_t ln, int x, int y) + { + simpleline_place_t l(ln); + return customviewer_t::jumpto(&l, x, y); + } + + //-------------------------------------------------------------------------- + // Initializes and links the Python object to this class + bool init(PyObject *py_link, const char *title) + { + // Already created? + if ( _form != NULL ) + return true; + + // Probe callbacks + features = 0; + static struct + { + const char *cb_name; + int feature; + } const cbtable[] = + { + {S_ON_CLICK, HAVE_CLICK}, + {S_ON_CLOSE, HAVE_CLOSE}, + {S_ON_HINT, HAVE_HINT}, + {S_ON_KEYDOWN, HAVE_KEYDOWN}, + {S_ON_POPUP, HAVE_POPUP}, + {S_ON_DBL_CLICK, HAVE_DBLCLICK}, + {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} + }; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + for ( size_t i=0; i + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +// +// +// Pywraps Simple Custom Viewer functions +// +PyObject *pyscv_init(PyObject *py_link, const char *title) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_simplecustview_t *_this = new py_simplecustview_t(); + bool ok = _this->init(py_link, title); + if ( !ok ) + { + delete _this; + Py_RETURN_NONE; + } + return _this->get_pythis(); +} +#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) + +//-------------------------------------------------------------------------- +bool pyscv_refresh(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh(); +} + +//-------------------------------------------------------------------------- +bool pyscv_delete(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + _this->close(); + delete _this; + return true; +} + +//-------------------------------------------------------------------------- +bool pyscv_refresh_current(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh_current(); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) +{ + DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + const char *line; + if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) + Py_RETURN_NONE; + return PyString_FromString(line); +} + +//-------------------------------------------------------------------------- +bool pyscv_is_focused(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->is_focused(); +} + +void pyscv_clear_popup_menu(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear_popup_menu(); +} + +size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); +} + +size_t pyscv_count(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->count(); +} + +bool pyscv_show(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? false : _this->show(); +} + +void pyscv_close(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->close(); +} + +bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->jumpto(ln, x, y); +} + +// Returns the line tuple +PyObject *pyscv_get_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } + return _this->get_line(nline); +} + +//-------------------------------------------------------------------------- +// Gets the position and returns a tuple (lineno, x, y) +PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) +{ + DECL_THIS; + if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } + return _this->get_pos(mouse); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_clear_lines(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear(); + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +// Adds a line tuple +bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->add_line(py_sl); +} + +//-------------------------------------------------------------------------- +bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->insert_line(nline, py_sl); +} + +//-------------------------------------------------------------------------- +bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) +{ + DECL_THIS; + return _this == NULL ? false : _this->patch_line(nline, offs, value); +} + +//-------------------------------------------------------------------------- +bool pyscv_del_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + return _this == NULL ? false : _this->del_line(nline); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_selection(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } + return _this->py_get_selection(); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) +{ + DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( _this != NULL ) + { + qstring word; + if ( _this->get_current_word(mouse, word) ) + return PyString_FromString(word.c_str()); + } + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +// Edits an existing line +bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->edit_line(nline, py_sl); +} + +//------------------------------------------------------------------------- +TForm *pyscv_get_tform(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? NULL : _this->get_tform(); +} + +//------------------------------------------------------------------------- +TCustomControl *pyscv_get_tcustom_control(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? NULL : _this->get_tcustom_control(); +} + + +#undef DECL_THIS +// +//--------------------------------------------------------------------------- +#endif // __PYWRAPS_CUSTVIEWER__ diff --git a/pywraps/py_custview.py b/pywraps/py_custview.py index afbb0c6b..4d7d790b 100644 --- a/pywraps/py_custview.py +++ b/pywraps/py_custview.py @@ -1,472 +1,472 @@ -# ----------------------------------------------------------------------- -# Standalone and testing code -import sys, struct - -try: - import _idaapi -except: - print "Please try me from inside IDA" - sys.exit(0) - -try: - import pywraps - pywraps_there = True - print "Using pywraps" - - _idaapi.pyscv_init = pywraps.pyscv_init - _idaapi.pyscv_close = pywraps.pyscv_close - _idaapi.pyscv_add_line = pywraps.pyscv_add_line - _idaapi.pyscv_delete = pywraps.pyscv_delete - _idaapi.pyscv_refresh = pywraps.pyscv_refresh - _idaapi.pyscv_show = pywraps.pyscv_show - _idaapi.pyscv_clear_popup_menu = pywraps.pyscv_clear_popup_menu - _idaapi.pyscv_del_line = pywraps.pyscv_del_line - _idaapi.pyscv_get_pos = pywraps.pyscv_get_pos - _idaapi.pyscv_refresh_current = pywraps.pyscv_refresh_current - _idaapi.pyscv_get_current_line = pywraps.pyscv_get_current_line - _idaapi.pyscv_is_focused = pywraps.pyscv_is_focused - _idaapi.pyscv_add_popup_menu = pywraps.pyscv_add_popup_menu - _idaapi.pyscv_get_line = pywraps.pyscv_get_line - _idaapi.pyscv_jumpto = pywraps.pyscv_jumpto - _idaapi.pyscv_edit_line = pywraps.pyscv_edit_line - _idaapi.pyscv_patch_line = pywraps.pyscv_patch_line - _idaapi.pyscv_insert_line = pywraps.pyscv_insert_line - _idaapi.pyscv_count = pywraps.pyscv_count - _idaapi.pyscv_get_selection = pywraps.pyscv_get_selection - _idaapi.pyscv_clear_lines = pywraps.pyscv_clear_lines - _idaapi.pyscv_get_current_word = pywraps.pyscv_get_current_word -except: - pywraps_there = False - print "Not using pywraps" - -# ----------------------------------------------------------------------- -# -class simplecustviewer_t(object): - """The base class for implementing simple custom viewers""" - def __init__(self): - self.__this = None - - def __del__(self): - """Destructor. It also frees the associated C++ object""" - try: - _idaapi.pyscv_delete(self.__this) - except: - pass - - @staticmethod - def __make_sl_arg(line, fgcolor=None, bgcolor=None): - return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) - - def Create(self, title): - """ - Creates the custom view. This should be the first method called after instantiation - - @param title: The title of the view - @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. - In this case better close existing windows - """ - self.title = title - self.__this = _idaapi.pyscv_init(self, title) - return True if self.__this else False - - def Close(self): - """ - Destroys the view. - One has to call Create() afterwards. - Show() can be called and it will call Create() internally. - @return: Boolean - """ - return _idaapi.pyscv_close(self.__this) - - def Show(self): - """ - Shows an already created view. It the view was close, then it will call Create() for you - @return: Boolean - """ - return _idaapi.pyscv_show(self.__this) - - def Refresh(self): - return _idaapi.pyscv_refresh(self.__this) - - def RefreshCurrent(self): - """Refreshes the current line only""" - return _idaapi.pyscv_refresh_current(self.__this) - - def Count(self): - """Returns the number of lines in the view""" - return _idaapi.pyscv_count(self.__this) - - def GetSelection(self): - """ - Returns the selected area or None - @return: - - tuple(x1, y1, x2, y2) - - None if no selection - """ - return _idaapi.pyscv_get_selection(self.__this) - - def ClearLines(self): - """Clears all the lines""" - _idaapi.pyscv_clear_lines(self.__this) - - def AddLine(self, line, fgcolor=None, bgcolor=None): - """ - Adds a colored line to the view - @return: Boolean - """ - return _idaapi.pyscv_add_line(self.__this, self.__make_sl_arg(line, fgcolor, bgcolor)) - - def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): - """ - Inserts a line in the given position - @return: Boolean - """ - return _idaapi.pyscv_insert_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) - - def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): - """ - Edits an existing line. - @return: Boolean - """ - return _idaapi.pyscv_edit_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) - - def PatchLine(self, lineno, offs, value): - """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" - return _idaapi.pyscv_patch_line(self.__this, lineno, offs, value) - - def DelLine(self, lineno): - """ - Deletes an existing line - @return: Boolean - """ - return _idaapi.pyscv_del_line(self.__this, lineno) - - def GetLine(self, lineno): - """ - Returns a line - @param lineno: The line number - @return: - Returns a tuple (colored_line, fgcolor, bgcolor) or None - """ - return _idaapi.pyscv_get_line(self.__this, lineno) - - def GetCurrentWord(self, mouse = 0): - """ - Returns the current word - @param mouse: Use mouse position or cursor position - @return: None if failed or a String containing the current word at mouse or cursor - """ - return _idaapi.pyscv_get_current_word(self.__this, mouse) - - def GetCurrentLine(self, mouse = 0, notags = 0): - """ - Returns the current line. - @param mouse: Current line at mouse pos - @param notags: If True then tag_remove() will be called before returning the line - @return: Returns the current line (colored or uncolored) or None on failure - """ - return _idaapi.pyscv_get_current_line(self.__this, mouse, notags) - - def GetPos(self, mouse = 0): - """ - Returns the current cursor or mouse position. - @param mouse: return mouse position - @return: Returns a tuple (lineno, x, y) - """ - return _idaapi.pyscv_get_pos(self.__this, mouse) - - def GetLineNo(self, mouse = 0): - """Calls GetPos() and returns the current line number or -1 on failure""" - r = self.GetPos(mouse) - return -1 if not r else r[0] - - def Jump(self, lineno, x=0, y=0): - return _idaapi.pyscv_jumpto(self.__this, lineno, x, y) - - def AddPopupMenu(self, title, hotkey=""): - """ - Adds a popup menu item - @param title: The name of the menu item - @param hotkey: Hotkey of the item or just empty - @return: Returns the - """ - return _idaapi.pyscv_add_popup_menu(self.__this, title, hotkey) - - def ClearPopupMenu(self): - """ - Clears all previously installed popup menu items. - Use this function if you're generating menu items on the fly (in the OnPopup() callback), - and before adding new items - """ - _idaapi.pyscv_clear_popup_menu(self.__this) - - def IsFocused(self): - """Returns True if the current view is the focused view""" - return _idaapi.pyscv_is_focused(self.__this) - - def GetTForm(self): - """ - Return the TForm hosting this view. - - @return: The TForm that hosts this view, or None. - """ - return _idaapi.pyscv_get_tform(self.__this) - - def GetTCustomControl(self): - """ - Return the TCustomControl underlying this view. - - @return: The TCustomControl underlying this view, or None. - """ - return _idaapi.pyscv_get_tcustom_control(self.__this) - - - - # Here are all the supported events -# -# def OnClick(self, shift): -# """ -# User clicked in the view -# @param shift: Shift flag -# @return: Boolean. True if you handled the event -# """ -# print "OnClick, shift=%d" % shift -# return True -# -# def OnDblClick(self, shift): -# """ -# User dbl-clicked in the view -# @param shift: Shift flag -# @return: Boolean. True if you handled the event -# """ -# print "OnDblClick, shift=%d" % shift -# return True -# -# def OnCursorPosChanged(self): -# """ -# Cursor position changed. -# @return: Nothing -# """ -# print "OnCurposChanged" -# -# def OnClose(self): -# """ -# The view is closing. Use this event to cleanup. -# @return: Nothing -# """ -# print "OnClose" -# -# def OnKeydown(self, vkey, shift): -# """ -# User pressed a key -# @param vkey: Virtual key code -# @param shift: Shift flag -# @return: Boolean. True if you handled the event -# """ -# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) -# return False -# -# def OnPopup(self): -# """ -# Context menu popup is about to be shown. Create items dynamically if you wish -# @return: Boolean. True if you handled the event -# """ -# print "OnPopup" -# -# def OnHint(self, lineno): -# """ -# Hint requested for the given line number. -# @param lineno: The line number (zero based) -# @return: -# - tuple(number of important lines, hint string) -# - None: if no hint available -# """ -# return (1, "OnHint, line=%d" % lineno) -# -# def OnPopupMenu(self, menu_id): -# """ -# A context (or popup) menu item was executed. -# @param menu_id: ID previously registered with add_popup_menu() -# @return: Boolean -# """ -# print "OnPopupMenu, menu_id=" % menu_id -# return True -# -# - -# - -class say_something_handler_t(idaapi.action_handler_t): - def __init__(self, thing): - idaapi.action_handler_t.__init__(self) - self.thing = thing - - def activate(self, ctx): - print self.thing - - def update(self, ctx): - return idaapi.AST_ENABLE_ALWAYS - - -# ----------------------------------------------------------------------- -class mycv_t(simplecustviewer_t): - def Create(self, sn=None): - # Form the title - title = "Simple custom view test" - if sn: - title += " %d" % sn - - # Create the customviewer - if not simplecustviewer_t.Create(self, title): - return False - - for i in xrange(0, 100): - self.AddLine("Line %d" % i) - -# self.Jump(0) - - return True - - def OnClick(self, shift): - """ - User clicked in the view - @param shift: Shift flag - @return: Boolean. True if you handled the event - """ - print "OnClick, shift=%d" % shift - return True - - def OnDblClick(self, shift): - """ - User dbl-clicked in the view - @param shift: Shift flag - @return: Boolean. True if you handled the event - """ - word = self.GetCurrentWord() - if not word: word = "" - print "OnDblClick, shift=%d, current word=%s" % (shift, word) - return True - - def OnCursorPosChanged(self): - """ - Cursor position changed. - @return: Nothing - """ - print "OnCurposChanged" - - def OnClose(self): - """ - The view is closing. Use this event to cleanup. - @return: Nothing - """ - print "OnClose " + self.title - - def OnKeydown(self, vkey, shift): - """ - User pressed a key - @param vkey: Virtual key code - @param shift: Shift flag - @return: Boolean. True if you handled the event - """ - print "OnKeydown, vk=%d shift=%d" % (vkey, shift) - # ESCAPE? - if vkey == 27: - self.Close() - # VK_DELETE - elif vkey == 46: - n = self.GetLineNo() - if n is not None: - self.DelLine(n) - self.Refresh() - print "Deleted line %d" % n - # Goto? - elif vkey == ord('G'): - n = self.GetLineNo() - if n is not None: - v = idc.AskLong(self.GetLineNo(), "Where to go?") - if v: - self.Jump(v, 0, 5) - elif vkey == ord('R'): - print "refreshing...." - self.Refresh() - elif vkey == ord('C'): - print "refreshing current line..." - self.RefreshCurrent() - elif vkey == ord('A'): - s = idc.AskStr("NewLine%d" % self.Count(), "Append new line") - self.AddLine(s) - self.Refresh() - elif vkey == ord('X'): - print "Clearing all lines" - self.ClearLines() - self.Refresh() - elif vkey == ord('I'): - n = self.GetLineNo() - s = idc.AskStr("InsertedLine%d" % n, "Insert new line") - self.InsertLine(n, s) - self.Refresh() - elif vkey == ord('E'): - l = self.GetCurrentLine(notags=1) - if not l: - return False - n = self.GetLineNo() - print "curline=<%s>" % l - l = l + idaapi.COLSTR("*", idaapi.SCOLOR_VOIDOP) - self.EditLine(n, l) - self.RefreshCurrent() - print "Edited line %d" % n - else: - return False - return True - - def OnHint(self, lineno): - """ - Hint requested for the given line number. - @param lineno: The line number (zero based) - @return: - - tuple(number of important lines, hint string) - - None: if no hint available - """ - return (1, "OnHint, line=%d" % lineno) - -# ----------------------------------------------------------------------- -try: - # created already? - mycv - print "Already created, will close it..." - mycv.Close() - del mycv -except: - pass - -def show_win(): - x = mycv_t() - if not x.Create(): - print "Failed to create!" - return None - x.Show() - tcc = x.GetTCustomControl() - - # Register actions - for thing in ["Hello", "World"]: - actname = "custview:say_%s" % thing - idaapi.register_action( - idaapi.action_desc_t(actname, "Say %s" % thing, say_something_handler_t(thing))) - idaapi.attach_action_to_popup(tcc, None, actname) - return x - -mycv = show_win() -if not mycv: - del mycv - -def make_many(n): - L = [] - for i in xrange(1, n+1): - v = mycv_t() - if not v.Create(i): - break - v.Show() - L.append(v) - return L - -# +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys, struct + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +try: + import pywraps + pywraps_there = True + print "Using pywraps" + + _idaapi.pyscv_init = pywraps.pyscv_init + _idaapi.pyscv_close = pywraps.pyscv_close + _idaapi.pyscv_add_line = pywraps.pyscv_add_line + _idaapi.pyscv_delete = pywraps.pyscv_delete + _idaapi.pyscv_refresh = pywraps.pyscv_refresh + _idaapi.pyscv_show = pywraps.pyscv_show + _idaapi.pyscv_clear_popup_menu = pywraps.pyscv_clear_popup_menu + _idaapi.pyscv_del_line = pywraps.pyscv_del_line + _idaapi.pyscv_get_pos = pywraps.pyscv_get_pos + _idaapi.pyscv_refresh_current = pywraps.pyscv_refresh_current + _idaapi.pyscv_get_current_line = pywraps.pyscv_get_current_line + _idaapi.pyscv_is_focused = pywraps.pyscv_is_focused + _idaapi.pyscv_add_popup_menu = pywraps.pyscv_add_popup_menu + _idaapi.pyscv_get_line = pywraps.pyscv_get_line + _idaapi.pyscv_jumpto = pywraps.pyscv_jumpto + _idaapi.pyscv_edit_line = pywraps.pyscv_edit_line + _idaapi.pyscv_patch_line = pywraps.pyscv_patch_line + _idaapi.pyscv_insert_line = pywraps.pyscv_insert_line + _idaapi.pyscv_count = pywraps.pyscv_count + _idaapi.pyscv_get_selection = pywraps.pyscv_get_selection + _idaapi.pyscv_clear_lines = pywraps.pyscv_clear_lines + _idaapi.pyscv_get_current_word = pywraps.pyscv_get_current_word +except: + pywraps_there = False + print "Not using pywraps" + +# ----------------------------------------------------------------------- +# +class simplecustviewer_t(object): + """The base class for implementing simple custom viewers""" + def __init__(self): + self.__this = None + + def __del__(self): + """Destructor. It also frees the associated C++ object""" + try: + _idaapi.pyscv_delete(self.__this) + except: + pass + + @staticmethod + def __make_sl_arg(line, fgcolor=None, bgcolor=None): + return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) + + def Create(self, title): + """ + Creates the custom view. This should be the first method called after instantiation + + @param title: The title of the view + @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. + In this case better close existing windows + """ + self.title = title + self.__this = _idaapi.pyscv_init(self, title) + return True if self.__this else False + + def Close(self): + """ + Destroys the view. + One has to call Create() afterwards. + Show() can be called and it will call Create() internally. + @return: Boolean + """ + return _idaapi.pyscv_close(self.__this) + + def Show(self): + """ + Shows an already created view. It the view was close, then it will call Create() for you + @return: Boolean + """ + return _idaapi.pyscv_show(self.__this) + + def Refresh(self): + return _idaapi.pyscv_refresh(self.__this) + + def RefreshCurrent(self): + """Refreshes the current line only""" + return _idaapi.pyscv_refresh_current(self.__this) + + def Count(self): + """Returns the number of lines in the view""" + return _idaapi.pyscv_count(self.__this) + + def GetSelection(self): + """ + Returns the selected area or None + @return: + - tuple(x1, y1, x2, y2) + - None if no selection + """ + return _idaapi.pyscv_get_selection(self.__this) + + def ClearLines(self): + """Clears all the lines""" + _idaapi.pyscv_clear_lines(self.__this) + + def AddLine(self, line, fgcolor=None, bgcolor=None): + """ + Adds a colored line to the view + @return: Boolean + """ + return _idaapi.pyscv_add_line(self.__this, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): + """ + Inserts a line in the given position + @return: Boolean + """ + return _idaapi.pyscv_insert_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): + """ + Edits an existing line. + @return: Boolean + """ + return _idaapi.pyscv_edit_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def PatchLine(self, lineno, offs, value): + """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" + return _idaapi.pyscv_patch_line(self.__this, lineno, offs, value) + + def DelLine(self, lineno): + """ + Deletes an existing line + @return: Boolean + """ + return _idaapi.pyscv_del_line(self.__this, lineno) + + def GetLine(self, lineno): + """ + Returns a line + @param lineno: The line number + @return: + Returns a tuple (colored_line, fgcolor, bgcolor) or None + """ + return _idaapi.pyscv_get_line(self.__this, lineno) + + def GetCurrentWord(self, mouse = 0): + """ + Returns the current word + @param mouse: Use mouse position or cursor position + @return: None if failed or a String containing the current word at mouse or cursor + """ + return _idaapi.pyscv_get_current_word(self.__this, mouse) + + def GetCurrentLine(self, mouse = 0, notags = 0): + """ + Returns the current line. + @param mouse: Current line at mouse pos + @param notags: If True then tag_remove() will be called before returning the line + @return: Returns the current line (colored or uncolored) or None on failure + """ + return _idaapi.pyscv_get_current_line(self.__this, mouse, notags) + + def GetPos(self, mouse = 0): + """ + Returns the current cursor or mouse position. + @param mouse: return mouse position + @return: Returns a tuple (lineno, x, y) + """ + return _idaapi.pyscv_get_pos(self.__this, mouse) + + def GetLineNo(self, mouse = 0): + """Calls GetPos() and returns the current line number or -1 on failure""" + r = self.GetPos(mouse) + return -1 if not r else r[0] + + def Jump(self, lineno, x=0, y=0): + return _idaapi.pyscv_jumpto(self.__this, lineno, x, y) + + def AddPopupMenu(self, title, hotkey=""): + """ + Adds a popup menu item + @param title: The name of the menu item + @param hotkey: Hotkey of the item or just empty + @return: Returns the + """ + return _idaapi.pyscv_add_popup_menu(self.__this, title, hotkey) + + def ClearPopupMenu(self): + """ + Clears all previously installed popup menu items. + Use this function if you're generating menu items on the fly (in the OnPopup() callback), + and before adding new items + """ + _idaapi.pyscv_clear_popup_menu(self.__this) + + def IsFocused(self): + """Returns True if the current view is the focused view""" + return _idaapi.pyscv_is_focused(self.__this) + + def GetTForm(self): + """ + Return the TForm hosting this view. + + @return: The TForm that hosts this view, or None. + """ + return _idaapi.pyscv_get_tform(self.__this) + + def GetTCustomControl(self): + """ + Return the TCustomControl underlying this view. + + @return: The TCustomControl underlying this view, or None. + """ + return _idaapi.pyscv_get_tcustom_control(self.__this) + + + + # Here are all the supported events +# +# def OnClick(self, shift): +# """ +# User clicked in the view +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnClick, shift=%d" % shift +# return True +# +# def OnDblClick(self, shift): +# """ +# User dbl-clicked in the view +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnDblClick, shift=%d" % shift +# return True +# +# def OnCursorPosChanged(self): +# """ +# Cursor position changed. +# @return: Nothing +# """ +# print "OnCurposChanged" +# +# def OnClose(self): +# """ +# The view is closing. Use this event to cleanup. +# @return: Nothing +# """ +# print "OnClose" +# +# def OnKeydown(self, vkey, shift): +# """ +# User pressed a key +# @param vkey: Virtual key code +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) +# return False +# +# def OnPopup(self): +# """ +# Context menu popup is about to be shown. Create items dynamically if you wish +# @return: Boolean. True if you handled the event +# """ +# print "OnPopup" +# +# def OnHint(self, lineno): +# """ +# Hint requested for the given line number. +# @param lineno: The line number (zero based) +# @return: +# - tuple(number of important lines, hint string) +# - None: if no hint available +# """ +# return (1, "OnHint, line=%d" % lineno) +# +# def OnPopupMenu(self, menu_id): +# """ +# A context (or popup) menu item was executed. +# @param menu_id: ID previously registered with add_popup_menu() +# @return: Boolean +# """ +# print "OnPopupMenu, menu_id=" % menu_id +# return True +# +# + +# + +class say_something_handler_t(idaapi.action_handler_t): + def __init__(self, thing): + idaapi.action_handler_t.__init__(self) + self.thing = thing + + def activate(self, ctx): + print self.thing + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +# ----------------------------------------------------------------------- +class mycv_t(simplecustviewer_t): + def Create(self, sn=None): + # Form the title + title = "Simple custom view test" + if sn: + title += " %d" % sn + + # Create the customviewer + if not simplecustviewer_t.Create(self, title): + return False + + for i in xrange(0, 100): + self.AddLine("Line %d" % i) + +# self.Jump(0) + + return True + + def OnClick(self, shift): + """ + User clicked in the view + @param shift: Shift flag + @return: Boolean. True if you handled the event + """ + print "OnClick, shift=%d" % shift + return True + + def OnDblClick(self, shift): + """ + User dbl-clicked in the view + @param shift: Shift flag + @return: Boolean. True if you handled the event + """ + word = self.GetCurrentWord() + if not word: word = "" + print "OnDblClick, shift=%d, current word=%s" % (shift, word) + return True + + def OnCursorPosChanged(self): + """ + Cursor position changed. + @return: Nothing + """ + print "OnCurposChanged" + + def OnClose(self): + """ + The view is closing. Use this event to cleanup. + @return: Nothing + """ + print "OnClose " + self.title + + def OnKeydown(self, vkey, shift): + """ + User pressed a key + @param vkey: Virtual key code + @param shift: Shift flag + @return: Boolean. True if you handled the event + """ + print "OnKeydown, vk=%d shift=%d" % (vkey, shift) + # ESCAPE? + if vkey == 27: + self.Close() + # VK_DELETE + elif vkey == 46: + n = self.GetLineNo() + if n is not None: + self.DelLine(n) + self.Refresh() + print "Deleted line %d" % n + # Goto? + elif vkey == ord('G'): + n = self.GetLineNo() + if n is not None: + v = idc.AskLong(self.GetLineNo(), "Where to go?") + if v: + self.Jump(v, 0, 5) + elif vkey == ord('R'): + print "refreshing...." + self.Refresh() + elif vkey == ord('C'): + print "refreshing current line..." + self.RefreshCurrent() + elif vkey == ord('A'): + s = idc.AskStr("NewLine%d" % self.Count(), "Append new line") + self.AddLine(s) + self.Refresh() + elif vkey == ord('X'): + print "Clearing all lines" + self.ClearLines() + self.Refresh() + elif vkey == ord('I'): + n = self.GetLineNo() + s = idc.AskStr("InsertedLine%d" % n, "Insert new line") + self.InsertLine(n, s) + self.Refresh() + elif vkey == ord('E'): + l = self.GetCurrentLine(notags=1) + if not l: + return False + n = self.GetLineNo() + print "curline=<%s>" % l + l = l + idaapi.COLSTR("*", idaapi.SCOLOR_VOIDOP) + self.EditLine(n, l) + self.RefreshCurrent() + print "Edited line %d" % n + else: + return False + return True + + def OnHint(self, lineno): + """ + Hint requested for the given line number. + @param lineno: The line number (zero based) + @return: + - tuple(number of important lines, hint string) + - None: if no hint available + """ + return (1, "OnHint, line=%d" % lineno) + +# ----------------------------------------------------------------------- +try: + # created already? + mycv + print "Already created, will close it..." + mycv.Close() + del mycv +except: + pass + +def show_win(): + x = mycv_t() + if not x.Create(): + print "Failed to create!" + return None + x.Show() + tcc = x.GetTCustomControl() + + # Register actions + for thing in ["Hello", "World"]: + actname = "custview:say_%s" % thing + idaapi.register_action( + idaapi.action_desc_t(actname, "Say %s" % thing, say_something_handler_t(thing))) + idaapi.attach_action_to_popup(tcc, None, actname) + return x + +mycv = show_win() +if not mycv: + del mycv + +def make_many(n): + L = [] + for i in xrange(1, n+1): + v = mycv_t() + if not v.Create(i): + break + v.Show() + L.append(v) + return L + +# diff --git a/pywraps/py_cvt.hpp b/pywraps/py_cvt.hpp index 1094a167..2d465f72 100644 --- a/pywraps/py_cvt.hpp +++ b/pywraps/py_cvt.hpp @@ -1,757 +1,757 @@ -#ifndef __PYCVT__ -#define __PYCVT__ - -// - -//------------------------------------------------------------------------ -// String constants used -static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; -static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; -static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; -static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; - -// Constants used by get_idaapi_class_reference() -#define PY_CLSID_CVT_INT64 0 -#define PY_CLSID_APPCALL_SKEL_OBJ 1 -#define PY_CLSID_CVT_BYREF 2 -#define PY_CLSID_LAST 3 - -//--------------------------------------------------------------------------- -// Use these macros to define script<->C fields -#define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt } -#define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0) -#define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1) - -//--------------------------------------------------------------------------- -enum scfield_types_t -{ - // Numeric fields - FT_FIRST_NUM, - FT_INT, - FT_SIZET, - FT_SSIZET, - FT_NUM16, - FT_NUM32, - FT_LAST_NUM, - // String field - FT_STR, - FT_CHAR, - // Object fields - FT_ARR, - // Allocated array of strings - FT_STRARR, - // Allocated array of 16bit numbers - FT_NUM16ARR, - // Fixed size character array. The size must be passed in the definition - FT_CHRARR_STATIC, -}; - -//--------------------------------------------------------------------------- -struct scfld_t -{ - const char *field_name; - uint32 field_type; - size_t field_offs; - bool is_optional; -}; - -#define FT_VALUE_MASK 0xFFFF0000 -// Possible return values of conversion functions -#define FT_NOT_FOUND -1 -#define FT_BAD_TYPE -2 -#define FT_OK 1 - -//------------------------------------------------------------------------- -Py_ssize_t pyvar_walk_list( - const ref_t &py_list, - int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), - void *ud) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - Py_ssize_t size = CIP_FAILED; - do - { - PyObject *o = py_list.o; - if ( !PyList_CheckExact(o) && !PyW_IsSequenceType(o) ) - break; - - bool is_seq = !PyList_CheckExact(o); - size = is_seq ? PySequence_Size(o) : PyList_Size(o); - if ( cb == NULL ) - break; - - Py_ssize_t i; - for ( i=0; i= CIP_OK; - if ( !ok ) - PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); - return ok; -} - -//------------------------------------------------------------------------ -static idc_class_t *get_py_idc_cvt_opaque() -{ - return find_idc_class(S_PY_IDC_OPAQUE_T); -} - -//------------------------------------------------------------------------- -// Utility function to create opaque / convertible Python <-> IDC variables -// The referred Python variable will have its reference increased -static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Create an IDC object of this special helper class - if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) - return false; - - // Store the CVT id - idc_value_t idc_val; - idc_val.set_long(PY_ICID_OPAQUE); - VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); - - // Store the value as a PVOID referencing the given Python object - py_var.incref(); - idc_val.set_pvoid(py_var.o); - VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); - - return true; -} - -//------------------------------------------------------------------------ -// IDC Opaque object destructor: when the IDC object dies we kill the -// opaque Python object along with it -static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; -static error_t idaapi py_idc_opaque_dtor( - idc_value_t *argv, - idc_value_t * /*res*/) -{ - // This can be called at plugin registration time, when a - // 'script_plugin_t' instance is ::free()'d. It is - // not guaranteed that we have the GIL at that point. - PYW_GIL_GET; - - // Get the value from the object - idc_value_t idc_val; - VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); - - // Extract the Python object reference - PyObject *py_obj = (PyObject *)idc_val.pvoid; - - // Decrease its reference (and eventually destroy it) - Py_DECREF(py_obj); - - return eOk; -} - -//------------------------------------------------------------------------- -// Converts a Python variable into an IDC variable -// This function returns on one CIP_XXXX -int pyvar_to_idcvar( - const ref_t &py_var, - idc_value_t *idc_var, - int *gvar_sn) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // None / NULL - if ( py_var == NULL || py_var.o == Py_None ) - { - idc_var->set_long(0); - } - // Numbers? - else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) - { - return CIP_OK; - } - // String - else if ( PyString_Check(py_var.o) ) - { - idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); - } - // Boolean - else if ( PyBool_Check(py_var.o) ) - { - idc_var->set_long(py_var.o == Py_True ? 1 : 0); - } - // Float - else if ( PyFloat_Check(py_var.o) ) - { - double dresult = PyFloat_AsDouble(py_var.o); - ieee_realcvt((void *)&dresult, idc_var->e, 3); - idc_var->vtype = VT_FLOAT; - } - // void* - else if ( PyCObject_Check(py_var.o) ) - { - idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); - } - // Python list? - else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) - { - // Create the object - VarObject(idc_var); - - // Determine list size and type - bool is_seq = !PyList_CheckExact(py_var.o); - Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); - bool ok = true; - qstring attr_name; - - // Convert each item - for ( Py_ssize_t i=0; i= CIP_OK; - if ( ok ) - { - // Form the attribute name - newref_t py_int(PyInt_FromSsize_t(i)); - ok = PyW_ObjectToString(py_int.o, &attr_name); - if ( !ok ) - break; - // Store the attribute - VarSetAttr(idc_var, attr_name.c_str(), &v); - } - if ( !ok ) - break; - } - return ok ? CIP_OK : CIP_FAILED; - } - // Dictionary: we convert to an IDC object - else if ( PyDict_Check(py_var.o) ) - { - // Create an empty IDC object - VarObject(idc_var); - - // Get the dict.items() list - newref_t py_items(PyDict_Items(py_var.o)); - - // Get the size of the list - qstring key_name; - bool ok = true; - Py_ssize_t size = PySequence_Size(py_items.o); - for ( Py_ssize_t i=0; i (key, value) - PyObject *py_item = PyList_GetItem(py_items.o, i); - - // Extract key/value - newref_t key(PySequence_GetItem(py_item, 0)); - newref_t val(PySequence_GetItem(py_item, 1)); - - // Get key's string representation - PyW_ObjectToString(key.o, &key_name); - - // Convert the attribute into an IDC value - idc_value_t v; - ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; - if ( ok ) - { - // Store the attribute - VarSetAttr(idc_var, key_name.c_str(), &v); - } - if ( !ok ) - break; - } - return ok ? CIP_OK : CIP_FAILED; - } - // Possible function? - else if ( PyCallable_Check(py_var.o) ) - { - idc_var->clear(); - idc_var->vtype = VT_FUNC; - idc_var->funcidx = -1; // Does not apply - return CIP_OK; - } - // Objects: - // - pyidc_cvt objects: int64, byref, opaque - // - other python objects - else - { - // Get the type - int cvt_id = get_pyidc_cvt_type(py_var.o); - switch ( cvt_id ) - { - // - // INT64 - // - case PY_ICID_INT64: - { - // Get the value attribute - ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); - if ( attr == NULL ) - return false; - idc_var->set_int64(PyLong_AsLongLong(attr.o)); - return CIP_OK; - } - // - // BYREF - // - case PY_ICID_BYREF: - { - // BYREF always require this parameter - if ( gvar_sn == NULL ) - return CIP_FAILED; - - // Get the value attribute - ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); - if ( attr == NULL ) - return CIP_FAILED; - - // Create a global variable - char buf[MAXSTR]; - qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); - idc_value_t *gvar = add_idc_gvar(buf); - // Convert the python value into the IDC global variable - bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; - if ( ok ) - { - (*gvar_sn)++; - // Create a reference to this global variable - VarRef(idc_var, gvar); - } - return ok ? CIP_OK : CIP_FAILED; - } - // - // OPAQUE - // - case PY_ICID_OPAQUE: - { - if ( !wrap_PyObject_ptr(py_var, idc_var) ) - return CIP_FAILED; - return CIP_OK_OPAQUE; - } - // - // Other objects - // - default: - // A normal object? - newref_t py_dir(PyObject_Dir(py_var.o)); - Py_ssize_t size = PyList_Size(py_dir.o); - if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) - return CIP_FAILED; - // Create the IDC object - VarObject(idc_var); - for ( Py_ssize_t i=0; i 2 ) - && (strncmp(field_name, "__", 2) == 0 ) - && (strncmp(field_name+len-2, "__", 2) == 0) ) - { - continue; - } - - idc_value_t v; - // Get the non-private attribute from the object - newref_t attr(PyObject_GetAttrString(py_var.o, field_name)); - if (attr == NULL - // Convert the attribute into an IDC value - || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK) - { - return CIP_FAILED; - } - - // Store the attribute - VarSetAttr(idc_var, field_name, &v); - } - } - } - return CIP_OK; -} - -//------------------------------------------------------------------------- -inline PyObject *cvt_to_pylong(int32 v) -{ - return PyLong_FromLong(v); -} - -inline PyObject *cvt_to_pylong(int64 v) -{ - return PyLong_FromLongLong(v); -} - -//------------------------------------------------------------------------- -// Converts an IDC variable to a Python variable -// If py_var points to an existing object then the object will be updated -// If py_var points to an existing immutable object then ZERO is returned -// Returns one of CIP_xxxx. Check pywraps.hpp -int idcvar_to_pyvar( - const idc_value_t &idc_var, - ref_t *py_var) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - switch ( idc_var.vtype ) - { - case VT_PVOID: - if ( *py_var == NULL ) - { - newref_t nr(PyCObject_FromVoidPtr(idc_var.pvoid, NULL)); - *py_var = nr; - } - else - { - return CIP_IMMUTABLE; - } - break; - - case VT_INT64: - { - // Recycle? - if ( *py_var != NULL ) - { - // Recycling an int64 object? - int t = get_pyidc_cvt_type(py_var->o); - if ( t != PY_ICID_INT64 ) - return CIP_IMMUTABLE; // Cannot recycle immutable object - // Update the attribute - PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); - return CIP_OK; - } - ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); - if ( py_cls == NULL ) - return CIP_FAILED; - *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); - if ( PyW_GetError() || *py_var == NULL ) - return CIP_FAILED; - break; - } - -#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) - case VT_STR: - *py_var = newref_t(PyString_FromString(idc_var.str)); - break; - -#endif - case VT_STR2: - if ( *py_var == NULL ) - { - const qstring &s = idc_var.qstr(); - *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); - break; - } - else - return CIP_IMMUTABLE; // Cannot recycle immutable object - case VT_LONG: - // Cannot recycle immutable objects - if ( *py_var != NULL ) - return CIP_IMMUTABLE; - *py_var = newref_t(cvt_to_pylong(idc_var.num)); - break; - case VT_FLOAT: - if ( *py_var == NULL ) - { - double x; - if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) - INTERR(30160); - - *py_var = newref_t(PyFloat_FromDouble(x)); - break; - } - else - return CIP_IMMUTABLE; - - case VT_REF: - { - if ( *py_var == NULL ) - { - ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); - if ( py_cls == NULL ) - return CIP_FAILED; - - // Create a byref object with None value. We populate it later - *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); - if ( PyW_GetError() || *py_var == NULL ) - return CIP_FAILED; - } - int t = get_pyidc_cvt_type(py_var->o); - if ( t != PY_ICID_BYREF ) - return CIP_FAILED; - - // Dereference - // (Since we are not using VREF_COPY flag, we can safely const_cast) - idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); - if ( dref_v == NULL ) - return CIP_FAILED; - - // Can we recycle the object? - ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); - if ( new_py_val != NULL ) - { - // Recycle - t = idcvar_to_pyvar(*dref_v, &new_py_val); - - // Success? Nothing more to be done - if ( t == CIP_OK ) - return CIP_OK; - - // Clear it so we don't recycle it - new_py_val = ref_t(); - } - // Try to convert (not recycle) - if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) - return CIP_FAILED; - - // Update the attribute - PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); - break; - } - - // Can convert back into a Python object or Python dictionary - // (Depending if py_var will be recycled and it was a dictionary) - case VT_OBJ: - { - // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields - idc_value_t idc_val; - if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk - && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) - { - // Extract the object - *py_var = borref_t((PyObject *) idc_val.pvoid); - return CIP_OK_OPAQUE; - } - ref_t obj; - bool is_dict = false; - - // Need to create a new object? - if ( *py_var == NULL ) - { - // Get skeleton class reference - ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); - if ( py_cls == NULL ) - return CIP_FAILED; - - // Call constructor - obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); - if ( PyW_GetError() || obj == NULL ) - return CIP_FAILED; - } - else - { - // Recycle existing variable - obj = *py_var; - if ( PyDict_Check(obj.o) ) - is_dict = true; - } - - // Walk the IDC attributes and store into python - for (const char *attr_name = VarFirstAttr(&idc_var); - attr_name != NULL; - attr_name = VarNextAttr(&idc_var, attr_name) ) - { - // Get the attribute - idc_value_t v; - VarGetAttr(&idc_var, attr_name, &v, true); - - // Convert attribute to a python value (recursively) - ref_t py_attr; - int cvt = idcvar_to_pyvar(v, &py_attr); - if ( cvt <= CIP_IMMUTABLE ) - return CIP_FAILED; - if ( is_dict ) - PyDict_SetItemString(obj.o, attr_name, py_attr.o); - else - PyObject_SetAttrString(obj.o, attr_name, py_attr.o); - } - *py_var = obj; - break; - } - // Unhandled type - default: - *py_var = ref_t(); - return CIP_FAILED; - } - return CIP_OK; -} - -//------------------------------------------------------------------------- -// Converts IDC arguments to Python argument list or just one tuple -// If 'decref' is NULL then 'pargs' will contain one element which is the tuple -bool pyw_convert_idc_args( - const idc_value_t args[], - int nargs, - ref_vec_t &pargs, - bool as_tupple, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - ref_t py_tuple; - - pargs.qclear(); - - if ( as_tupple ) - { - py_tuple = newref_t(PyTuple_New(nargs)); - if ( py_tuple == NULL ) - { - if ( errbuf != 0 && errbufsize > 0 ) - qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); - return false; - } - } - - for ( int i=0; i 0 ) - qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype); - return false; - } - - if ( as_tupple ) - { - // PyTuple_SetItem() steals the reference. - py_obj.incref(); - QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0); - } - else - { - pargs.push_back(py_obj); - } - } - - // Add the tuple to the list of args only now. Doing so earlier will - // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will - // cause 'PyTuple_SetItem()' to fail. - if ( as_tupple ) - pargs.push_back(py_tuple); - - return true; -} - -// - -#endif +#ifndef __PYCVT__ +#define __PYCVT__ + +// + +//------------------------------------------------------------------------ +// String constants used +static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; +static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; +static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; +static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; + +// Constants used by get_idaapi_class_reference() +#define PY_CLSID_CVT_INT64 0 +#define PY_CLSID_APPCALL_SKEL_OBJ 1 +#define PY_CLSID_CVT_BYREF 2 +#define PY_CLSID_LAST 3 + +//--------------------------------------------------------------------------- +// Use these macros to define script<->C fields +#define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt } +#define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0) +#define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1) + +//--------------------------------------------------------------------------- +enum scfield_types_t +{ + // Numeric fields + FT_FIRST_NUM, + FT_INT, + FT_SIZET, + FT_SSIZET, + FT_NUM16, + FT_NUM32, + FT_LAST_NUM, + // String field + FT_STR, + FT_CHAR, + // Object fields + FT_ARR, + // Allocated array of strings + FT_STRARR, + // Allocated array of 16bit numbers + FT_NUM16ARR, + // Fixed size character array. The size must be passed in the definition + FT_CHRARR_STATIC, +}; + +//--------------------------------------------------------------------------- +struct scfld_t +{ + const char *field_name; + uint32 field_type; + size_t field_offs; + bool is_optional; +}; + +#define FT_VALUE_MASK 0xFFFF0000 +// Possible return values of conversion functions +#define FT_NOT_FOUND -1 +#define FT_BAD_TYPE -2 +#define FT_OK 1 + +//------------------------------------------------------------------------- +Py_ssize_t pyvar_walk_list( + const ref_t &py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), + void *ud) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + Py_ssize_t size = CIP_FAILED; + do + { + PyObject *o = py_list.o; + if ( !PyList_CheckExact(o) && !PyW_IsSequenceType(o) ) + break; + + bool is_seq = !PyList_CheckExact(o); + size = is_seq ? PySequence_Size(o) : PyList_Size(o); + if ( cb == NULL ) + break; + + Py_ssize_t i; + for ( i=0; i= CIP_OK; + if ( !ok ) + PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); + return ok; +} + +//------------------------------------------------------------------------ +static idc_class_t *get_py_idc_cvt_opaque() +{ + return find_idc_class(S_PY_IDC_OPAQUE_T); +} + +//------------------------------------------------------------------------- +// Utility function to create opaque / convertible Python <-> IDC variables +// The referred Python variable will have its reference increased +static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Create an IDC object of this special helper class + if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) + return false; + + // Store the CVT id + idc_value_t idc_val; + idc_val.set_long(PY_ICID_OPAQUE); + VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); + + // Store the value as a PVOID referencing the given Python object + py_var.incref(); + idc_val.set_pvoid(py_var.o); + VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); + + return true; +} + +//------------------------------------------------------------------------ +// IDC Opaque object destructor: when the IDC object dies we kill the +// opaque Python object along with it +static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; +static error_t idaapi py_idc_opaque_dtor( + idc_value_t *argv, + idc_value_t * /*res*/) +{ + // This can be called at plugin registration time, when a + // 'script_plugin_t' instance is ::free()'d. It is + // not guaranteed that we have the GIL at that point. + PYW_GIL_GET; + + // Get the value from the object + idc_value_t idc_val; + VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); + + // Extract the Python object reference + PyObject *py_obj = (PyObject *)idc_val.pvoid; + + // Decrease its reference (and eventually destroy it) + Py_DECREF(py_obj); + + return eOk; +} + +//------------------------------------------------------------------------- +// Converts a Python variable into an IDC variable +// This function returns on one CIP_XXXX +int pyvar_to_idcvar( + const ref_t &py_var, + idc_value_t *idc_var, + int *gvar_sn) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // None / NULL + if ( py_var == NULL || py_var.o == Py_None ) + { + idc_var->set_long(0); + } + // Numbers? + else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) + { + return CIP_OK; + } + // String + else if ( PyString_Check(py_var.o) ) + { + idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); + } + // Boolean + else if ( PyBool_Check(py_var.o) ) + { + idc_var->set_long(py_var.o == Py_True ? 1 : 0); + } + // Float + else if ( PyFloat_Check(py_var.o) ) + { + double dresult = PyFloat_AsDouble(py_var.o); + ieee_realcvt((void *)&dresult, idc_var->e, 3); + idc_var->vtype = VT_FLOAT; + } + // void* + else if ( PyCObject_Check(py_var.o) ) + { + idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); + } + // Python list? + else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) + { + // Create the object + VarObject(idc_var); + + // Determine list size and type + bool is_seq = !PyList_CheckExact(py_var.o); + Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); + bool ok = true; + qstring attr_name; + + // Convert each item + for ( Py_ssize_t i=0; i= CIP_OK; + if ( ok ) + { + // Form the attribute name + newref_t py_int(PyInt_FromSsize_t(i)); + ok = PyW_ObjectToString(py_int.o, &attr_name); + if ( !ok ) + break; + // Store the attribute + VarSetAttr(idc_var, attr_name.c_str(), &v); + } + if ( !ok ) + break; + } + return ok ? CIP_OK : CIP_FAILED; + } + // Dictionary: we convert to an IDC object + else if ( PyDict_Check(py_var.o) ) + { + // Create an empty IDC object + VarObject(idc_var); + + // Get the dict.items() list + newref_t py_items(PyDict_Items(py_var.o)); + + // Get the size of the list + qstring key_name; + bool ok = true; + Py_ssize_t size = PySequence_Size(py_items.o); + for ( Py_ssize_t i=0; i (key, value) + PyObject *py_item = PyList_GetItem(py_items.o, i); + + // Extract key/value + newref_t key(PySequence_GetItem(py_item, 0)); + newref_t val(PySequence_GetItem(py_item, 1)); + + // Get key's string representation + PyW_ObjectToString(key.o, &key_name); + + // Convert the attribute into an IDC value + idc_value_t v; + ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; + if ( ok ) + { + // Store the attribute + VarSetAttr(idc_var, key_name.c_str(), &v); + } + if ( !ok ) + break; + } + return ok ? CIP_OK : CIP_FAILED; + } + // Possible function? + else if ( PyCallable_Check(py_var.o) ) + { + idc_var->clear(); + idc_var->vtype = VT_FUNC; + idc_var->funcidx = -1; // Does not apply + return CIP_OK; + } + // Objects: + // - pyidc_cvt objects: int64, byref, opaque + // - other python objects + else + { + // Get the type + int cvt_id = get_pyidc_cvt_type(py_var.o); + switch ( cvt_id ) + { + // + // INT64 + // + case PY_ICID_INT64: + { + // Get the value attribute + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); + if ( attr == NULL ) + return false; + idc_var->set_int64(PyLong_AsLongLong(attr.o)); + return CIP_OK; + } + // + // BYREF + // + case PY_ICID_BYREF: + { + // BYREF always require this parameter + if ( gvar_sn == NULL ) + return CIP_FAILED; + + // Get the value attribute + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); + if ( attr == NULL ) + return CIP_FAILED; + + // Create a global variable + char buf[MAXSTR]; + qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); + idc_value_t *gvar = add_idc_gvar(buf); + // Convert the python value into the IDC global variable + bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; + if ( ok ) + { + (*gvar_sn)++; + // Create a reference to this global variable + VarRef(idc_var, gvar); + } + return ok ? CIP_OK : CIP_FAILED; + } + // + // OPAQUE + // + case PY_ICID_OPAQUE: + { + if ( !wrap_PyObject_ptr(py_var, idc_var) ) + return CIP_FAILED; + return CIP_OK_OPAQUE; + } + // + // Other objects + // + default: + // A normal object? + newref_t py_dir(PyObject_Dir(py_var.o)); + Py_ssize_t size = PyList_Size(py_dir.o); + if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) + return CIP_FAILED; + // Create the IDC object + VarObject(idc_var); + for ( Py_ssize_t i=0; i 2 ) + && (strncmp(field_name, "__", 2) == 0 ) + && (strncmp(field_name+len-2, "__", 2) == 0) ) + { + continue; + } + + idc_value_t v; + // Get the non-private attribute from the object + newref_t attr(PyObject_GetAttrString(py_var.o, field_name)); + if (attr == NULL + // Convert the attribute into an IDC value + || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK) + { + return CIP_FAILED; + } + + // Store the attribute + VarSetAttr(idc_var, field_name, &v); + } + } + } + return CIP_OK; +} + +//------------------------------------------------------------------------- +inline PyObject *cvt_to_pylong(int32 v) +{ + return PyLong_FromLong(v); +} + +inline PyObject *cvt_to_pylong(int64 v) +{ + return PyLong_FromLongLong(v); +} + +//------------------------------------------------------------------------- +// Converts an IDC variable to a Python variable +// If py_var points to an existing object then the object will be updated +// If py_var points to an existing immutable object then ZERO is returned +// Returns one of CIP_xxxx. Check pywraps.hpp +int idcvar_to_pyvar( + const idc_value_t &idc_var, + ref_t *py_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + switch ( idc_var.vtype ) + { + case VT_PVOID: + if ( *py_var == NULL ) + { + newref_t nr(PyCObject_FromVoidPtr(idc_var.pvoid, NULL)); + *py_var = nr; + } + else + { + return CIP_IMMUTABLE; + } + break; + + case VT_INT64: + { + // Recycle? + if ( *py_var != NULL ) + { + // Recycling an int64 object? + int t = get_pyidc_cvt_type(py_var->o); + if ( t != PY_ICID_INT64 ) + return CIP_IMMUTABLE; // Cannot recycle immutable object + // Update the attribute + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); + return CIP_OK; + } + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); + if ( py_cls == NULL ) + return CIP_FAILED; + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); + if ( PyW_GetError() || *py_var == NULL ) + return CIP_FAILED; + break; + } + +#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) + case VT_STR: + *py_var = newref_t(PyString_FromString(idc_var.str)); + break; + +#endif + case VT_STR2: + if ( *py_var == NULL ) + { + const qstring &s = idc_var.qstr(); + *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); + break; + } + else + return CIP_IMMUTABLE; // Cannot recycle immutable object + case VT_LONG: + // Cannot recycle immutable objects + if ( *py_var != NULL ) + return CIP_IMMUTABLE; + *py_var = newref_t(cvt_to_pylong(idc_var.num)); + break; + case VT_FLOAT: + if ( *py_var == NULL ) + { + double x; + if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) + INTERR(30160); + + *py_var = newref_t(PyFloat_FromDouble(x)); + break; + } + else + return CIP_IMMUTABLE; + + case VT_REF: + { + if ( *py_var == NULL ) + { + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); + if ( py_cls == NULL ) + return CIP_FAILED; + + // Create a byref object with None value. We populate it later + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); + if ( PyW_GetError() || *py_var == NULL ) + return CIP_FAILED; + } + int t = get_pyidc_cvt_type(py_var->o); + if ( t != PY_ICID_BYREF ) + return CIP_FAILED; + + // Dereference + // (Since we are not using VREF_COPY flag, we can safely const_cast) + idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); + if ( dref_v == NULL ) + return CIP_FAILED; + + // Can we recycle the object? + ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); + if ( new_py_val != NULL ) + { + // Recycle + t = idcvar_to_pyvar(*dref_v, &new_py_val); + + // Success? Nothing more to be done + if ( t == CIP_OK ) + return CIP_OK; + + // Clear it so we don't recycle it + new_py_val = ref_t(); + } + // Try to convert (not recycle) + if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) + return CIP_FAILED; + + // Update the attribute + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); + break; + } + + // Can convert back into a Python object or Python dictionary + // (Depending if py_var will be recycled and it was a dictionary) + case VT_OBJ: + { + // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields + idc_value_t idc_val; + if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk + && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) + { + // Extract the object + *py_var = borref_t((PyObject *) idc_val.pvoid); + return CIP_OK_OPAQUE; + } + ref_t obj; + bool is_dict = false; + + // Need to create a new object? + if ( *py_var == NULL ) + { + // Get skeleton class reference + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); + if ( py_cls == NULL ) + return CIP_FAILED; + + // Call constructor + obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); + if ( PyW_GetError() || obj == NULL ) + return CIP_FAILED; + } + else + { + // Recycle existing variable + obj = *py_var; + if ( PyDict_Check(obj.o) ) + is_dict = true; + } + + // Walk the IDC attributes and store into python + for (const char *attr_name = VarFirstAttr(&idc_var); + attr_name != NULL; + attr_name = VarNextAttr(&idc_var, attr_name) ) + { + // Get the attribute + idc_value_t v; + VarGetAttr(&idc_var, attr_name, &v, true); + + // Convert attribute to a python value (recursively) + ref_t py_attr; + int cvt = idcvar_to_pyvar(v, &py_attr); + if ( cvt <= CIP_IMMUTABLE ) + return CIP_FAILED; + if ( is_dict ) + PyDict_SetItemString(obj.o, attr_name, py_attr.o); + else + PyObject_SetAttrString(obj.o, attr_name, py_attr.o); + } + *py_var = obj; + break; + } + // Unhandled type + default: + *py_var = ref_t(); + return CIP_FAILED; + } + return CIP_OK; +} + +//------------------------------------------------------------------------- +// Converts IDC arguments to Python argument list or just one tuple +// If 'decref' is NULL then 'pargs' will contain one element which is the tuple +bool pyw_convert_idc_args( + const idc_value_t args[], + int nargs, + ref_vec_t &pargs, + bool as_tupple, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + ref_t py_tuple; + + pargs.qclear(); + + if ( as_tupple ) + { + py_tuple = newref_t(PyTuple_New(nargs)); + if ( py_tuple == NULL ) + { + if ( errbuf != 0 && errbufsize > 0 ) + qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); + return false; + } + } + + for ( int i=0; i 0 ) + qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype); + return false; + } + + if ( as_tupple ) + { + // PyTuple_SetItem() steals the reference. + py_obj.incref(); + QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0); + } + else + { + pargs.push_back(py_obj); + } + } + + // Add the tuple to the list of args only now. Doing so earlier will + // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will + // cause 'PyTuple_SetItem()' to fail. + if ( as_tupple ) + pargs.push_back(py_tuple); + + return true; +} + +// + +#endif diff --git a/pywraps/py_dbg.hpp b/pywraps/py_dbg.hpp index 85db5f57..12a675b0 100644 --- a/pywraps/py_dbg.hpp +++ b/pywraps/py_dbg.hpp @@ -1,785 +1,785 @@ -#ifndef __PYDBG__ -#define __PYDBG__ - -// - -//------------------------------------------------------------------------- -static bool dbg_can_query() -{ - // Reject the request only if no debugger is set - // or the debugger cannot be queried while not in suspended state - return dbg != NULL && (dbg->may_disturb() || get_process_state() < DSTATE_NOTASK); -} - -//------------------------------------------------------------------------- -static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - PyObject *py_list = PyList_New(areas.size()); - meminfo_vec_t::const_iterator it, it_end(areas.end()); - Py_ssize_t i = 0; - for ( it=areas.begin(); it!=it_end; ++it, ++i ) - { - const memory_info_t &mi = *it; - // startEA endEA name sclass sbase bitness perm - PyList_SetItem(py_list, i, - Py_BuildValue("("PY_FMT64 PY_FMT64 "ss" PY_FMT64 "II)", - pyul_t(mi.startEA), - pyul_t(mi.endEA), - mi.name.c_str(), - mi.sclass.c_str(), - pyul_t(mi.sbase), - (unsigned int)(mi.bitness), - (unsigned int)mi.perm)); - } - return py_list; -} - -//------------------------------------------------------------------------- -PyObject *py_appcall( - ea_t func_ea, - thid_t tid, - PyObject *py_type, - PyObject *py_fields, - PyObject *arg_list) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !PyList_Check(arg_list) ) - return NULL; - - const char *type = py_type == Py_None ? NULL : PyString_AS_STRING(py_type); - const char *fields = py_fields == Py_None ? NULL : PyString_AS_STRING(py_fields); - - // Convert Python arguments into IDC values - qvector idc_args; - int sn = 0; - Py_ssize_t nargs = PyList_Size(arg_list); - idc_args.resize(nargs); - bool ok = true; - for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); - } - // Convert it - if ( pyvar_to_idcvar(py_item, &idc_args[i], &sn) < CIP_OK ) - { - ok = false; - break; - } - } - - // Set exception message - if ( !ok ) - { - PyErr_SetString( - PyExc_ValueError, - "PyAppCall: Failed to convert Python values to IDC values"); - return NULL; - } - - error_t ret; - idc_value_t idc_result; - Py_BEGIN_ALLOW_THREADS; - - if ( (debug & IDA_DEBUG_APPCALL) != 0 ) - { - msg("input variables:\n" - "----------------\n"); - - qstring s; - for ( Py_ssize_t i=0; iob_refcnt == 1); - if ( (debug & IDA_DEBUG_APPCALL) != 0 ) - { - msg("return var:\n" - "-----------\n"); - qstring s; - VarPrint(&s, &idc_result); - msg("%s\n-----------\n", s.c_str()); - } - py_result.incref(); - return py_result.o; -} -// - -// - -//------------------------------------------------------------------------- -/* -# -def dbg_get_registers(): - """ - This function returns the register definition from the currently loaded debugger. - Basically, it returns an array of structure similar to to idd.hpp / register_info_t - @return: - None if no debugger is loaded - tuple(name, flags, class, dtyp, bit_strings, bit_strings_default_mask) - The bit_strings can be a tuple of strings or None (if the register does not have bit_strings) - """ - pass -# -*/ -static PyObject *dbg_get_registers() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( dbg == NULL ) - Py_RETURN_NONE; - - PyObject *py_list = PyList_New(dbg->registers_size); - - for ( int i=0; iregisters_size; i++ ) - { - register_info_t &ri = dbg->registers[i]; - PyObject *py_bits; - - // Does this register have bit strings? - // (Make sure it does not use custom formats because bit_string would be the format name) - if ( ri.bit_strings != NULL && (ri.flags & REGISTER_CUSTFMT) == 0 ) - { - int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; - py_bits = PyList_New(nbits); - for ( int i=0; i -def dbg_get_thread_sreg_base(tid, sreg_value): - """ - Returns the segment register base value - @param tid: thread id - @param sreg_value: segment register (selector) value - @return: - - The base as an 'ea' - - Or None on failure - """ - pass -# -*/ -static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) - Py_RETURN_NONE; - ea_t answer; - thid_t tid = PyInt_AsLong(py_tid); - int sreg_value = PyInt_AsLong(py_sreg_value); - if ( internal_get_sreg_base(tid, sreg_value, &answer) != 1 ) - Py_RETURN_NONE; - - return Py_BuildValue(PY_FMT64, pyul_t(answer)); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_read_memory(ea, sz): - """ - Reads from the debugee's memory at the specified ea - @return: - - The read buffer (as a string) - - Or None on failure - """ - pass -# -*/ -static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - uint64 ea, sz; - if ( !dbg_can_query() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(py_sz, &sz) ) - Py_RETURN_NONE; - - // Create a Python string - PyObject *ret = PyString_FromStringAndSize(NULL, Py_ssize_t(sz)); - if ( ret == NULL ) - Py_RETURN_NONE; - - // Get the internal buffer - Py_ssize_t len; - char *buf; - PyString_AsStringAndSize(ret, &buf, &len); - - if ( (size_t)read_dbg_memory(ea_t(ea), buf, size_t(sz)) != sz ) - { - // Release the string on failure - Py_DECREF(ret); - // Return None on failure - Py_RETURN_NONE; - } - return ret; -} - -//------------------------------------------------------------------------- -/* -# -def dbg_write_memory(ea, buffer): - """ - Writes a buffer to the debugee's memory - @return: Boolean - """ - pass -# -*/ -static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - uint64 ea; - if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyW_GetNumber(py_ea, &ea) ) - Py_RETURN_NONE; - - size_t sz = PyString_GET_SIZE(py_buf); - void *buf = (void *)PyString_AS_STRING(py_buf); - if ( write_dbg_memory(ea_t(ea), buf, sz) != sz ) - Py_RETURN_FALSE; - Py_RETURN_TRUE; -} - -//------------------------------------------------------------------------- -/* -# -def dbg_get_name(): - """ - This function returns the current debugger's name. - @return: Debugger name or None if no debugger is active - """ - pass -# -*/ -static PyObject *dbg_get_name() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( dbg == NULL ) - Py_RETURN_NONE; - else - return PyString_FromString(dbg->name); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_get_memory_info(): - """ - This function returns the memory configuration of a debugged process. - @return: - None if no debugger is active - tuple(startEA, endEA, name, sclass, sbase, bitness, perm) - """ - pass -# -*/ -static PyObject *dbg_get_memory_info() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !dbg_can_query() ) - Py_RETURN_NONE; - - // Invalidate memory - meminfo_vec_t areas; - Py_BEGIN_ALLOW_THREADS; - invalidate_dbgmem_config(); - invalidate_dbgmem_contents(BADADDR, BADADDR); - - get_dbg_memory_info(&areas); - Py_END_ALLOW_THREADS; - return meminfo_vec_t_to_py(areas); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_can_query(): - """ - This function can be used to check if the debugger can be queried: - - debugger is loaded - - process is suspended - - process is not suspended but can take requests. In this case some requests like - memory read/write, bpt management succeed and register querying will fail. - Check if idaapi.get_process_state() < 0 to tell if the process is suspended - @return: Boolean - """ - pass -# -*/ -static bool dbg_can_query(); -PyObject *py_appcall( - ea_t func_ea, - thid_t tid, - PyObject *py_type, - PyObject *py_fields, - PyObject *arg_list); -// - -// -static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); -// - -// - -//------------------------------------------------------------------------- -/* -# -def get_manual_regions(): - """ - Returns the manual memory regions - @return: list(startEA, endEA, name, sclass, sbase, bitness, perm) - """ - pass -# -*/ -static PyObject *py_get_manual_regions() -{ - meminfo_vec_t areas; - get_manual_regions(&areas); - return meminfo_vec_t_to_py(areas); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_is_loaded(): - """ - Checks if a debugger is loaded - @return: Boolean - """ - pass -# -*/ -static bool dbg_is_loaded() -{ - return dbg != NULL; -} - -//------------------------------------------------------------------------- -/* -# -def refresh_debugger_memory(): - """ - Refreshes the debugger memory - @return: Nothing - """ - pass -# -*/ -static PyObject *refresh_debugger_memory() -{ - invalidate_dbgmem_config(); - invalidate_dbgmem_contents(BADADDR, 0); - - // Ask the debugger to populate debug names - if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) - dbg->stopped_at_debug_event(true); - - // Invalidate the cache - isEnabled(0); - - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; -} - -int idaapi DBG_Callback(void *ud, int notification_code, va_list va); -class DBG_Hooks -{ -public: - virtual ~DBG_Hooks() { unhook(); } - - bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); } - bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); } - /* Hook functions to be overridden in Python */ - virtual void dbg_process_start(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) {} - virtual void dbg_process_exit(pid_t pid, - thid_t tid, - ea_t ea, - int exit_code) {} - virtual void dbg_process_attach(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) {} - virtual void dbg_process_detach(pid_t pid, - thid_t tid, - ea_t ea) {} - virtual void dbg_thread_start(pid_t pid, - thid_t tid, - ea_t ea) {} - virtual void dbg_thread_exit(pid_t pid, - thid_t tid, - ea_t ea, - int exit_code) {} - virtual void dbg_library_load(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) {} - virtual void dbg_library_unload(pid_t pid, - thid_t tid, - ea_t ea, - char *libname) {} - virtual void dbg_information(pid_t pid, - thid_t tid, - ea_t ea, - char *info) {} - virtual int dbg_exception(pid_t pid, - thid_t tid, - ea_t ea, - int code, - bool can_cont, - ea_t exc_ea, - char *info) { return 0; } - virtual void dbg_suspend_process(void) {} - virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; } - virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; } - virtual void dbg_request_error(int failed_command, - int failed_dbg_notification) {} - virtual void dbg_step_into(void) {} - virtual void dbg_step_over(void) {} - virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {} - virtual void dbg_step_until_ret(void) {} -}; - -int idaapi DBG_Callback(void *ud, int notification_code, va_list va) -{ - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - class DBG_Hooks *proxy = (class DBG_Hooks *)ud; - debug_event_t *event; - int code = 0; - - try - { - switch (notification_code) - { - case dbg_process_start: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_start(event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - break; - - case dbg_process_exit: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_exit( - event->pid, - event->tid, - event->ea, - event->exit_code); - break; - - case dbg_process_attach: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_attach( - event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - break; - - case dbg_process_detach: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_detach( - event->pid, - event->tid, - event->ea); - break; - - case dbg_thread_start: - event = va_arg(va, debug_event_t *); - proxy->dbg_thread_start( - event->pid, - event->tid, - event->ea); - break; - - case dbg_thread_exit: - event = va_arg(va, debug_event_t *); - proxy->dbg_thread_exit( - event->pid, - event->tid, - event->ea, - event->exit_code); - break; - - case dbg_library_load: - event = va_arg(va, debug_event_t *); - proxy->dbg_library_load( - event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - break; - - case dbg_library_unload: - event = va_arg(va, debug_event_t *); - proxy->dbg_library_unload( - event->pid, - event->tid, - event->ea, - event->info); - break; - - case dbg_information: - event = va_arg(va, debug_event_t *); - proxy->dbg_information( - event->pid, - event->tid, - event->ea, - event->info); - break; - - case dbg_exception: - { - event = va_arg(va, debug_event_t *); - int *warn = va_arg(va, int *); - *warn = proxy->dbg_exception( - event->pid, - event->tid, - event->ea, - event->exc.code, - event->exc.can_cont, - event->exc.ea, - event->exc.info); - break; - } - - case dbg_suspend_process: - proxy->dbg_suspend_process(); - break; - - case dbg_bpt: - { - thid_t tid = va_arg(va, thid_t); - ea_t breakpoint_ea = va_arg(va, ea_t); - int *warn = va_arg(va, int *); - *warn = proxy->dbg_bpt(tid, breakpoint_ea); - break; - } - - case dbg_trace: - { - thid_t tid = va_arg(va, thid_t); - ea_t ip = va_arg(va, ea_t); - code = proxy->dbg_trace(tid, ip); - break; - } - - case dbg_request_error: - { - int failed_command = (int)va_argi(va, ui_notification_t); - int failed_dbg_notification = (int)va_argi(va, dbg_notification_t); - proxy->dbg_request_error(failed_command, failed_dbg_notification); - break; - } - - case dbg_step_into: - proxy->dbg_step_into(); - break; - - case dbg_step_over: - proxy->dbg_step_over(); - break; - - case dbg_run_to: - event = va_arg(va, debug_event_t *); - proxy->dbg_run_to( - event->pid, - event->tid, - event->ea); - break; - - case dbg_step_until_ret: - proxy->dbg_step_until_ret(); - break; - } - } - catch (Swig::DirectorException &e) - { - msg("Exception in DBG Hook function: %s\n", e.getMessage()); - if (PyErr_Occurred()) - PyErr_Print(); - } - return code; -} - -//------------------------------------------------------------------------ -/* -# -def py_list_bptgrps(): - """ - Returns list of breakpoint group names - @return: A list of strings or None on failure - """ - pass -# -*/ -static PyObject *py_list_bptgrps() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstrvec_t args; - if ( list_bptgrps(&args) == 0 ) - Py_RETURN_NONE; - return qstrvec2pylist(args); -} - -//------------------------------------------------------------------------ -/* -# -def move_bpt_to_grp(): - """ - Sets new group for the breakpoint - """ - pass -# -*/ -static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - set_bpt_group(*bpt, grp_name); -} - -/* -# -def internal_get_sreg_base(): - """ - Get the sreg base, for the given thread. - - @return: The sreg base, or BADADDR on failure. - """ - pass -# -*/ -static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ea_t answer; - return internal_get_sreg_base(tid, sreg_value, &answer) < 1 - ? BADADDR - : answer; -} - -// -#endif +#ifndef __PYDBG__ +#define __PYDBG__ + +// + +//------------------------------------------------------------------------- +static bool dbg_can_query() +{ + // Reject the request only if no debugger is set + // or the debugger cannot be queried while not in suspended state + return dbg != NULL && (dbg->may_disturb() || get_process_state() < DSTATE_NOTASK); +} + +//------------------------------------------------------------------------- +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + PyObject *py_list = PyList_New(areas.size()); + meminfo_vec_t::const_iterator it, it_end(areas.end()); + Py_ssize_t i = 0; + for ( it=areas.begin(); it!=it_end; ++it, ++i ) + { + const memory_info_t &mi = *it; + // startEA endEA name sclass sbase bitness perm + PyList_SetItem(py_list, i, + Py_BuildValue("("PY_FMT64 PY_FMT64 "ss" PY_FMT64 "II)", + pyul_t(mi.startEA), + pyul_t(mi.endEA), + mi.name.c_str(), + mi.sclass.c_str(), + pyul_t(mi.sbase), + (unsigned int)(mi.bitness), + (unsigned int)mi.perm)); + } + return py_list; +} + +//------------------------------------------------------------------------- +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !PyList_Check(arg_list) ) + return NULL; + + const char *type = py_type == Py_None ? NULL : PyString_AS_STRING(py_type); + const char *fields = py_fields == Py_None ? NULL : PyString_AS_STRING(py_fields); + + // Convert Python arguments into IDC values + qvector idc_args; + int sn = 0; + Py_ssize_t nargs = PyList_Size(arg_list); + idc_args.resize(nargs); + bool ok = true; + for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); + } + // Convert it + if ( pyvar_to_idcvar(py_item, &idc_args[i], &sn) < CIP_OK ) + { + ok = false; + break; + } + } + + // Set exception message + if ( !ok ) + { + PyErr_SetString( + PyExc_ValueError, + "PyAppCall: Failed to convert Python values to IDC values"); + return NULL; + } + + error_t ret; + idc_value_t idc_result; + Py_BEGIN_ALLOW_THREADS; + + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) + { + msg("input variables:\n" + "----------------\n"); + + qstring s; + for ( Py_ssize_t i=0; iob_refcnt == 1); + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) + { + msg("return var:\n" + "-----------\n"); + qstring s; + VarPrint(&s, &idc_result); + msg("%s\n-----------\n", s.c_str()); + } + py_result.incref(); + return py_result.o; +} +// + +// + +//------------------------------------------------------------------------- +/* +# +def dbg_get_registers(): + """ + This function returns the register definition from the currently loaded debugger. + Basically, it returns an array of structure similar to to idd.hpp / register_info_t + @return: + None if no debugger is loaded + tuple(name, flags, class, dtyp, bit_strings, default_bit_strings_mask) + The bit_strings can be a tuple of strings or None (if the register does not have bit_strings) + """ + pass +# +*/ +static PyObject *dbg_get_registers() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( dbg == NULL ) + Py_RETURN_NONE; + + PyObject *py_list = PyList_New(dbg->registers_size); + + for ( int i=0; iregisters_size; i++ ) + { + register_info_t &ri = dbg->registers(i); + PyObject *py_bits; + + // Does this register have bit strings? + // (Make sure it does not use custom formats because bit_string would be the format name) + if ( ri.bit_strings != NULL && (ri.flags & REGISTER_CUSTFMT) == 0 ) + { + int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; + py_bits = PyList_New(nbits); + for ( int i=0; i +def dbg_get_thread_sreg_base(tid, sreg_value): + """ + Returns the segment register base value + @param tid: thread id + @param sreg_value: segment register (selector) value + @return: + - The base as an 'ea' + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) + Py_RETURN_NONE; + ea_t answer; + thid_t tid = PyInt_AsLong(py_tid); + int sreg_value = PyInt_AsLong(py_sreg_value); + if ( internal_get_sreg_base(tid, sreg_value, &answer) != 1 ) + Py_RETURN_NONE; + + return Py_BuildValue(PY_FMT64, pyul_t(answer)); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_read_memory(ea, sz): + """ + Reads from the debugee's memory at the specified ea + @return: + - The read buffer (as a string) + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + uint64 ea, sz; + if ( !dbg_can_query() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(py_sz, &sz) ) + Py_RETURN_NONE; + + // Create a Python string + PyObject *ret = PyString_FromStringAndSize(NULL, Py_ssize_t(sz)); + if ( ret == NULL ) + Py_RETURN_NONE; + + // Get the internal buffer + Py_ssize_t len; + char *buf; + PyString_AsStringAndSize(ret, &buf, &len); + + if ( (size_t)read_dbg_memory(ea_t(ea), buf, size_t(sz)) != sz ) + { + // Release the string on failure + Py_DECREF(ret); + // Return None on failure + Py_RETURN_NONE; + } + return ret; +} + +//------------------------------------------------------------------------- +/* +# +def dbg_write_memory(ea, buffer): + """ + Writes a buffer to the debugee's memory + @return: Boolean + """ + pass +# +*/ +static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + uint64 ea; + if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyW_GetNumber(py_ea, &ea) ) + Py_RETURN_NONE; + + size_t sz = PyString_GET_SIZE(py_buf); + void *buf = (void *)PyString_AS_STRING(py_buf); + if ( write_dbg_memory(ea_t(ea), buf, sz) != sz ) + Py_RETURN_FALSE; + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +/* +# +def dbg_get_name(): + """ + This function returns the current debugger's name. + @return: Debugger name or None if no debugger is active + """ + pass +# +*/ +static PyObject *dbg_get_name() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( dbg == NULL ) + Py_RETURN_NONE; + else + return PyString_FromString(dbg->name); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_get_memory_info(): + """ + This function returns the memory configuration of a debugged process. + @return: + None if no debugger is active + tuple(startEA, endEA, name, sclass, sbase, bitness, perm) + """ + pass +# +*/ +static PyObject *dbg_get_memory_info() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !dbg_can_query() ) + Py_RETURN_NONE; + + // Invalidate memory + meminfo_vec_t areas; + Py_BEGIN_ALLOW_THREADS; + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, BADADDR); + + get_dbg_memory_info(&areas); + Py_END_ALLOW_THREADS; + return meminfo_vec_t_to_py(areas); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_can_query(): + """ + This function can be used to check if the debugger can be queried: + - debugger is loaded + - process is suspended + - process is not suspended but can take requests. In this case some requests like + memory read/write, bpt management succeed and register querying will fail. + Check if idaapi.get_process_state() < 0 to tell if the process is suspended + @return: Boolean + """ + pass +# +*/ +static bool dbg_can_query(); +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list); +// + +// +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); +// + +// + +//------------------------------------------------------------------------- +/* +# +def get_manual_regions(): + """ + Returns the manual memory regions + @return: list(startEA, endEA, name, sclass, sbase, bitness, perm) + """ + pass +# +*/ +static PyObject *py_get_manual_regions() +{ + meminfo_vec_t areas; + get_manual_regions(&areas); + return meminfo_vec_t_to_py(areas); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_is_loaded(): + """ + Checks if a debugger is loaded + @return: Boolean + """ + pass +# +*/ +static bool dbg_is_loaded() +{ + return dbg != NULL; +} + +//------------------------------------------------------------------------- +/* +# +def refresh_debugger_memory(): + """ + Refreshes the debugger memory + @return: Nothing + """ + pass +# +*/ +static PyObject *refresh_debugger_memory() +{ + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, 0); + + // Ask the debugger to populate debug names + if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) + dbg->stopped_at_debug_event(true); + + // Invalidate the cache + isEnabled(0); + + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; +} + +int idaapi DBG_Callback(void *ud, int notification_code, va_list va); +class DBG_Hooks +{ +public: + virtual ~DBG_Hooks() { unhook(); } + + bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); } + bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); } + /* Hook functions to be overridden in Python */ + virtual void dbg_process_start(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) {} + virtual void dbg_process_exit(pid_t pid, + thid_t tid, + ea_t ea, + int exit_code) {} + virtual void dbg_process_attach(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) {} + virtual void dbg_process_detach(pid_t pid, + thid_t tid, + ea_t ea) {} + virtual void dbg_thread_start(pid_t pid, + thid_t tid, + ea_t ea) {} + virtual void dbg_thread_exit(pid_t pid, + thid_t tid, + ea_t ea, + int exit_code) {} + virtual void dbg_library_load(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) {} + virtual void dbg_library_unload(pid_t pid, + thid_t tid, + ea_t ea, + char *libname) {} + virtual void dbg_information(pid_t pid, + thid_t tid, + ea_t ea, + char *info) {} + virtual int dbg_exception(pid_t pid, + thid_t tid, + ea_t ea, + int code, + bool can_cont, + ea_t exc_ea, + char *info) { return 0; } + virtual void dbg_suspend_process(void) {} + virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; } + virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; } + virtual void dbg_request_error(int failed_command, + int failed_dbg_notification) {} + virtual void dbg_step_into(void) {} + virtual void dbg_step_over(void) {} + virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {} + virtual void dbg_step_until_ret(void) {} +}; + +int idaapi DBG_Callback(void *ud, int notification_code, va_list va) +{ + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + class DBG_Hooks *proxy = (class DBG_Hooks *)ud; + debug_event_t *event; + int code = 0; + + try + { + switch (notification_code) + { + case dbg_process_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_start(event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + break; + + case dbg_process_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_exit( + event->pid, + event->tid, + event->ea, + event->exit_code); + break; + + case dbg_process_attach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_attach( + event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + break; + + case dbg_process_detach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_detach( + event->pid, + event->tid, + event->ea); + break; + + case dbg_thread_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_start( + event->pid, + event->tid, + event->ea); + break; + + case dbg_thread_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_exit( + event->pid, + event->tid, + event->ea, + event->exit_code); + break; + + case dbg_library_load: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_load( + event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + break; + + case dbg_library_unload: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_unload( + event->pid, + event->tid, + event->ea, + event->info); + break; + + case dbg_information: + event = va_arg(va, debug_event_t *); + proxy->dbg_information( + event->pid, + event->tid, + event->ea, + event->info); + break; + + case dbg_exception: + { + event = va_arg(va, debug_event_t *); + int *warn = va_arg(va, int *); + *warn = proxy->dbg_exception( + event->pid, + event->tid, + event->ea, + event->exc.code, + event->exc.can_cont, + event->exc.ea, + event->exc.info); + break; + } + + case dbg_suspend_process: + proxy->dbg_suspend_process(); + break; + + case dbg_bpt: + { + thid_t tid = va_arg(va, thid_t); + ea_t breakpoint_ea = va_arg(va, ea_t); + int *warn = va_arg(va, int *); + *warn = proxy->dbg_bpt(tid, breakpoint_ea); + break; + } + + case dbg_trace: + { + thid_t tid = va_arg(va, thid_t); + ea_t ip = va_arg(va, ea_t); + code = proxy->dbg_trace(tid, ip); + break; + } + + case dbg_request_error: + { + int failed_command = (int)va_argi(va, ui_notification_t); + int failed_dbg_notification = (int)va_argi(va, dbg_notification_t); + proxy->dbg_request_error(failed_command, failed_dbg_notification); + break; + } + + case dbg_step_into: + proxy->dbg_step_into(); + break; + + case dbg_step_over: + proxy->dbg_step_over(); + break; + + case dbg_run_to: + event = va_arg(va, debug_event_t *); + proxy->dbg_run_to( + event->pid, + event->tid, + event->ea); + break; + + case dbg_step_until_ret: + proxy->dbg_step_until_ret(); + break; + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in DBG Hook function: %s\n", e.getMessage()); + if (PyErr_Occurred()) + PyErr_Print(); + } + return code; +} + +//------------------------------------------------------------------------ +/* +# +def py_list_bptgrps(): + """ + Returns list of breakpoint group names + @return: A list of strings or None on failure + """ + pass +# +*/ +static PyObject *py_list_bptgrps() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstrvec_t args; + if ( list_bptgrps(&args) == 0 ) + Py_RETURN_NONE; + return qstrvec2pylist(args); +} + +//------------------------------------------------------------------------ +/* +# +def move_bpt_to_grp(): + """ + Sets new group for the breakpoint + """ + pass +# +*/ +static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + set_bpt_group(*bpt, grp_name); +} + +/* +# +def internal_get_sreg_base(): + """ + Get the sreg base, for the given thread. + + @return: The sreg base, or BADADDR on failure. + """ + pass +# +*/ +static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ea_t answer; + return internal_get_sreg_base(tid, sreg_value, &answer) < 1 + ? BADADDR + : answer; +} + +// +#endif diff --git a/pywraps/py_diskio.hpp b/pywraps/py_diskio.hpp index a20da3d6..8c7b937a 100644 --- a/pywraps/py_diskio.hpp +++ b/pywraps/py_diskio.hpp @@ -1,64 +1,64 @@ -#ifndef __PY_IDA_DISKIO__ -#define __PY_IDA_DISKIO__ - -// -//-------------------------------------------------------------------------- -int idaapi py_enumerate_files_cb(const char *file, void *ud) -{ - // No need to 'PYW_GIL_GET' here, as this is called synchronously - // and from the same thread as the one that executes - // 'py_enumerate_files'. - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_file(PyString_FromString(file)); - newref_t py_ret( - PyObject_CallFunctionObjArgs( - (PyObject *)ud, - py_file.o, - NULL)); - return (py_ret == NULL || !PyNumber_Check(py_ret.o)) ? 1 /* stop enum on failure */ : PyInt_AsLong(py_ret.o); -} -// - -// -//-------------------------------------------------------------------------- -/* -# -def enumerate_files(path, fname, callback): - """ - Enumerate files in the specified directory while the callback returns 0. - @param path: directory to enumerate files in - @param fname: mask of file names to enumerate - @param callback: a callable object that takes the filename as - its first argument and it returns 0 to continue - enumeration or non-zero to stop enumeration. - @return: - None in case of script errors - tuple(code, fname) : If the callback returns non-zero - """ - pass -# -*/ -PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - do - { - if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) - break; - - const char *_path = PyString_AsString(path); - const char *_fname = PyString_AsString(fname); - if ( _path == NULL || _fname == NULL ) - break; - - char answer[MAXSTR]; - answer[0] = '\0'; - int r = enumerate_files(answer, sizeof(answer), _path, _fname, py_enumerate_files_cb, callback); - return Py_BuildValue("(is)", r, answer); - } while ( false ); - Py_RETURN_NONE; -} -// - -#endif +#ifndef __PY_IDA_DISKIO__ +#define __PY_IDA_DISKIO__ + +// +//-------------------------------------------------------------------------- +int idaapi py_enumerate_files_cb(const char *file, void *ud) +{ + // No need to 'PYW_GIL_GET' here, as this is called synchronously + // and from the same thread as the one that executes + // 'py_enumerate_files'. + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_file(PyString_FromString(file)); + newref_t py_ret( + PyObject_CallFunctionObjArgs( + (PyObject *)ud, + py_file.o, + NULL)); + return (py_ret == NULL || !PyNumber_Check(py_ret.o)) ? 1 /* stop enum on failure */ : PyInt_AsLong(py_ret.o); +} +// + +// +//-------------------------------------------------------------------------- +/* +# +def enumerate_files(path, fname, callback): + """ + Enumerate files in the specified directory while the callback returns 0. + @param path: directory to enumerate files in + @param fname: mask of file names to enumerate + @param callback: a callable object that takes the filename as + its first argument and it returns 0 to continue + enumeration or non-zero to stop enumeration. + @return: + None in case of script errors + tuple(code, fname) : If the callback returns non-zero + """ + pass +# +*/ +PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + do + { + if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) + break; + + const char *_path = PyString_AsString(path); + const char *_fname = PyString_AsString(fname); + if ( _path == NULL || _fname == NULL ) + break; + + char answer[MAXSTR]; + answer[0] = '\0'; + int r = enumerate_files(answer, sizeof(answer), _path, _fname, py_enumerate_files_cb, callback); + return Py_BuildValue("(is)", r, answer); + } while ( false ); + Py_RETURN_NONE; +} +// + +#endif diff --git a/pywraps/py_diskio.py b/pywraps/py_diskio.py index 50ff54ca..9434cb74 100644 --- a/pywraps/py_diskio.py +++ b/pywraps/py_diskio.py @@ -1,5 +1,5 @@ -# -def enumerate_system_files(subdir, fname, callback): - """Similar to enumerate_files() however it searches inside IDA directory or its subdirectories""" - return enumerate_files(idadir(subdir), fname, callback) -# +# +def enumerate_system_files(subdir, fname, callback): + """Similar to enumerate_files() however it searches inside IDA directory or its subdirectories""" + return enumerate_files(idadir(subdir), fname, callback) +# diff --git a/pywraps/py_expr.hpp b/pywraps/py_expr.hpp index 06976d54..b6f18aea 100644 --- a/pywraps/py_expr.hpp +++ b/pywraps/py_expr.hpp @@ -1,81 +1,81 @@ -#ifndef __PY_EXPR__ -#define __PY_EXPR__ - -// -struct py_idcfunc_ctx_t -{ - PyObject *py_func; - qstring name; - int nargs; - py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_INCREF(py_func); - } - ~py_idcfunc_ctx_t() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_DECREF(py_func); - } -}; - -//--------------------------------------------------------------------------- -static error_t py_call_idc_func( - void *_ctx, - idc_value_t *argv, - idc_value_t *r) -{ - // Convert IDC arguments to Python list - py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; - int cvt; - char errbuf[MAXSTR]; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_vec_t pargs; - if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, true, errbuf, sizeof(errbuf)) ) - { - // Error during conversion? Create an IDC exception - return PyW_CreateIdcException(r, errbuf); - } - - // Call the Python function - newref_t py_result(PyObject_CallObject( - ctx->py_func, - pargs.empty() ? NULL : pargs[0].o)); - - error_t err; - if ( PyW_GetError(errbuf, sizeof(errbuf)) ) - { - err = PyW_CreateIdcException(r, errbuf); - } - else - { - // Convert the result to IDC - r->clear(); - cvt = pyvar_to_idcvar(py_result, r); - if ( cvt < CIP_OK ) - err = PyW_CreateIdcException(r, "ERROR: bad return value"); - else - err = eOk; - } - - return err; -} - -// - -// - -//--------------------------------------------------------------------------- -static size_t py_get_call_idc_func() -{ - return (size_t)py_call_idc_func; -} - -//--------------------------------------------------------------------------- -// Internal function: -// - capture the python callable -// - return a C context as a numeric value +#ifndef __PY_EXPR__ +#define __PY_EXPR__ + +// +struct py_idcfunc_ctx_t +{ + PyObject *py_func; + qstring name; + int nargs; + py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_INCREF(py_func); + } + ~py_idcfunc_ctx_t() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_DECREF(py_func); + } +}; + +//--------------------------------------------------------------------------- +static error_t py_call_idc_func( + void *_ctx, + idc_value_t *argv, + idc_value_t *r) +{ + // Convert IDC arguments to Python list + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; + int cvt; + char errbuf[MAXSTR]; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_vec_t pargs; + if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, true, errbuf, sizeof(errbuf)) ) + { + // Error during conversion? Create an IDC exception + return PyW_CreateIdcException(r, errbuf); + } + + // Call the Python function + newref_t py_result(PyObject_CallObject( + ctx->py_func, + pargs.empty() ? NULL : pargs[0].o)); + + error_t err; + if ( PyW_GetError(errbuf, sizeof(errbuf)) ) + { + err = PyW_CreateIdcException(r, errbuf); + } + else + { + // Convert the result to IDC + r->clear(); + cvt = pyvar_to_idcvar(py_result, r); + if ( cvt < CIP_OK ) + err = PyW_CreateIdcException(r, "ERROR: bad return value"); + else + err = eOk; + } + + return err; +} + +// + +// + +//--------------------------------------------------------------------------- +static size_t py_get_call_idc_func() +{ + return (size_t)py_call_idc_func; +} + +//--------------------------------------------------------------------------- +// Internal function: +// - capture the python callable +// - return a C context as a numeric value static size_t pyw_register_idc_func( const char *name, const char *args, @@ -83,10 +83,10 @@ static size_t pyw_register_idc_func( { return (size_t)new py_idcfunc_ctx_t(py_fp, name, strlen(args)); } - -//--------------------------------------------------------------------------- -// Internal function: -// - free the C context + +//--------------------------------------------------------------------------- +// Internal function: +// - free the C context static bool pyw_unregister_idc_func(size_t ctxptr) { // Unregister the function @@ -98,18 +98,18 @@ static bool pyw_unregister_idc_func(size_t ctxptr) return ok; } - -//--------------------------------------------------------------------------- -static bool py_set_idc_func_ex( - const char *name, - size_t fp_ptr, - const char *args, - int flags) -{ - return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); -} - -//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +static bool py_set_idc_func_ex( + const char *name, + size_t fp_ptr, + const char *args, + int flags) +{ + return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); +} + +//--------------------------------------------------------------------------- // Compile* functions return false when error so the return // value must be negated for the error string to be returned bool CompileEx_wrap( @@ -119,12 +119,12 @@ bool CompileEx_wrap( { return !CompileEx(file, del_macros, errbuf, errbufsize); } - + bool Compile_wrap(const char *file, char *errbuf, size_t errbufsize) { return !Compile(file, errbuf, errbufsize); } - + bool calcexpr_wrap( ea_t where, const char *line, @@ -133,7 +133,7 @@ bool calcexpr_wrap( { return !calcexpr(where, line, rv, errbuf, errbufsize); } - + bool calc_idc_expr_wrap( ea_t where, const char *line, @@ -142,11 +142,11 @@ bool calc_idc_expr_wrap( { return !calc_idc_expr(where, line, rv, errbuf, errbufsize); } - + bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) { return !CompileLineEx(line, errbuf, errbufsize); } - -// -#endif + +// +#endif diff --git a/pywraps/py_expr.py b/pywraps/py_expr.py index 84f83734..e14aec7a 100644 --- a/pywraps/py_expr.py +++ b/pywraps/py_expr.py @@ -1,169 +1,169 @@ -# -------------------------------------------------------------------------- -import os -import sys - -import idaapi -import _idaapi -from sys import getrefcount -import gc - -try: - import pywraps - pywraps_there = True - _idaapi.pyw_register_idc_func = pywraps.pyw_register_idc_func - _idaapi.pyw_unregister_idc_func = pywraps.pyw_unregister_idc_func - _idaapi.py_get_call_idc_func = pywraps.py_get_call_idc_func - _idaapi.py_set_idc_func_ex = pywraps.py_set_idc_func_ex - - -except Exception as e: - pywraps_there = False - print("exception: %s" % str(e)) - - -print("Using PyWraps: %s" % pywraps_there) - -# -------------------------------------------------------------------------- -# -try: - import types - import ctypes - # Callback for IDC func callback (On Windows, we use stdcall) - # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); - _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) - - # A trampoline function that is called from idcfunc_t that will - # call the Python callback with the argv and r properly serialized to python - call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) -except: - def call_idc_func__(*args): - warning("IDC extensions need ctypes library in order to work") - return 0 - try: - _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) - except: - _IDCFUNC_CB_T = None - - -# -------------------------------------------------------------------------- -EXTFUN_BASE = 0x0001 -"""requires open database""" -EXTFUN_NORET = 0x0002 -"""does not return. the interpreter may clean up its state before calling it.""" -EXTFUN_SAFE = 0x0004 -"""thread safe function. may be called""" - -# -------------------------------------------------------------------------- -class _IdcFunction(object): - """ - Internal class that calls pyw_call_idc_func() with a context - """ - def __init__(self, ctxptr): - self.ctxptr = ctxptr - # Take a reference to the ctypes callback - # (note: this will create a circular reference) - self.cb = _IDCFUNC_CB_T(self) - - fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) - - def __call__(self, args, res): - return call_idc_func__(self.ctxptr, args, res) - - -# -------------------------------------------------------------------------- -# Dictionary to remember IDC function names along with the context pointer -# retrieved by using the internal pyw_register_idc_func() -__IDC_FUNC_CTXS = {} - -# -------------------------------------------------------------------------- -def set_idc_func_ex(name, fp=None, args=(), flags=0): - """ - Extends the IDC language by exposing a new IDC function that is backed up by a Python function - This function also unregisters the IDC function if 'fp' was passed as None - - @param name: IDC function name to expose - @param fp: Python callable that will receive the arguments and return a tuple. - If this argument is None then the IDC function is unregistered - @param args: Arguments. A tuple of idaapi.VT_XXX constants - @param flags: IDC function flags. A combination of EXTFUN_XXX constants - - @return: Boolean. - """ - global __IDC_FUNC_CTXS - - # Get the context - f = __IDC_FUNC_CTXS.get(name, None) - - # Unregistering? - if fp is None: - # Not registered? - if f is None: - return False - - # Break circular reference - del f.cb - - # Delete the name from the dictionary - del __IDC_FUNC_CTXS[name] - - # Delete the context and unregister the function - return _idaapi.pyw_unregister_idc_func(f.ctxptr) - - # Registering a function that is already registered? - if f is not None: - # Unregister it first - set_idc_func_ex(name, None) - - # Convert the tupple argument info to a string - args = "".join([chr(x) for x in args]) - - # Create a context - ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) - if ctxptr == 0: - return False - - # Bind the context with the IdcFunc object - f = _IdcFunction(ctxptr) - - # Remember the Python context - __IDC_FUNC_CTXS[name] = f - - # Register IDC function with a callback - return _idaapi.py_set_idc_func_ex( - name, - f.fp_ptr, - args, - flags) - -# - -# -------------------------------------------------------------------------- -def test1(): - global MY_IDC_FUNC - try: - # Already registered? - MY_IDC_FUNC - # Unregister - print("Unregistering function") - set_idc_func_ex(MY_IDC_FUNC) - except: - MY_IDC_FUNC = "pysum" - - ok = set_idc_func_ex(MY_IDC_FUNC, my_idc_sum, (idaapi.VT_LONG, idaapi.VT_LONG), 0) - if not ok: - del MY_IDC_FUNC - - -# - -# -------------------------------------------------------------------------- -# -def py_power(n, e): - return n ** e - -ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) -if ok: - print("Now the pow() will be present IDC!") -else: - print("Failed to register pow() IDC function") -# +# -------------------------------------------------------------------------- +import os +import sys + +import idaapi +import _idaapi +from sys import getrefcount +import gc + +try: + import pywraps + pywraps_there = True + _idaapi.pyw_register_idc_func = pywraps.pyw_register_idc_func + _idaapi.pyw_unregister_idc_func = pywraps.pyw_unregister_idc_func + _idaapi.py_get_call_idc_func = pywraps.py_get_call_idc_func + _idaapi.py_set_idc_func_ex = pywraps.py_set_idc_func_ex + + +except Exception as e: + pywraps_there = False + print("exception: %s" % str(e)) + + +print("Using PyWraps: %s" % pywraps_there) + +# -------------------------------------------------------------------------- +# +try: + import types + import ctypes + # Callback for IDC func callback (On Windows, we use stdcall) + # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); + _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) + + # A trampoline function that is called from idcfunc_t that will + # call the Python callback with the argv and r properly serialized to python + call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) +except: + def call_idc_func__(*args): + warning("IDC extensions need ctypes library in order to work") + return 0 + try: + _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) + except: + _IDCFUNC_CB_T = None + + +# -------------------------------------------------------------------------- +EXTFUN_BASE = 0x0001 +"""requires open database""" +EXTFUN_NORET = 0x0002 +"""does not return. the interpreter may clean up its state before calling it.""" +EXTFUN_SAFE = 0x0004 +"""thread safe function. may be called""" + +# -------------------------------------------------------------------------- +class _IdcFunction(object): + """ + Internal class that calls pyw_call_idc_func() with a context + """ + def __init__(self, ctxptr): + self.ctxptr = ctxptr + # Take a reference to the ctypes callback + # (note: this will create a circular reference) + self.cb = _IDCFUNC_CB_T(self) + + fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) + + def __call__(self, args, res): + return call_idc_func__(self.ctxptr, args, res) + + +# -------------------------------------------------------------------------- +# Dictionary to remember IDC function names along with the context pointer +# retrieved by using the internal pyw_register_idc_func() +__IDC_FUNC_CTXS = {} + +# -------------------------------------------------------------------------- +def set_idc_func_ex(name, fp=None, args=(), flags=0): + """ + Extends the IDC language by exposing a new IDC function that is backed up by a Python function + This function also unregisters the IDC function if 'fp' was passed as None + + @param name: IDC function name to expose + @param fp: Python callable that will receive the arguments and return a tuple. + If this argument is None then the IDC function is unregistered + @param args: Arguments. A tuple of idaapi.VT_XXX constants + @param flags: IDC function flags. A combination of EXTFUN_XXX constants + + @return: Boolean. + """ + global __IDC_FUNC_CTXS + + # Get the context + f = __IDC_FUNC_CTXS.get(name, None) + + # Unregistering? + if fp is None: + # Not registered? + if f is None: + return False + + # Break circular reference + del f.cb + + # Delete the name from the dictionary + del __IDC_FUNC_CTXS[name] + + # Delete the context and unregister the function + return _idaapi.pyw_unregister_idc_func(f.ctxptr) + + # Registering a function that is already registered? + if f is not None: + # Unregister it first + set_idc_func_ex(name, None) + + # Convert the tupple argument info to a string + args = "".join([chr(x) for x in args]) + + # Create a context + ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) + if ctxptr == 0: + return False + + # Bind the context with the IdcFunc object + f = _IdcFunction(ctxptr) + + # Remember the Python context + __IDC_FUNC_CTXS[name] = f + + # Register IDC function with a callback + return _idaapi.py_set_idc_func_ex( + name, + f.fp_ptr, + args, + flags) + +# + +# -------------------------------------------------------------------------- +def test1(): + global MY_IDC_FUNC + try: + # Already registered? + MY_IDC_FUNC + # Unregister + print("Unregistering function") + set_idc_func_ex(MY_IDC_FUNC) + except: + MY_IDC_FUNC = "pysum" + + ok = set_idc_func_ex(MY_IDC_FUNC, my_idc_sum, (idaapi.VT_LONG, idaapi.VT_LONG), 0) + if not ok: + del MY_IDC_FUNC + + +# + +# -------------------------------------------------------------------------- +# +def py_power(n, e): + return n ** e + +ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) +if ok: + print("Now the pow() will be present IDC!") +else: + print("Failed to register pow() IDC function") +# diff --git a/pywraps/py_gdl.py b/pywraps/py_gdl.py index 73712063..0de5dcb9 100644 --- a/pywraps/py_gdl.py +++ b/pywraps/py_gdl.py @@ -1,88 +1,88 @@ -# -# ----------------------------------------------------------------------- -class BasicBlock(object): - """Basic block class. It is returned by the Flowchart class""" - def __init__(self, id, bb, fc): - self._fc = fc - - self.id = id - """Basic block ID""" - - self.startEA = bb.startEA - """startEA of basic block""" - - self.endEA = bb.endEA - """endEA of basic block""" - - self.type = self._fc._q.calc_block_type(self.id) - """Block type (check fc_block_type_t enum)""" - - - def preds(self): - """ - Iterates the predecessors list - """ - q = self._fc._q - for i in xrange(0, self._fc._q.npred(self.id)): - yield self._fc[q.pred(self.id, i)] - - - def succs(self): - """ - Iterates the successors list - """ - q = self._fc._q - for i in xrange(0, q.nsucc(self.id)): - yield self._fc[q.succ(self.id, i)] - -# ----------------------------------------------------------------------- -class FlowChart(object): - """ - Flowchart class used to determine basic blocks. - Check ex_gdl_qflow_chart.py for sample usage. - """ - def __init__(self, f=None, bounds=None, flags=0): - """ - Constructor - @param f: A func_t type, use get_func(ea) to get a reference - @param bounds: A tuple of the form (start, end). Used if "f" is None - @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS - """ - if (f is None) and (bounds is None or type(bounds) != types.TupleType): - raise Exception("Please specifiy either a function or start/end pair") - - if bounds is None: - bounds = (BADADDR, BADADDR) - - # Create the flowchart - self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) - - size = property(lambda self: self._q.size()) - """Number of blocks in the flow chart""" - - - def refresh(): - """Refreshes the flow chart""" - self._q.refresh() - - - def _getitem(self, index): - return BasicBlock(index, self._q[index], self) - - - def __iter__(self): - return (self._getitem(index) for index in xrange(0, self.size)) - - - def __getitem__(self, index): - """ - Returns a basic block - - @return: BasicBlock - """ - if index >= self.size: - raise KeyError - else: - return self._getitem(index) - -# +# +# ----------------------------------------------------------------------- +class BasicBlock(object): + """Basic block class. It is returned by the Flowchart class""" + def __init__(self, id, bb, fc): + self._fc = fc + + self.id = id + """Basic block ID""" + + self.startEA = bb.startEA + """startEA of basic block""" + + self.endEA = bb.endEA + """endEA of basic block""" + + self.type = self._fc._q.calc_block_type(self.id) + """Block type (check fc_block_type_t enum)""" + + + def preds(self): + """ + Iterates the predecessors list + """ + q = self._fc._q + for i in xrange(0, self._fc._q.npred(self.id)): + yield self._fc[q.pred(self.id, i)] + + + def succs(self): + """ + Iterates the successors list + """ + q = self._fc._q + for i in xrange(0, q.nsucc(self.id)): + yield self._fc[q.succ(self.id, i)] + +# ----------------------------------------------------------------------- +class FlowChart(object): + """ + Flowchart class used to determine basic blocks. + Check ex_gdl_qflow_chart.py for sample usage. + """ + def __init__(self, f=None, bounds=None, flags=0): + """ + Constructor + @param f: A func_t type, use get_func(ea) to get a reference + @param bounds: A tuple of the form (start, end). Used if "f" is None + @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS + """ + if (f is None) and (bounds is None or type(bounds) != types.TupleType): + raise Exception("Please specifiy either a function or start/end pair") + + if bounds is None: + bounds = (BADADDR, BADADDR) + + # Create the flowchart + self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) + + size = property(lambda self: self._q.size()) + """Number of blocks in the flow chart""" + + + def refresh(): + """Refreshes the flow chart""" + self._q.refresh() + + + def _getitem(self, index): + return BasicBlock(index, self._q[index], self) + + + def __iter__(self): + return (self._getitem(index) for index in xrange(0, self.size)) + + + def __getitem__(self, index): + """ + Returns a basic block + + @return: BasicBlock + """ + if index >= self.size: + raise KeyError + else: + return self._getitem(index) + +# diff --git a/pywraps/py_graph.hpp b/pywraps/py_graph.hpp index bacb7410..89d3d9bc 100644 --- a/pywraps/py_graph.hpp +++ b/pywraps/py_graph.hpp @@ -1,816 +1,816 @@ -#ifndef __PY_GRAPH__ -#define __PY_GRAPH__ - -// -class py_graph_t : public py_customidamemo_t -{ - typedef py_customidamemo_t inherited; - -protected: - - virtual void node_info_modified(int n, const node_info_t *ni, uint32 flags) - { - if ( ni == NULL ) - { - node_cache.erase(n); - } - else - { - nodetext_cache_t *c = node_cache.get(n); - if ( c != NULL ) - { - if ( (flags & NIF_TEXT) == NIF_TEXT ) - c->text = ni->text; - if ( (flags & NIF_BG_COLOR) == NIF_BG_COLOR ) - c->bgcolor = ni->bg_color; - } - } - } - - void collect_class_callbacks_ids(callbacks_ids_t *out); - -private: - enum - { - GRCODE_HAVE_USER_HINT = 0x00010000, - GRCODE_HAVE_CLICKED = 0x00020000, - GRCODE_HAVE_DBL_CLICKED = 0x00040000, - GRCODE_HAVE_GOTFOCUS = 0x00080000, - GRCODE_HAVE_LOSTFOCUS = 0x00100000, - GRCODE_HAVE_CHANGED_CURRENT = 0x00200000, - GRCODE_HAVE_COMMAND = 0x00400000, - GRCODE_HAVE_CREATING_GROUP = 0x00800000, - GRCODE_HAVE_DELETING_GROUP = 0x01000000, - GRCODE_HAVE_GROUP_VISIBILITY = 0x02000000, - }; - struct nodetext_cache_t - { - qstring text; - bgcolor_t bgcolor; - nodetext_cache_t(const nodetext_cache_t &rhs): text(rhs.text), bgcolor(rhs.bgcolor) { } - nodetext_cache_t(const char *t, bgcolor_t c): text(t), bgcolor(c) { } - nodetext_cache_t() { } - }; - - class nodetext_cache_map_t: public std::map - { - public: - nodetext_cache_t *get(int node_id) - { - iterator it = find(node_id); - if ( it == end() ) - return NULL; - return &it->second; - } - nodetext_cache_t *add(const int node_id, const char *text, bgcolor_t bgcolor = DEFCOLOR) - { - return &(insert(std::make_pair(node_id, nodetext_cache_t(text, bgcolor))).first->second); - } - }; - - class cmdid_map_t: public std::map - { - private: - Py_ssize_t uid; - public: - - cmdid_map_t() - { - // We start by one and keep zero for error id - uid = 1; - } - - void add(py_graph_t *pyg) - { - (*this)[uid] = pyg; - ++uid; - } - - const Py_ssize_t id() const - { - return uid; - } - - void clear(py_graph_t *pyg) - { - iterator e = end(); - for (iterator it=begin();it!=end();) - { - if ( it->second == pyg ) - { - iterator temp = it++; - erase(temp); - } - else - ++it; - } - } - - py_graph_t *get(Py_ssize_t id) - { - iterator it = find(id); - return it == end() ? NULL : it->second; - } - }; - - static cmdid_map_t cmdid_pyg; - - bool refresh_needed; - nodetext_cache_map_t node_cache; - - // instance callback - int gr_callback(int code, va_list va); - - // static callback - static int idaapi s_callback(void *obj, int code, va_list va) - { - QASSERT(30453, py_customidamemo_t::lookup_info.find_by_py_view(NULL, NULL, (py_graph_t *) obj)); - PYW_GIL_GET; - return ((py_graph_t *)obj)->gr_callback(code, va); - } - - static bool idaapi s_menucb(void *ud) - { - PYW_GIL_GET; - Py_ssize_t id = (Py_ssize_t)ud; - py_graph_t *_this = cmdid_pyg.get(id); - if ( _this != NULL ) - _this->on_command(id); - - return true; - } - - void on_command(Py_ssize_t id) - { - // Check return value to OnRefresh() call - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id)); - PyW_ShowCbErr(S_ON_COMMAND); - } - - // Refresh user-defined graph node number and edges - // It calls Python method and expects that the user already filled - // the nodes and edges. The nodes and edges are retrieved and passed to IDA - void on_user_refresh(mutable_graph_t *g); - - // Retrieves the text for user-defined graph node - // It expects either a string or a tuple (string, bgcolor) - bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color); - - // Retrieves the hint for the user-defined graph - // Calls Python and expects a string or None - int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint); - - // graph is being destroyed - void on_graph_destroyed(mutable_graph_t * /*g*/ = NULL) - { - refresh_needed = true; - node_cache.clear(); - } - - // graph is being clicked - int on_clicked( - graph_viewer_t * /*view*/, - selection_item_t * /*item1*/, - graph_item_t *item2) - { - // in: graph_viewer_t *view - // selection_item_t *current_item1 - // graph_item_t *current_item2 - // out: 0-ok, 1-ignore click - // this callback allows you to ignore some clicks. - // it occurs too early, internal graph variables are not updated yet - // current_item1, current_item2 point to the same thing - // item2 has more information. - // see also: kernwin.hpp, custom_viewer_click_t - if ( item2->n == -1 ) - return 1; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_CLICK, - "i", - item2->n)); - PyW_ShowCbErr(S_ON_CLICK); - return result == NULL || !PyObject_IsTrue(result.o); - } - - // a graph node has been double clicked - int on_dblclicked(graph_viewer_t * /*view*/, selection_item_t *item) - { - // in: graph_viewer_t *view - // selection_item_t *current_item - // out: 0-ok, 1-ignore click - //graph_viewer_t *v = va_arg(va, graph_viewer_t *); - //selection_item_t *s = va_arg(va, selection_item_t *); - if ( item == NULL || !item->is_node ) - return 1; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_DBL_CLICK, - "i", - item->node)); - PyW_ShowCbErr(S_ON_DBL_CLICK); - return result == NULL || !PyObject_IsTrue(result.o); - } - - // a graph viewer got focus - void on_gotfocus(graph_viewer_t * /*view*/) - { - if ( self.o == NULL ) - return; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_ACTIVATE, - NULL)); - PyW_ShowCbErr(S_ON_ACTIVATE); - } - - // a graph viewer lost focus - void on_lostfocus(graph_viewer_t * /*view*/) - { - if ( self.o == NULL ) - return; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_DEACTIVATE, - NULL)); - PyW_ShowCbErr(S_ON_DEACTIVATE); - } - - // a new graph node became the current node - int on_changed_current(graph_viewer_t * /*view*/, int curnode) - { - // in: graph_viewer_t *view - // int curnode - // out: 0-ok, 1-forbid to change the current node - if ( curnode < 0 ) - return 0; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_SELECT, - "i", - curnode)); - PyW_ShowCbErr(S_ON_SELECT); - return !(result != NULL && PyObject_IsTrue(result.o)); - } - - // a group is being created - int on_creating_group(mutable_graph_t *my_g, intvec_t *my_nodes) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_nodes(PyList_New(my_nodes->size())); - int i; - intvec_t::const_iterator p; - for ( i = 0, p=my_nodes->begin(); p != my_nodes->end(); ++p, ++i ) - PyList_SetItem(py_nodes.o, i, PyInt_FromLong(*p)); - newref_t py_result( - PyObject_CallMethod( - self.o, - (char *)S_ON_CREATING_GROUP, - "O", - py_nodes.o)); - PyW_ShowCbErr(S_ON_CREATING_GROUP); - return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o); - } - - // a group is being deleted - int on_deleting_group(mutable_graph_t * /*g*/, int old_group) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - // TODO - return 0; - } - - // a group is being collapsed/uncollapsed - int on_group_visibility(mutable_graph_t * /*g*/, int group, bool expand) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - // TODO - return 0; - } - - - void show() - { - TForm *form; - if ( lookup_info.find_by_py_view(&form, NULL, this) ) - open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); - } - - void jump_to_node(int nid) - { - ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); - if ( nid >= PyList_Size(nodes.o) ) - return; - - viewer_center_on(view, nid); - int x, y; - - // will return a place only when a node was previously selected - place_t *old_pl = get_custom_viewer_place(view, false, &x, &y); - if ( old_pl != NULL ) - { - user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); - new_pl->node = nid; - jumpto(view, new_pl, x, y); - delete new_pl; - } - } - - virtual void refresh() - { - refresh_needed = true; - inherited::refresh(); - } - - int initialize(PyObject *self, const char *title) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !collect_pyobject_callbacks(self) ) - return -1; - - // Create form - HWND hwnd = NULL; - TForm *form = create_tform(title, &hwnd); - if ( hwnd != NULL ) // Created new tform - { - lookup_info_t::entry_t &e = lookup_info.new_entry(this); - // get a unique graph id - netnode id; - char grnode[MAXSTR]; - qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); - id.create(grnode); - graph_viewer_t *pview = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); - if ( pview != NULL ) - viewer_fit_window(pview); - bind(self, pview); - install_custom_viewer_handlers(); - refresh(); - lookup_info.commit(e, form, view); - } - else - { - show(); - } - - viewer_fit_window(view); - return 0; - } - - Py_ssize_t add_command(const char *title, const char *hotkey) - { - if ( !has_callback(GRCODE_HAVE_COMMAND) || view == NULL) - return 0; - Py_ssize_t cmd_id = cmdid_pyg.id(); - bool ok = viewer_add_menu_item(view, title, s_menucb, (void *)cmd_id, hotkey, 0); - if ( !ok ) - return 0; - cmdid_pyg.add(this); - return cmd_id; - } - -public: - py_graph_t() - { - // form = NULL; - refresh_needed = true; - } - - virtual ~py_graph_t() - { - // Remove all associated commands from the list - cmdid_pyg.clear(this); - } - - static void SelectNode(PyObject *self, int nid) - { - if ( nid < 0 ) - return; - - py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) - return; - - _this->jump_to_node(nid); - } - - static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) - { - py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) - return 0; - - return _this->add_command(title, hotkey); - } - - static void Close(PyObject *self) - { - TForm *form; - py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) - return; - close_tform(form, FORM_CLOSE_LATER); - } - - static py_graph_t *Show(PyObject *self) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - py_graph_t *py_graph = view_extract_this(self); - - // New instance? - if ( py_graph == NULL ) - { - qstring title; - if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) - return NULL; - - // Form already created? try to get associated py_graph instance - // so that we reuse it - graph_viewer_t *found_view; - TForm *form = find_tform(title.c_str()); - if ( form != NULL ) - lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); - - if ( py_graph == NULL ) - { - py_graph = new py_graph_t(); - } - else - { - // unbind so we are rebound - py_graph->unbind(); - py_graph->refresh_needed = true; - } - if ( py_graph->initialize(self, title.c_str()) < 0 ) - { - delete py_graph; - py_graph = NULL; - } - } - else - { - py_graph->show(); - } - return py_graph; - } -}; - -//------------------------------------------------------------------------- -void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) -{ - inherited::collect_class_callbacks_ids(out); - out->add(S_ON_REFRESH, 0); - out->add(S_ON_GETTEXT, 0); - out->add(S_M_EDGES, -1); - out->add(S_M_NODES, -1); - out->add(S_ON_HINT, GRCODE_HAVE_USER_HINT); - out->add(S_ON_CLICK, GRCODE_HAVE_CLICKED); - out->add(S_ON_DBL_CLICK, GRCODE_HAVE_DBL_CLICKED); - out->add(S_ON_COMMAND, GRCODE_HAVE_COMMAND); - out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT); - out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS); - out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS); - out->add(S_ON_CREATING_GROUP, GRCODE_HAVE_CREATING_GROUP); - out->add(S_ON_DELETING_GROUP, GRCODE_HAVE_DELETING_GROUP); - out->add(S_ON_GROUP_VISIBILITY, GRCODE_HAVE_GROUP_VISIBILITY); -} - -//------------------------------------------------------------------------- -void py_graph_t::on_user_refresh(mutable_graph_t *g) -{ - if ( !refresh_needed || self == NULL /* Happens at creation-time */ ) - return; - - // Check return value to OnRefresh() call - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL)); - PyW_ShowCbErr(S_ON_REFRESH); - if ( ret != NULL && PyObject_IsTrue(ret.o) ) - { - // Refer to the nodes - ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); - if ( ret != NULL && PyList_Check(nodes.o) ) - { - // Refer to the edges - ref_t edges(PyW_TryGetAttrString(self.o, S_M_EDGES)); - if ( ret != NULL && PyList_Check(edges.o) ) - { - // Resize the nodes - int max_nodes = abs(int(PyList_Size(nodes.o))); - g->clear(); - g->resize(max_nodes); - - // Mark that we refreshed already - refresh_needed = false; - - // Clear cached nodes - node_cache.clear(); - - // Get the edges - for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) - { - // Each list item is a sequence (id1, id2) - borref_t item(PyList_GetItem(edges.o, i)); - if ( !PySequence_Check(item.o) ) - continue; - - // Get and validate each of the two elements in the sequence - int edge_ids[2]; - int j; - for ( j=0; j max_nodes ) - break; - edge_ids[j] = v; - } - - // Incomplete? - if ( j != qnumber(edge_ids) ) - break; - - // Add the edge - g->add_edge(edge_ids[0], edge_ids[1], NULL); - } - } - } - } -} - -//------------------------------------------------------------------------- -bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) -{ - // If already cached then return the value - nodetext_cache_t *c = node_cache.get(node); - if ( c != NULL ) - { - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - return true; - } - - // Not cached, call Python - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node)); - PyW_ShowCbErr(S_ON_GETTEXT); - if ( result == NULL ) - return false; - - bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; - const char *s; - - // User returned a string? - if ( PyString_Check(result.o) ) - { - s = PyString_AsString(result.o); - if ( s == NULL ) - s = ""; - c = node_cache.add(node, s, cl); - } - // User returned a sequence of text and bgcolor - else if ( PySequence_Check(result.o) && PySequence_Size(result.o) == 2 ) - { - newref_t py_str(PySequence_GetItem(result.o, 0)); - newref_t py_color(PySequence_GetItem(result.o, 1)); - - if ( py_str == NULL || !PyString_Check(py_str.o) || (s = PyString_AsString(py_str.o)) == NULL ) - s = ""; - if ( py_color != NULL && PyNumber_Check(py_color.o) ) - cl = bgcolor_t(PyLong_AsUnsignedLong(py_color.o)); - - c = node_cache.add(node, s, cl); - } - - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - - return true; -} - -//------------------------------------------------------------------------- -int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) -{ - // 'hint' must be allocated by qalloc() or qstrdup() - // out: 0-use default hint, 1-use proposed hint - - // We dispatch hints over nodes only - if ( mousenode == -1 ) - return 0; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode)); - PyW_ShowCbErr(S_ON_HINT); - bool ok = result != NULL && PyString_Check(result.o); - if ( ok ) - *hint = qstrdup(PyString_AsString(result.o)); - return ok; // use our hint -} - - -//------------------------------------------------------------------------- -int py_graph_t::gr_callback(int code, va_list va) -{ - int ret; - switch ( code ) - { - // - case grcode_user_text: - { - mutable_graph_t *g = va_arg(va, mutable_graph_t *); - int node = va_arg(va, int); - const char **result = va_arg(va, const char **); - bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); - ret = on_user_text(g, node, result, bgcolor); - break; - } - // - case grcode_destroyed: - on_graph_destroyed(va_arg(va, mutable_graph_t *)); - ret = 0; - break; - - // - case grcode_clicked: - if ( has_callback(GRCODE_HAVE_CLICKED) ) - { - graph_viewer_t *view = va_arg(va, graph_viewer_t *); - selection_item_t *item = va_arg(va, selection_item_t *); - graph_item_t *gitem = va_arg(va, graph_item_t *); - ret = on_clicked(view, item, gitem); - } - else - { - // Ignore the click - ret = 1; - } - break; - // - case grcode_dblclicked: - if ( has_callback(GRCODE_HAVE_DBL_CLICKED) ) - { - graph_viewer_t *view = va_arg(va, graph_viewer_t *); - selection_item_t *item = va_arg(va, selection_item_t *); - ret = on_dblclicked(view, item); - } - else - ret = 0; // We don't want to ignore the double click, but rather - // fallback to the default behavior (e.g., double-clicking - // on an edge will to jump to the node on the other side - // of that edge.) - break; - // - case grcode_gotfocus: - if ( has_callback(GRCODE_HAVE_GOTFOCUS) ) - on_gotfocus(va_arg(va, graph_viewer_t *)); - - ret = 0; - break; - // - case grcode_lostfocus: - if ( has_callback(GRCODE_HAVE_LOSTFOCUS) ) - on_lostfocus(va_arg(va, graph_viewer_t *)); - - ret = 0; - break; - // - case grcode_user_refresh: - on_user_refresh(va_arg(va, mutable_graph_t *)); - - ret = 1; - break; - // - case grcode_user_hint: - if ( has_callback(GRCODE_HAVE_USER_HINT) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t *); - int mousenode = va_arg(va, int); - int mouseedge_src = va_arg(va, int); - int mouseedge_dest = va_arg(va, int); - char **hint = va_arg(va, char **); - ret = on_user_hint(g, mousenode, mouseedge_src, mouseedge_dest, hint); - } - else - { - ret = 0; - } - break; - // - case grcode_changed_current: - if ( has_callback(GRCODE_HAVE_CHANGED_CURRENT) ) - { - graph_viewer_t *view = va_arg(va, graph_viewer_t *); - int cur_node = va_arg(va, int); - ret = on_changed_current(view, cur_node); - } - else - ret = 0; // allow selection change - break; - // - case grcode_creating_group: // a group is being created - if ( has_callback(GRCODE_HAVE_CREATING_GROUP) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t*); - intvec_t *nodes = va_arg(va, intvec_t*); - ret = on_creating_group(g, nodes); - } - else - { - ret = 0; // Ok to create - } - break; - // - case grcode_deleting_group: // a group is being deleted - if ( has_callback(GRCODE_HAVE_DELETING_GROUP) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t*); - int old_group = va_arg(va, int); - ret = on_deleting_group(g, old_group); - } - else - { - ret = 0; // Ok to delete - } - break; - // - case grcode_group_visibility: // a group is being collapsed/uncollapsed - if ( has_callback(GRCODE_HAVE_GROUP_VISIBILITY) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t*); - int group = va_arg(va, int); - bool expand = bool(va_arg(va, int)); - ret = on_group_visibility(g, group, expand); - } - else - { - ret = 0; // Ok. - } - break; - // - default: - ret = 0; - break; - } - //grcode_changed_graph, // new graph has been set - //grcode_user_size, // calculate node size for user-defined graph - //grcode_user_title, // render node title of a user-defined graph - //grcode_user_draw, // render node of a user-defined graph - return ret; -} - -//------------------------------------------------------------------------- -py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; - -bool pyg_show(PyObject *self) -{ - return py_graph_t::Show(self) != NULL; -} - -void pyg_close(PyObject *self) -{ - py_graph_t::Close(self); -} - -PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); -} - -void pyg_select_node(PyObject *self, int nid) -{ - py_graph_t::SelectNode(self, nid); -} -// - -//-------------------------------------------------------------------------- - -// -void pyg_close(PyObject *self); -PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); -void pyg_select_node(PyObject *self, int nid); -bool pyg_show(PyObject *self); -// -#endif +#ifndef __PY_GRAPH__ +#define __PY_GRAPH__ + +// +class py_graph_t : public py_customidamemo_t +{ + typedef py_customidamemo_t inherited; + +protected: + + virtual void node_info_modified(int n, const node_info_t *ni, uint32 flags) + { + if ( ni == NULL ) + { + node_cache.erase(n); + } + else + { + nodetext_cache_t *c = node_cache.get(n); + if ( c != NULL ) + { + if ( (flags & NIF_TEXT) == NIF_TEXT ) + c->text = ni->text; + if ( (flags & NIF_BG_COLOR) == NIF_BG_COLOR ) + c->bgcolor = ni->bg_color; + } + } + } + + void collect_class_callbacks_ids(callbacks_ids_t *out); + +private: + enum + { + GRCODE_HAVE_USER_HINT = 0x00010000, + GRCODE_HAVE_CLICKED = 0x00020000, + GRCODE_HAVE_DBL_CLICKED = 0x00040000, + GRCODE_HAVE_GOTFOCUS = 0x00080000, + GRCODE_HAVE_LOSTFOCUS = 0x00100000, + GRCODE_HAVE_CHANGED_CURRENT = 0x00200000, + GRCODE_HAVE_COMMAND = 0x00400000, + GRCODE_HAVE_CREATING_GROUP = 0x00800000, + GRCODE_HAVE_DELETING_GROUP = 0x01000000, + GRCODE_HAVE_GROUP_VISIBILITY = 0x02000000, + }; + struct nodetext_cache_t + { + qstring text; + bgcolor_t bgcolor; + nodetext_cache_t(const nodetext_cache_t &rhs): text(rhs.text), bgcolor(rhs.bgcolor) { } + nodetext_cache_t(const char *t, bgcolor_t c): text(t), bgcolor(c) { } + nodetext_cache_t() { } + }; + + class nodetext_cache_map_t: public std::map + { + public: + nodetext_cache_t *get(int node_id) + { + iterator it = find(node_id); + if ( it == end() ) + return NULL; + return &it->second; + } + nodetext_cache_t *add(const int node_id, const char *text, bgcolor_t bgcolor = DEFCOLOR) + { + return &(insert(std::make_pair(node_id, nodetext_cache_t(text, bgcolor))).first->second); + } + }; + + class cmdid_map_t: public std::map + { + private: + Py_ssize_t uid; + public: + + cmdid_map_t() + { + // We start by one and keep zero for error id + uid = 1; + } + + void add(py_graph_t *pyg) + { + (*this)[uid] = pyg; + ++uid; + } + + const Py_ssize_t id() const + { + return uid; + } + + void clear(py_graph_t *pyg) + { + iterator e = end(); + for (iterator it=begin();it!=end();) + { + if ( it->second == pyg ) + { + iterator temp = it++; + erase(temp); + } + else + ++it; + } + } + + py_graph_t *get(Py_ssize_t id) + { + iterator it = find(id); + return it == end() ? NULL : it->second; + } + }; + + static cmdid_map_t cmdid_pyg; + + bool refresh_needed; + nodetext_cache_map_t node_cache; + + // instance callback + int gr_callback(int code, va_list va); + + // static callback + static int idaapi s_callback(void *obj, int code, va_list va) + { + QASSERT(30453, py_customidamemo_t::lookup_info.find_by_py_view(NULL, NULL, (py_graph_t *) obj)); + PYW_GIL_GET; + return ((py_graph_t *)obj)->gr_callback(code, va); + } + + static bool idaapi s_menucb(void *ud) + { + PYW_GIL_GET; + Py_ssize_t id = (Py_ssize_t)ud; + py_graph_t *_this = cmdid_pyg.get(id); + if ( _this != NULL ) + _this->on_command(id); + + return true; + } + + void on_command(Py_ssize_t id) + { + // Check return value to OnRefresh() call + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id)); + PyW_ShowCbErr(S_ON_COMMAND); + } + + // Refresh user-defined graph node number and edges + // It calls Python method and expects that the user already filled + // the nodes and edges. The nodes and edges are retrieved and passed to IDA + void on_user_refresh(mutable_graph_t *g); + + // Retrieves the text for user-defined graph node + // It expects either a string or a tuple (string, bgcolor) + bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color); + + // Retrieves the hint for the user-defined graph + // Calls Python and expects a string or None + int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint); + + // graph is being destroyed + void on_graph_destroyed(mutable_graph_t * /*g*/ = NULL) + { + refresh_needed = true; + node_cache.clear(); + } + + // graph is being clicked + int on_clicked( + graph_viewer_t * /*view*/, + selection_item_t * /*item1*/, + graph_item_t *item2) + { + // in: graph_viewer_t *view + // selection_item_t *current_item1 + // graph_item_t *current_item2 + // out: 0-ok, 1-ignore click + // this callback allows you to ignore some clicks. + // it occurs too early, internal graph variables are not updated yet + // current_item1, current_item2 point to the same thing + // item2 has more information. + // see also: kernwin.hpp, custom_viewer_click_t + if ( item2->n == -1 ) + return 1; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CLICK, + "i", + item2->n)); + PyW_ShowCbErr(S_ON_CLICK); + return result == NULL || !PyObject_IsTrue(result.o); + } + + // a graph node has been double clicked + int on_dblclicked(graph_viewer_t * /*view*/, selection_item_t *item) + { + // in: graph_viewer_t *view + // selection_item_t *current_item + // out: 0-ok, 1-ignore click + //graph_viewer_t *v = va_arg(va, graph_viewer_t *); + //selection_item_t *s = va_arg(va, selection_item_t *); + if ( item == NULL || !item->is_node ) + return 1; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DBL_CLICK, + "i", + item->node)); + PyW_ShowCbErr(S_ON_DBL_CLICK); + return result == NULL || !PyObject_IsTrue(result.o); + } + + // a graph viewer got focus + void on_gotfocus(graph_viewer_t * /*view*/) + { + if ( self.o == NULL ) + return; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_ACTIVATE, + NULL)); + PyW_ShowCbErr(S_ON_ACTIVATE); + } + + // a graph viewer lost focus + void on_lostfocus(graph_viewer_t * /*view*/) + { + if ( self.o == NULL ) + return; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DEACTIVATE, + NULL)); + PyW_ShowCbErr(S_ON_DEACTIVATE); + } + + // a new graph node became the current node + int on_changed_current(graph_viewer_t * /*view*/, int curnode) + { + // in: graph_viewer_t *view + // int curnode + // out: 0-ok, 1-forbid to change the current node + if ( curnode < 0 ) + return 0; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_SELECT, + "i", + curnode)); + PyW_ShowCbErr(S_ON_SELECT); + return !(result != NULL && PyObject_IsTrue(result.o)); + } + + // a group is being created + int on_creating_group(mutable_graph_t *my_g, intvec_t *my_nodes) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_nodes(PyList_New(my_nodes->size())); + int i; + intvec_t::const_iterator p; + for ( i = 0, p=my_nodes->begin(); p != my_nodes->end(); ++p, ++i ) + PyList_SetItem(py_nodes.o, i, PyInt_FromLong(*p)); + newref_t py_result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CREATING_GROUP, + "O", + py_nodes.o)); + PyW_ShowCbErr(S_ON_CREATING_GROUP); + return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o); + } + + // a group is being deleted + int on_deleting_group(mutable_graph_t * /*g*/, int old_group) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + // a group is being collapsed/uncollapsed + int on_group_visibility(mutable_graph_t * /*g*/, int group, bool expand) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + + void show() + { + TForm *form; + if ( lookup_info.find_by_py_view(&form, NULL, this) ) + open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); + } + + void jump_to_node(int nid) + { + ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); + if ( nid >= PyList_Size(nodes.o) ) + return; + + viewer_center_on(view, nid); + int x, y; + + // will return a place only when a node was previously selected + place_t *old_pl = get_custom_viewer_place(view, false, &x, &y); + if ( old_pl != NULL ) + { + user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); + new_pl->node = nid; + jumpto(view, new_pl, x, y); + delete new_pl; + } + } + + virtual void refresh() + { + refresh_needed = true; + inherited::refresh(); + } + + int initialize(PyObject *self, const char *title) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !collect_pyobject_callbacks(self) ) + return -1; + + // Create form + HWND hwnd = NULL; + TForm *form = create_tform(title, &hwnd); + if ( hwnd != NULL ) // Created new tform + { + lookup_info_t::entry_t &e = lookup_info.new_entry(this); + // get a unique graph id + netnode id; + char grnode[MAXSTR]; + qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); + id.create(grnode); + graph_viewer_t *pview = create_graph_viewer(form, id, s_callback, this, 0); + open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); + if ( pview != NULL ) + viewer_fit_window(pview); + bind(self, pview); + install_custom_viewer_handlers(); + refresh(); + lookup_info.commit(e, form, view); + } + else + { + show(); + } + + viewer_fit_window(view); + return 0; + } + + Py_ssize_t add_command(const char *title, const char *hotkey) + { + if ( !has_callback(GRCODE_HAVE_COMMAND) || view == NULL) + return 0; + Py_ssize_t cmd_id = cmdid_pyg.id(); + bool ok = viewer_add_menu_item(view, title, s_menucb, (void *)cmd_id, hotkey, 0); + if ( !ok ) + return 0; + cmdid_pyg.add(this); + return cmd_id; + } + +public: + py_graph_t() + { + // form = NULL; + refresh_needed = true; + } + + virtual ~py_graph_t() + { + // Remove all associated commands from the list + cmdid_pyg.clear(this); + } + + static void SelectNode(PyObject *self, int nid) + { + if ( nid < 0 ) + return; + + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return; + + _this->jump_to_node(nid); + } + + static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) + { + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return 0; + + return _this->add_command(title, hotkey); + } + + static void Close(PyObject *self) + { + TForm *form; + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) + return; + close_tform(form, FORM_CLOSE_LATER); + } + + static py_graph_t *Show(PyObject *self) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_graph_t *py_graph = view_extract_this(self); + + // New instance? + if ( py_graph == NULL ) + { + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return NULL; + + // Form already created? try to get associated py_graph instance + // so that we reuse it + graph_viewer_t *found_view; + TForm *form = find_tform(title.c_str()); + if ( form != NULL ) + lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); + + if ( py_graph == NULL ) + { + py_graph = new py_graph_t(); + } + else + { + // unbind so we are rebound + py_graph->unbind(false); + py_graph->refresh_needed = true; + } + if ( py_graph->initialize(self, title.c_str()) < 0 ) + { + delete py_graph; + py_graph = NULL; + } + } + else + { + py_graph->show(); + } + return py_graph; + } +}; + +//------------------------------------------------------------------------- +void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + inherited::collect_class_callbacks_ids(out); + out->add(S_ON_REFRESH, 0); + out->add(S_ON_GETTEXT, 0); + out->add(S_M_EDGES, -1); + out->add(S_M_NODES, -1); + out->add(S_ON_HINT, GRCODE_HAVE_USER_HINT); + out->add(S_ON_CLICK, GRCODE_HAVE_CLICKED); + out->add(S_ON_DBL_CLICK, GRCODE_HAVE_DBL_CLICKED); + out->add(S_ON_COMMAND, GRCODE_HAVE_COMMAND); + out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT); + out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS); + out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS); + out->add(S_ON_CREATING_GROUP, GRCODE_HAVE_CREATING_GROUP); + out->add(S_ON_DELETING_GROUP, GRCODE_HAVE_DELETING_GROUP); + out->add(S_ON_GROUP_VISIBILITY, GRCODE_HAVE_GROUP_VISIBILITY); +} + +//------------------------------------------------------------------------- +void py_graph_t::on_user_refresh(mutable_graph_t *g) +{ + if ( !refresh_needed || self == NULL /* Happens at creation-time */ ) + return; + + // Check return value to OnRefresh() call + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL)); + PyW_ShowCbErr(S_ON_REFRESH); + if ( ret != NULL && PyObject_IsTrue(ret.o) ) + { + // Refer to the nodes + ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); + if ( ret != NULL && PyList_Check(nodes.o) ) + { + // Refer to the edges + ref_t edges(PyW_TryGetAttrString(self.o, S_M_EDGES)); + if ( ret != NULL && PyList_Check(edges.o) ) + { + // Resize the nodes + int max_nodes = abs(int(PyList_Size(nodes.o))); + g->clear(); + g->resize(max_nodes); + + // Mark that we refreshed already + refresh_needed = false; + + // Clear cached nodes + node_cache.clear(); + + // Get the edges + for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) + { + // Each list item is a sequence (id1, id2) + borref_t item(PyList_GetItem(edges.o, i)); + if ( !PySequence_Check(item.o) ) + continue; + + // Get and validate each of the two elements in the sequence + int edge_ids[2]; + int j; + for ( j=0; j max_nodes ) + break; + edge_ids[j] = v; + } + + // Incomplete? + if ( j != qnumber(edge_ids) ) + break; + + // Add the edge + g->add_edge(edge_ids[0], edge_ids[1], NULL); + } + } + } + } +} + +//------------------------------------------------------------------------- +bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) +{ + // If already cached then return the value + nodetext_cache_t *c = node_cache.get(node); + if ( c != NULL ) + { + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + return true; + } + + // Not cached, call Python + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node)); + PyW_ShowCbErr(S_ON_GETTEXT); + if ( result == NULL ) + return false; + + bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; + const char *s; + + // User returned a string? + if ( PyString_Check(result.o) ) + { + s = PyString_AsString(result.o); + if ( s == NULL ) + s = ""; + c = node_cache.add(node, s, cl); + } + // User returned a sequence of text and bgcolor + else if ( PySequence_Check(result.o) && PySequence_Size(result.o) == 2 ) + { + newref_t py_str(PySequence_GetItem(result.o, 0)); + newref_t py_color(PySequence_GetItem(result.o, 1)); + + if ( py_str == NULL || !PyString_Check(py_str.o) || (s = PyString_AsString(py_str.o)) == NULL ) + s = ""; + if ( py_color != NULL && PyNumber_Check(py_color.o) ) + cl = bgcolor_t(PyLong_AsUnsignedLong(py_color.o)); + + c = node_cache.add(node, s, cl); + } + + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + + return true; +} + +//------------------------------------------------------------------------- +int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) +{ + // 'hint' must be allocated by qalloc() or qstrdup() + // out: 0-use default hint, 1-use proposed hint + + // We dispatch hints over nodes only + if ( mousenode == -1 ) + return 0; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode)); + PyW_ShowCbErr(S_ON_HINT); + bool ok = result != NULL && PyString_Check(result.o); + if ( ok ) + *hint = qstrdup(PyString_AsString(result.o)); + return ok; // use our hint +} + + +//------------------------------------------------------------------------- +int py_graph_t::gr_callback(int code, va_list va) +{ + int ret; + switch ( code ) + { + // + case grcode_user_text: + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int node = va_arg(va, int); + const char **result = va_arg(va, const char **); + bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); + ret = on_user_text(g, node, result, bgcolor); + break; + } + // + case grcode_destroyed: + on_graph_destroyed(va_arg(va, mutable_graph_t *)); + ret = 0; + break; + + // + case grcode_clicked: + if ( has_callback(GRCODE_HAVE_CLICKED) ) + { + graph_viewer_t *view = va_arg(va, graph_viewer_t *); + selection_item_t *item = va_arg(va, selection_item_t *); + graph_item_t *gitem = va_arg(va, graph_item_t *); + ret = on_clicked(view, item, gitem); + } + else + { + // Ignore the click + ret = 1; + } + break; + // + case grcode_dblclicked: + if ( has_callback(GRCODE_HAVE_DBL_CLICKED) ) + { + graph_viewer_t *view = va_arg(va, graph_viewer_t *); + selection_item_t *item = va_arg(va, selection_item_t *); + ret = on_dblclicked(view, item); + } + else + ret = 0; // We don't want to ignore the double click, but rather + // fallback to the default behavior (e.g., double-clicking + // on an edge will to jump to the node on the other side + // of that edge.) + break; + // + case grcode_gotfocus: + if ( has_callback(GRCODE_HAVE_GOTFOCUS) ) + on_gotfocus(va_arg(va, graph_viewer_t *)); + + ret = 0; + break; + // + case grcode_lostfocus: + if ( has_callback(GRCODE_HAVE_LOSTFOCUS) ) + on_lostfocus(va_arg(va, graph_viewer_t *)); + + ret = 0; + break; + // + case grcode_user_refresh: + on_user_refresh(va_arg(va, mutable_graph_t *)); + + ret = 1; + break; + // + case grcode_user_hint: + if ( has_callback(GRCODE_HAVE_USER_HINT) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int mousenode = va_arg(va, int); + int mouseedge_src = va_arg(va, int); + int mouseedge_dest = va_arg(va, int); + char **hint = va_arg(va, char **); + ret = on_user_hint(g, mousenode, mouseedge_src, mouseedge_dest, hint); + } + else + { + ret = 0; + } + break; + // + case grcode_changed_current: + if ( has_callback(GRCODE_HAVE_CHANGED_CURRENT) ) + { + graph_viewer_t *view = va_arg(va, graph_viewer_t *); + int cur_node = va_arg(va, int); + ret = on_changed_current(view, cur_node); + } + else + ret = 0; // allow selection change + break; + // + case grcode_creating_group: // a group is being created + if ( has_callback(GRCODE_HAVE_CREATING_GROUP) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + intvec_t *nodes = va_arg(va, intvec_t*); + ret = on_creating_group(g, nodes); + } + else + { + ret = 0; // Ok to create + } + break; + // + case grcode_deleting_group: // a group is being deleted + if ( has_callback(GRCODE_HAVE_DELETING_GROUP) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int old_group = va_arg(va, int); + ret = on_deleting_group(g, old_group); + } + else + { + ret = 0; // Ok to delete + } + break; + // + case grcode_group_visibility: // a group is being collapsed/uncollapsed + if ( has_callback(GRCODE_HAVE_GROUP_VISIBILITY) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int group = va_arg(va, int); + bool expand = bool(va_arg(va, int)); + ret = on_group_visibility(g, group, expand); + } + else + { + ret = 0; // Ok. + } + break; + // + default: + ret = 0; + break; + } + //grcode_changed_graph, // new graph has been set + //grcode_user_size, // calculate node size for user-defined graph + //grcode_user_title, // render node title of a user-defined graph + //grcode_user_draw, // render node of a user-defined graph + return ret; +} + +//------------------------------------------------------------------------- +py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; + +bool pyg_show(PyObject *self) +{ + return py_graph_t::Show(self) != NULL; +} + +void pyg_close(PyObject *self) +{ + py_graph_t::Close(self); +} + +PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); +} + +void pyg_select_node(PyObject *self, int nid) +{ + py_graph_t::SelectNode(self, nid); +} +// + +//-------------------------------------------------------------------------- + +// +void pyg_close(PyObject *self); +PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); +void pyg_select_node(PyObject *self, int nid); +bool pyg_show(PyObject *self); +// +#endif diff --git a/pywraps/py_graph.py b/pywraps/py_graph.py index 689a31bc..9678bc10 100644 --- a/pywraps/py_graph.py +++ b/pywraps/py_graph.py @@ -1,161 +1,161 @@ -# -class GraphViewer(CustomIDAMemo): - """This class wraps the user graphing facility provided by the graph.hpp file""" - def __init__(self, title, close_open = False): - """ - Constructs the GraphView object. - Please do not remove or rename the private fields - - @param title: The title of the graph window - @param close_open: Should it attempt to close an existing graph (with same title) before creating this graph? - """ - self._title = title - self._nodes = [] - self._edges = [] - self._close_open = close_open - - def AddNode(self, obj): - """Creates a node associated with the given object and returns the node id""" - id = len(self._nodes) - self._nodes.append(obj) - return id - - def AddEdge(self, src_node, dest_node): - """Creates an edge between two given node ids""" - self._edges.append( (src_node, dest_node) ) - - def Clear(self): - """Clears all the nodes and edges""" - self._nodes = [] - self._edges = [] - - - def __iter__(self): - return (self._nodes[index] for index in xrange(0, len(self._nodes))) - - - def __getitem__(self, idx): - """Returns a reference to the object associated with this node id""" - if idx >= len(self._nodes): - raise KeyError - else: - return self._nodes[idx] - - def Count(self): - """Returns the node count""" - return len(self._nodes) - - def Close(self): - """ - Closes the graph. - It is possible to call Show() again (which will recreate the graph) - """ - _idaapi.pyg_close(self) - - def Show(self): - """ - Shows an existing graph or creates a new one - - @return: Boolean - """ - if self._close_open: - frm = _idaapi.find_tform(self._title) - if frm: - _idaapi.close_tform(frm, 0) - return _idaapi.pyg_show(self) - - def Select(self, node_id): - """Selects a node on the graph""" - _idaapi.pyg_select_node(self, node_id) - - def AddCommand(self, title, hotkey): - """ - Deprecated: Use - - register_action() - - attach_action_to_popup() - """ - return _idaapi.pyg_add_command(self, title, hotkey) - - def OnRefresh(self): - """ - Event called when the graph is refreshed or first created. - From this event you are supposed to create nodes and edges. - This callback is mandatory. - - @note: ***It is important to clear previous nodes before adding nodes.*** - @return: Returning True tells the graph viewer to use the items. Otherwise old items will be used. - """ - self.Clear() - - return True -# -# def OnGetText(self, node_id): -# """ -# Triggered when the graph viewer wants the text and color for a given node. -# This callback is triggered one time for a given node (the value will be cached and used later without calling Python). -# When you call refresh then again this callback will be called for each node. -# -# This callback is mandatory. -# -# @return: Return a string to describe the node text or return a tuple (node_text, node_color) to describe both text and color -# """ -# return str(self[node_id]) -# -# def OnActivate(self): -# """ -# Triggered when the graph window gets the focus -# @return: None -# """ -# print "Activated...." -# -# def OnDeactivate(self): -# """Triggered when the graph window loses the focus -# @return: None -# """ -# print "Deactivated...." -# -# def OnSelect(self, node_id): -# """ -# Triggered when a node is being selected -# @return: Return True to allow the node to be selected or False to disallow node selection change -# """ -# # allow selection change -# return True -# -# def OnHint(self, node_id): -# """ -# Triggered when the graph viewer wants to retrieve hint text associated with a given node -# -# @return: None if no hint is avail or a string designating the hint -# """ -# return "hint for " + str(node_id) -# -# def OnClose(self): -# """Triggered when the graph viewer window is being closed -# @return: None -# """ -# print "Closing......." -# -# def OnClick(self, node_id): -# """ -# Triggered when a node is clicked -# @return: False to ignore the click and True otherwise -# """ -# print "clicked on", self[node_id] -# return True -# -# def OnDblClick(self, node_id): -# """ -# Triggerd when a node is double-clicked. -# @return: False to ignore the click and True otherwise -# """ -# print "dblclicked on", self[node_id] -# return True -# -# def OnCommand(self, cmd_id): -# """ -# Deprecated -# """ -# pass -# -# +# +class GraphViewer(CustomIDAMemo): + """This class wraps the user graphing facility provided by the graph.hpp file""" + def __init__(self, title, close_open = False): + """ + Constructs the GraphView object. + Please do not remove or rename the private fields + + @param title: The title of the graph window + @param close_open: Should it attempt to close an existing graph (with same title) before creating this graph? + """ + self._title = title + self._nodes = [] + self._edges = [] + self._close_open = close_open + + def AddNode(self, obj): + """Creates a node associated with the given object and returns the node id""" + id = len(self._nodes) + self._nodes.append(obj) + return id + + def AddEdge(self, src_node, dest_node): + """Creates an edge between two given node ids""" + self._edges.append( (src_node, dest_node) ) + + def Clear(self): + """Clears all the nodes and edges""" + self._nodes = [] + self._edges = [] + + + def __iter__(self): + return (self._nodes[index] for index in xrange(0, len(self._nodes))) + + + def __getitem__(self, idx): + """Returns a reference to the object associated with this node id""" + if idx >= len(self._nodes): + raise KeyError + else: + return self._nodes[idx] + + def Count(self): + """Returns the node count""" + return len(self._nodes) + + def Close(self): + """ + Closes the graph. + It is possible to call Show() again (which will recreate the graph) + """ + _idaapi.pyg_close(self) + + def Show(self): + """ + Shows an existing graph or creates a new one + + @return: Boolean + """ + if self._close_open: + frm = _idaapi.find_tform(self._title) + if frm: + _idaapi.close_tform(frm, 0) + return _idaapi.pyg_show(self) + + def Select(self, node_id): + """Selects a node on the graph""" + _idaapi.pyg_select_node(self, node_id) + + def AddCommand(self, title, hotkey): + """ + Deprecated: Use + - register_action() + - attach_action_to_popup() + """ + return _idaapi.pyg_add_command(self, title, hotkey) + + def OnRefresh(self): + """ + Event called when the graph is refreshed or first created. + From this event you are supposed to create nodes and edges. + This callback is mandatory. + + @note: ***It is important to clear previous nodes before adding nodes.*** + @return: Returning True tells the graph viewer to use the items. Otherwise old items will be used. + """ + self.Clear() + + return True +# +# def OnGetText(self, node_id): +# """ +# Triggered when the graph viewer wants the text and color for a given node. +# This callback is triggered one time for a given node (the value will be cached and used later without calling Python). +# When you call refresh then again this callback will be called for each node. +# +# This callback is mandatory. +# +# @return: Return a string to describe the node text or return a tuple (node_text, node_color) to describe both text and color +# """ +# return str(self[node_id]) +# +# def OnActivate(self): +# """ +# Triggered when the graph window gets the focus +# @return: None +# """ +# print "Activated...." +# +# def OnDeactivate(self): +# """Triggered when the graph window loses the focus +# @return: None +# """ +# print "Deactivated...." +# +# def OnSelect(self, node_id): +# """ +# Triggered when a node is being selected +# @return: Return True to allow the node to be selected or False to disallow node selection change +# """ +# # allow selection change +# return True +# +# def OnHint(self, node_id): +# """ +# Triggered when the graph viewer wants to retrieve hint text associated with a given node +# +# @return: None if no hint is avail or a string designating the hint +# """ +# return "hint for " + str(node_id) +# +# def OnClose(self): +# """Triggered when the graph viewer window is being closed +# @return: None +# """ +# print "Closing......." +# +# def OnClick(self, node_id): +# """ +# Triggered when a node is clicked +# @return: False to ignore the click and True otherwise +# """ +# print "clicked on", self[node_id] +# return True +# +# def OnDblClick(self, node_id): +# """ +# Triggerd when a node is double-clicked. +# @return: False to ignore the click and True otherwise +# """ +# print "dblclicked on", self[node_id] +# return True +# +# def OnCommand(self, cmd_id): +# """ +# Deprecated +# """ +# pass +# +# diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp index df07b692..a87ca7d1 100644 --- a/pywraps/py_idaapi.hpp +++ b/pywraps/py_idaapi.hpp @@ -1,832 +1,832 @@ -#ifndef __PY_IDAAPI__ -#define __PY_IDAAPI__ - -// - -//------------------------------------------------------------------------ -// String constants used -static const char S_PYINVOKE0[] = "_py_invoke0"; -static const char S_PY_SWIEX_CLSNAME[] = "switch_info_ex_t"; -static const char S_PY_OP_T_CLSNAME[] = "op_t"; -static const char S_PROPS[] = "props"; -static const char S_NAME[] = "name"; -static const char S_TITLE[] = "title"; -static const char S_ASM_KEYWORD[] = "asm_keyword"; -static const char S_MENU_NAME[] = "menu_name"; -static const char S_HOTKEY[] = "hotkey"; -static const char S_EMBEDDED[] = "embedded"; -static const char S_POPUP_NAMES[] = "popup_names"; -static const char S_FLAGS[] = "flags"; -static const char S_VALUE_SIZE[] = "value_size"; -static const char S_MAY_CREATE_AT[] = "may_create_at"; -static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; -static const char S_ID[] = "id"; -static const char S_PRINTF[] = "printf"; -static const char S_TEXT_WIDTH[] = "text_width"; -static const char S_SCAN[] = "scan"; -static const char S_ANALYZE[] = "analyze"; -static const char S_CBSIZE[] = "cbsize"; -static const char S_ON_CLICK[] = "OnClick"; -static const char S_ON_CLOSE[] = "OnClose"; -static const char S_ON_DBL_CLICK[] = "OnDblClick"; -static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; -static const char S_ON_KEYDOWN[] = "OnKeydown"; -static const char S_ON_COMPLETE_LINE[] = "OnCompleteLine"; -static const char S_ON_CREATE[] = "OnCreate"; -static const char S_ON_POPUP[] = "OnPopup"; -static const char S_ON_HINT[] = "OnHint"; -static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; -static const char S_ON_EDIT_LINE[] = "OnEditLine"; -static const char S_ON_INSERT_LINE[] = "OnInsertLine"; -static const char S_ON_GET_LINE[] = "OnGetLine"; -static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; -static const char S_ON_REFRESH[] = "OnRefresh"; -static const char S_ON_REFRESHED[] = "OnRefreshed"; -static const char S_ON_EXECUTE_LINE[] = "OnExecuteLine"; -static const char S_ON_SELECT_LINE[] = "OnSelectLine"; -static const char S_ON_SELECTION_CHANGE[] = "OnSelectionChange"; -static const char S_ON_COMMAND[] = "OnCommand"; -static const char S_ON_GET_ICON[] = "OnGetIcon"; -static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; -static const char S_ON_GET_SIZE[] = "OnGetSize"; -static const char S_ON_GETTEXT[] = "OnGetText"; -static const char S_ON_ACTIVATE[] = "OnActivate"; -static const char S_ON_DEACTIVATE[] = "OnDeactivate"; -static const char S_ON_SELECT[] = "OnSelect"; -static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; -static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; -static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; -static const char S_M_EDGES[] = "_edges"; -static const char S_M_NODES[] = "_nodes"; -static const char S_M_THIS[] = "_this"; -static const char S_M_TITLE[] = "_title"; -static const char S_CLINK_NAME[] = "__clink__"; -static const char S_ON_VIEW_ACTIVATED[] = "OnViewActivated"; -static const char S_ON_VIEW_DEACTIVATED[] = "OnViewDeactivated"; -static const char S_ON_VIEW_KEYDOWN[] = "OnViewKeydown"; -static const char S_ON_VIEW_CLICK[] = "OnViewClick"; -static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick"; -static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos"; -static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched"; -static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver"; -static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved"; - - -#ifdef __PYWRAPS__ -static const char S_PY_IDAAPI_MODNAME[] = "__main__"; -#else -static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; -#endif - -//------------------------------------------------------------------------ -static ref_t py_cvt_helper_module; -static bool pywraps_initialized = false; - -//--------------------------------------------------------------------------- -// Context structure used by add|del_menu_item() -struct py_add_del_menu_item_ctx -{ - qstring menupath; - PyObject *cb_data; -}; - -//--------------------------------------------------------------------------- -// Context structure used by add|del_idc_hotkey() -struct py_idchotkey_ctx_t -{ - qstring hotkey; - PyObject *pyfunc; -}; - -//--------------------------------------------------------------------------- -// Context structure used by register/unregister timer -struct py_timer_ctx_t -{ - qtimer_t timer_id; - PyObject *pycallback; -}; - -//------------------------------------------------------------------------ -// check if we have a file which is known to be executed automatically -// by SWIG or Python runtime -bool pywraps_check_autoscripts(char *buf, size_t bufsize) -{ - static const char *const exts[] = - { - "py", - "pyc", - "pyd", - "pyo", - "pyw", - }; - - static const char *const fns[] = - { - "swig_runtime_data" SWIG_RUNTIME_VERSION, - "sitecustomize", - "usercustomize" - }; - - for ( size_t ifn=0; ifn < qnumber(fns); ++ifn ) - { - // check for a script or module with several possible extensions - for ( size_t iext=0; iext < qnumber(exts); ++iext ) - { - qsnprintf(buf, bufsize, "%s.%s", fns[ifn], exts[iext]); - if ( qfileexist(buf) ) - return true; - } - // check for a subdirectory under current directory - if ( qfileexist(fns[ifn]) ) - { - qstrncpy(buf, fns[ifn], bufsize); - return true; - } - } - return false; -} - -//------------------------------------------------------------------------ -error_t PyW_CreateIdcException(idc_value_t *res, const char *msg) -{ - // Create exception object - VarObject(res, find_idc_class("exception")); - - // Set the message field - idc_value_t v; - v.set_string(msg); - VarSetAttr(res, "message", &v); - - // Throw exception - return set_qerrno(eExecThrow); -} - -//------------------------------------------------------------------------ -// Calls a Python callable encoded in IDC.pvoid member -static const char idc_py_invoke0_args[] = { VT_PVOID, 0 }; -static error_t idaapi idc_py_invoke0( - idc_value_t *argv, - idc_value_t *res) -{ - PYW_GIL_GET; - PyObject *pyfunc = (PyObject *) argv[0].pvoid; - newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); - - // Report Python error as IDC exception - qstring err; - error_t err_code = eOk; - if ( PyW_GetError(&err) ) - err_code = PyW_CreateIdcException(res, err.c_str()); - return err_code; -} - -//------------------------------------------------------------------------ -// This function must be called on initialization -bool init_pywraps() -{ - if ( pywraps_initialized ) - return true; - - // Take a reference to the idaapi python module - // (We need it to create instances of certain classes) - if ( py_cvt_helper_module == NULL ) - { - // Take a reference to the module so we can create the needed class instances - py_cvt_helper_module = PyW_TryImportModule(S_PY_IDAAPI_MODNAME); - if ( py_cvt_helper_module == NULL ) - return false; - } - - // Register the IDC PyInvoke0 method (helper function for add_idc_hotkey()) - if ( !set_idc_func_ex(S_PYINVOKE0, idc_py_invoke0, idc_py_invoke0_args, 0) ) - return false; - - // IDC opaque class not registered? - if ( get_py_idc_cvt_opaque() == NULL ) - { - // Add the class - idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); - if ( idc_cvt_opaque == NULL ) - return false; - - // Form the dtor name - char dtor_name[MAXSTR]; - qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); - - // Register the dtor function - if ( !set_idc_func_ex(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args, 0) ) - return false; - - // Link the dtor function to the class - set_idc_dtor(idc_cvt_opaque, dtor_name); - } - - pywraps_initialized = true; - return true; -} - -//------------------------------------------------------------------------ -// This function must be called on de-initialization -void deinit_pywraps() -{ - if ( !pywraps_initialized ) - return; - - pywraps_initialized = false; - - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - py_cvt_helper_module = ref_t(); // Deref. - } - - // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) - set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); -} - -//------------------------------------------------------------------------ -// Utility function to create a class instance whose constructor takes zero arguments -ref_t create_idaapi_class_instance0(const char *clsname) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_cls(get_idaapi_attr(clsname)); - if ( py_cls == NULL ) - return ref_t(); - - ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); - if ( PyW_GetError() || py_obj == NULL ) - py_obj = ref_t(); - return py_obj; -} - -//------------------------------------------------------------------------ -// Utility function to create linked class instances -ref_t create_idaapi_linked_class_instance( - const char *clsname, - void *lnk) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_cls(get_idaapi_attr(clsname)); - if ( py_cls == NULL ) - return ref_t(); - - newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); - ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, py_lnk.o, NULL)); - if ( PyW_GetError() || py_obj == NULL ) - py_obj = ref_t(); - return py_obj; -} - -//------------------------------------------------------------------------ -// Gets a class type reference in idaapi -// With the class type reference we can create a new instance of that type -// This function takes a reference to the idaapi module and keeps the reference -ref_t get_idaapi_attr_by_id(const int class_id) -{ - if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) - return ref_t(); - - // Some class names. The array is parallel with the PY_CLSID_xxx consts - static const char *class_names[]= - { - "PyIdc_cvt_int64__", - "object_t", - "PyIdc_cvt_refclass__" - }; - PYW_GIL_CHECK_LOCKED_SCOPE(); - return newref_t(PyObject_GetAttrString(py_cvt_helper_module.o, class_names[class_id])); -} - -//------------------------------------------------------------------------ -// Gets a class reference by name -ref_t get_idaapi_attr(const char *attrname) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return py_cvt_helper_module == NULL - ? ref_t() - : PyW_TryGetAttrString(py_cvt_helper_module.o, attrname); -} - -//------------------------------------------------------------------------ -// Returns a qstring from an object attribute -bool PyW_GetStringAttr( - PyObject *py_obj, - const char *attr_name, - qstring *str) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); - if ( py_attr == NULL ) - return false; - - bool ok = PyString_Check(py_attr.o) != 0; - if ( ok ) - *str = PyString_AsString(py_attr.o); - - return ok; -} - -//------------------------------------------------------------------------ -// Returns an attribute or NULL -// No errors will be set if the attribute did not exist -ref_t PyW_TryGetAttrString(PyObject *py_obj, const char *attr) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t o; - if ( PyObject_HasAttrString(py_obj, attr) ) - o = newref_t(PyObject_GetAttrString(py_obj, attr)); - return o; -} - -//------------------------------------------------------------------------ -// Tries to import a module and clears the exception on failure -ref_t PyW_TryImportModule(const char *name) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result(PyImport_ImportModule(name)); - if ( result == NULL && PyErr_Occurred() ) - PyErr_Clear(); - return result; -} - -//------------------------------------------------------------------------- -// Converts a Python number into an IDC value (32 or 64bits) -// The function will first try to convert the number into a 32bit value -// If the number does not fit then VT_INT64 will be used -// NB: This function cannot properly detect if the Python value should be -// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which -// can fit in a C long but Python creates a PyLong object for it. -// And because of that we are confused as to whether to convert to 32 or 64 -bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = true; - do - { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - { - rc = false; - break; - } - - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - break; - } - // Clear last error - PyErr_Clear(); - // Can be fit into a C unsigned long? - l = (long) PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - break; - } - PyErr_Clear(); - idc_var->set_int64(PyLong_AsLongLong(py_var)); - } while ( false ); - return rc; -} - -//------------------------------------------------------------------------- -// Parses a Python object as a long or long long -bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = true; -#define SETNUM(numexpr, is64_expr) \ - do \ - { \ - if ( num != NULL ) \ - *num = numexpr; \ - if ( is_64 != NULL ) \ - *is_64 = is64_expr; \ - } while ( false ) - - do - { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - { - rc = false; - break; - } - - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(l), false); - break; - } - - // Clear last error - PyErr_Clear(); - - // Can be fit into a C unsigned long? - unsigned long ul = PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(ul), false); - break; - } - PyErr_Clear(); - - // Try to parse as int64 - PY_LONG_LONG ll = PyLong_AsLongLong(py_var); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(ll), true); - break; - } - PyErr_Clear(); - - // Try to parse as uint64 - unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); - PyObject *err = PyErr_Occurred(); - if ( err == NULL ) - { - SETNUM(uint64(ull), true); - break; - } - // Negative number? _And_ it with uint64(-1) - rc = false; - if ( err == PyExc_TypeError ) - { - newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); - newref_t py_num(PyNumber_And(py_var, py_mask.o)); - if ( py_num != NULL && py_mask != NULL ) - { - PyErr_Clear(); - ull = PyLong_AsUnsignedLongLong(py_num.o); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(ull), true); - rc = true; - } - } - } - PyErr_Clear(); - } while ( false ); - return rc; -#undef SETNUM -} - -//------------------------------------------------------------------------- -// Checks if a given object is of sequence type -bool PyW_IsSequenceType(PyObject *obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = true; - do - { - if ( !PySequence_Check(obj) ) - { - rc = false; - break; - } - - Py_ssize_t sz = PySequence_Size(obj); - if ( sz == -1 || PyErr_Occurred() != NULL ) - { - PyErr_Clear(); - rc = false; - break; - } - } while ( false ); - return rc; -} - -//------------------------------------------------------------------------- -// Returns the string representation of an object -bool PyW_ObjectToString(PyObject *obj, qstring *out) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_str(PyObject_Str(obj)); - bool ok = py_str != NULL; - if ( ok ) - *out = PyString_AsString(py_str.o); - else - out->qclear(); - return ok; -} - -//-------------------------------------------------------------------------- -// Checks if a Python error occured and fills the out parameter with the -// exception string -bool PyW_GetError(qstring *out, bool clear_err) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( PyErr_Occurred() == NULL ) - return false; - - // Error occurred but details not needed? - if ( out == NULL ) - { - // Just clear the error - if ( clear_err ) - PyErr_Clear(); - return true; - } - - // Get the exception info - PyObject *err_type, *err_value, *err_traceback, *py_ret(NULL); - PyErr_Fetch(&err_type, &err_value, &err_traceback); - - if ( !clear_err ) - PyErr_Restore(err_type, err_value, err_traceback); - - // Resolve FormatExc() - ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); - - // Helper there? - if ( py_fmtexc != NULL ) - { - // Call helper - py_ret = PyObject_CallFunctionObjArgs( - py_fmtexc.o, - err_type, - err_value, - err_traceback, - NULL); - } - - // Clear the error - if ( clear_err ) - PyErr_Clear(); - - // Helper failed?! - if ( py_ret == NULL ) - { - // Just convert the 'value' part of the original error - py_ret = PyObject_Str(err_value); - } - - // No exception text? - if ( py_ret == NULL ) - { - *out = "IDAPython: unknown error!"; - } - else - { - *out = PyString_AsString(py_ret); - Py_DECREF(py_ret); - } - - if ( clear_err ) - { - Py_XDECREF(err_traceback); - Py_XDECREF(err_value); - Py_XDECREF(err_type); - } - return true; -} - -//------------------------------------------------------------------------- -bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstring s; - if ( !PyW_GetError(&s, clear_err) ) - return false; - - qstrncpy(buf, s.c_str(), bufsz); - return true; -} - -//------------------------------------------------------------------------- -// A loud version of PyGetError() which gets the error and displays it -// This method is used to display errors that occurred in a callback -bool PyW_ShowCbErr(const char *cb_name) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - static qstring err_str; - if ( !PyW_GetError(&err_str) ) - return false; - - warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); - return true; -} - -//--------------------------------------------------------------------------- -void *pyobj_get_clink(PyObject *pyobj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Try to query the link attribute - ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); - void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; - return t; -} - -// -// -//------------------------------------------------------------------------ -/* -# -def parse_command_line(cmdline): - """ - Parses a space separated string (quotes and escape character are supported) - @param cmdline: The command line to parse - @return: A list of strings or None on failure - """ - pass -# -*/ -static PyObject *py_parse_command_line(const char *cmdline) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstrvec_t args; - if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 ) - Py_RETURN_NONE; - return qstrvec2pylist(args); -} - -//------------------------------------------------------------------------- -/* -# -def get_inf_structure(): - """ - Returns the global variable 'inf' (an instance of idainfo structure, see ida.hpp) - """ - pass -# -*/ -idainfo *get_inf_structure(void) -{ - return &inf; -} - -//------------------------------------------------------------------------- -// Declarations from Python.cpp -/* -# -def set_script_timeout(timeout): - """ - Changes the script timeout value. The script wait box dialog will be hidden and shown again when the timeout elapses. - See also L{disable_script_timeout}. - - @param timeout: This value is in seconds. - If this value is set to zero then the script will never timeout. - @return: Returns the old timeout value - """ - pass -# -*/ -int set_script_timeout(int timeout); - -/* -# -def disable_script_timeout(): - """ - Disables the script timeout and hides the script wait box. - Calling L{set_script_timeout} will not have any effects until the script is compiled and executed again - - @return: None - """ - pass -# -*/ -void disable_script_timeout(); - -/* -# -def enable_extlang_python(enable): - """ - Enables or disables Python extlang. - When enabled, all expressions will be evaluated by Python. - @param enable: Set to True to enable, False otherwise - """ - pass -# -*/ -void enable_extlang_python(bool enable); -void enable_python_cli(bool enable); - -/* -# -def RunPythonStatement(stmt): - """ - This is an IDC function exported from the Python plugin. - It is used to evaluate Python statements from IDC. - @param stmt: The statement to evaluate - @return: 0 - on success otherwise a string containing the error - """ - pass -# -*/ - -//--------------------------------------------------------------------------- -// qstrvec_t wrapper (INTERNAL! Don't expose. See py_idaapi.py) -//--------------------------------------------------------------------------- -static bool qstrvec_t_assign(PyObject *self, PyObject *other) -{ - qstrvec_t *lhs = qstrvec_t_get_clink(self); - qstrvec_t *rhs = qstrvec_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - *lhs = *rhs; - return true; -} - -static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - Py_RETURN_NONE; - else - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)&sv->at(idx)); -} - - -static bool qstrvec_t_set( - PyObject *self, - size_t idx, - const char *s) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - return false; - (*sv)[idx] = s; - return true; -} - -static bool qstrvec_t_from_list( - PyObject *self, - PyObject *py_list) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - qstrvec_t *sv = qstrvec_t_get_clink(self); - return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); -} - -static size_t qstrvec_t_size(PyObject *self) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - return sv == NULL ? 0 : sv->size(); -} - -static PyObject *qstrvec_t_get(PyObject *self, size_t idx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - Py_RETURN_NONE; - return PyString_FromString(sv->at(idx).c_str()); -} - -static bool qstrvec_t_add(PyObject *self, const char *s) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL ) - return false; - sv->push_back(s); - return true; -} - -static bool qstrvec_t_clear(PyObject *self, bool qclear) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL ) - return false; - - if ( qclear ) - sv->qclear(); - else - sv->clear(); - - return true; -} - -static bool qstrvec_t_insert( - PyObject *self, - size_t idx, - const char *s) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - return false; - sv->insert(sv->begin() + idx, s); - return true; -} - -static bool qstrvec_t_remove(PyObject *self, size_t idx) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - return false; - - sv->erase(sv->begin()+idx); - return true; -} - -//--------------------------------------------------------------------------- -// - -#endif +#ifndef __PY_IDAAPI__ +#define __PY_IDAAPI__ + +// + +//------------------------------------------------------------------------ +// String constants used +static const char S_PYINVOKE0[] = "_py_invoke0"; +static const char S_PY_SWIEX_CLSNAME[] = "switch_info_ex_t"; +static const char S_PY_OP_T_CLSNAME[] = "op_t"; +static const char S_PROPS[] = "props"; +static const char S_NAME[] = "name"; +static const char S_TITLE[] = "title"; +static const char S_ASM_KEYWORD[] = "asm_keyword"; +static const char S_MENU_NAME[] = "menu_name"; +static const char S_HOTKEY[] = "hotkey"; +static const char S_EMBEDDED[] = "embedded"; +static const char S_POPUP_NAMES[] = "popup_names"; +static const char S_FLAGS[] = "flags"; +static const char S_VALUE_SIZE[] = "value_size"; +static const char S_MAY_CREATE_AT[] = "may_create_at"; +static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; +static const char S_ID[] = "id"; +static const char S_PRINTF[] = "printf"; +static const char S_TEXT_WIDTH[] = "text_width"; +static const char S_SCAN[] = "scan"; +static const char S_ANALYZE[] = "analyze"; +static const char S_CBSIZE[] = "cbsize"; +static const char S_ON_CLICK[] = "OnClick"; +static const char S_ON_CLOSE[] = "OnClose"; +static const char S_ON_DBL_CLICK[] = "OnDblClick"; +static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; +static const char S_ON_KEYDOWN[] = "OnKeydown"; +static const char S_ON_COMPLETE_LINE[] = "OnCompleteLine"; +static const char S_ON_CREATE[] = "OnCreate"; +static const char S_ON_POPUP[] = "OnPopup"; +static const char S_ON_HINT[] = "OnHint"; +static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; +static const char S_ON_EDIT_LINE[] = "OnEditLine"; +static const char S_ON_INSERT_LINE[] = "OnInsertLine"; +static const char S_ON_GET_LINE[] = "OnGetLine"; +static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; +static const char S_ON_REFRESH[] = "OnRefresh"; +static const char S_ON_REFRESHED[] = "OnRefreshed"; +static const char S_ON_EXECUTE_LINE[] = "OnExecuteLine"; +static const char S_ON_SELECT_LINE[] = "OnSelectLine"; +static const char S_ON_SELECTION_CHANGE[] = "OnSelectionChange"; +static const char S_ON_COMMAND[] = "OnCommand"; +static const char S_ON_GET_ICON[] = "OnGetIcon"; +static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; +static const char S_ON_GET_SIZE[] = "OnGetSize"; +static const char S_ON_GETTEXT[] = "OnGetText"; +static const char S_ON_ACTIVATE[] = "OnActivate"; +static const char S_ON_DEACTIVATE[] = "OnDeactivate"; +static const char S_ON_SELECT[] = "OnSelect"; +static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; +static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; +static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; +static const char S_M_EDGES[] = "_edges"; +static const char S_M_NODES[] = "_nodes"; +static const char S_M_THIS[] = "_this"; +static const char S_M_TITLE[] = "_title"; +static const char S_CLINK_NAME[] = "__clink__"; +static const char S_ON_VIEW_ACTIVATED[] = "OnViewActivated"; +static const char S_ON_VIEW_DEACTIVATED[] = "OnViewDeactivated"; +static const char S_ON_VIEW_KEYDOWN[] = "OnViewKeydown"; +static const char S_ON_VIEW_CLICK[] = "OnViewClick"; +static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick"; +static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos"; +static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched"; +static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver"; +static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved"; + + +#ifdef __PYWRAPS__ +static const char S_PY_IDAAPI_MODNAME[] = "__main__"; +#else +static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; +#endif + +//------------------------------------------------------------------------ +static ref_t py_cvt_helper_module; +static bool pywraps_initialized = false; + +//--------------------------------------------------------------------------- +// Context structure used by add|del_menu_item() +struct py_add_del_menu_item_ctx +{ + qstring menupath; + PyObject *cb_data; +}; + +//--------------------------------------------------------------------------- +// Context structure used by add|del_idc_hotkey() +struct py_idchotkey_ctx_t +{ + qstring hotkey; + PyObject *pyfunc; +}; + +//--------------------------------------------------------------------------- +// Context structure used by register/unregister timer +struct py_timer_ctx_t +{ + qtimer_t timer_id; + PyObject *pycallback; +}; + +//------------------------------------------------------------------------ +// check if we have a file which is known to be executed automatically +// by SWIG or Python runtime +bool pywraps_check_autoscripts(char *buf, size_t bufsize) +{ + static const char *const exts[] = + { + "py", + "pyc", + "pyd", + "pyo", + "pyw", + }; + + static const char *const fns[] = + { + "swig_runtime_data" SWIG_RUNTIME_VERSION, + "sitecustomize", + "usercustomize" + }; + + for ( size_t ifn=0; ifn < qnumber(fns); ++ifn ) + { + // check for a script or module with several possible extensions + for ( size_t iext=0; iext < qnumber(exts); ++iext ) + { + qsnprintf(buf, bufsize, "%s.%s", fns[ifn], exts[iext]); + if ( qfileexist(buf) ) + return true; + } + // check for a subdirectory under current directory + if ( qfileexist(fns[ifn]) ) + { + qstrncpy(buf, fns[ifn], bufsize); + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +error_t PyW_CreateIdcException(idc_value_t *res, const char *msg) +{ + // Create exception object + VarObject(res, find_idc_class("exception")); + + // Set the message field + idc_value_t v; + v.set_string(msg); + VarSetAttr(res, "message", &v); + + // Throw exception + return set_qerrno(eExecThrow); +} + +//------------------------------------------------------------------------ +// Calls a Python callable encoded in IDC.pvoid member +static const char idc_py_invoke0_args[] = { VT_PVOID, 0 }; +static error_t idaapi idc_py_invoke0( + idc_value_t *argv, + idc_value_t *res) +{ + PYW_GIL_GET; + PyObject *pyfunc = (PyObject *) argv[0].pvoid; + newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); + + // Report Python error as IDC exception + qstring err; + error_t err_code = eOk; + if ( PyW_GetError(&err) ) + err_code = PyW_CreateIdcException(res, err.c_str()); + return err_code; +} + +//------------------------------------------------------------------------ +// This function must be called on initialization +bool init_pywraps() +{ + if ( pywraps_initialized ) + return true; + + // Take a reference to the idaapi python module + // (We need it to create instances of certain classes) + if ( py_cvt_helper_module == NULL ) + { + // Take a reference to the module so we can create the needed class instances + py_cvt_helper_module = PyW_TryImportModule(S_PY_IDAAPI_MODNAME); + if ( py_cvt_helper_module == NULL ) + return false; + } + + // Register the IDC PyInvoke0 method (helper function for add_idc_hotkey()) + if ( !set_idc_func_ex(S_PYINVOKE0, idc_py_invoke0, idc_py_invoke0_args, 0) ) + return false; + + // IDC opaque class not registered? + if ( get_py_idc_cvt_opaque() == NULL ) + { + // Add the class + idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); + if ( idc_cvt_opaque == NULL ) + return false; + + // Form the dtor name + char dtor_name[MAXSTR]; + qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); + + // Register the dtor function + if ( !set_idc_func_ex(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args, 0) ) + return false; + + // Link the dtor function to the class + set_idc_dtor(idc_cvt_opaque, dtor_name); + } + + pywraps_initialized = true; + return true; +} + +//------------------------------------------------------------------------ +// This function must be called on de-initialization +void deinit_pywraps() +{ + if ( !pywraps_initialized ) + return; + + pywraps_initialized = false; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_cvt_helper_module = ref_t(); // Deref. + } + + // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) + set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); +} + +//------------------------------------------------------------------------ +// Utility function to create a class instance whose constructor takes zero arguments +ref_t create_idaapi_class_instance0(const char *clsname) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); + if ( py_cls == NULL ) + return ref_t(); + + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); + if ( PyW_GetError() || py_obj == NULL ) + py_obj = ref_t(); + return py_obj; +} + +//------------------------------------------------------------------------ +// Utility function to create linked class instances +ref_t create_idaapi_linked_class_instance( + const char *clsname, + void *lnk) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); + if ( py_cls == NULL ) + return ref_t(); + + newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, py_lnk.o, NULL)); + if ( PyW_GetError() || py_obj == NULL ) + py_obj = ref_t(); + return py_obj; +} + +//------------------------------------------------------------------------ +// Gets a class type reference in idaapi +// With the class type reference we can create a new instance of that type +// This function takes a reference to the idaapi module and keeps the reference +ref_t get_idaapi_attr_by_id(const int class_id) +{ + if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) + return ref_t(); + + // Some class names. The array is parallel with the PY_CLSID_xxx consts + static const char *class_names[]= + { + "PyIdc_cvt_int64__", + "object_t", + "PyIdc_cvt_refclass__" + }; + PYW_GIL_CHECK_LOCKED_SCOPE(); + return newref_t(PyObject_GetAttrString(py_cvt_helper_module.o, class_names[class_id])); +} + +//------------------------------------------------------------------------ +// Gets a class reference by name +ref_t get_idaapi_attr(const char *attrname) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return py_cvt_helper_module == NULL + ? ref_t() + : PyW_TryGetAttrString(py_cvt_helper_module.o, attrname); +} + +//------------------------------------------------------------------------ +// Returns a qstring from an object attribute +bool PyW_GetStringAttr( + PyObject *py_obj, + const char *attr_name, + qstring *str) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); + if ( py_attr == NULL ) + return false; + + bool ok = PyString_Check(py_attr.o) != 0; + if ( ok ) + *str = PyString_AsString(py_attr.o); + + return ok; +} + +//------------------------------------------------------------------------ +// Returns an attribute or NULL +// No errors will be set if the attribute did not exist +ref_t PyW_TryGetAttrString(PyObject *py_obj, const char *attr) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t o; + if ( PyObject_HasAttrString(py_obj, attr) ) + o = newref_t(PyObject_GetAttrString(py_obj, attr)); + return o; +} + +//------------------------------------------------------------------------ +// Tries to import a module and clears the exception on failure +ref_t PyW_TryImportModule(const char *name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyImport_ImportModule(name)); + if ( result == NULL && PyErr_Occurred() ) + PyErr_Clear(); + return result; +} + +//------------------------------------------------------------------------- +// Converts a Python number into an IDC value (32 or 64bits) +// The function will first try to convert the number into a 32bit value +// If the number does not fit then VT_INT64 will be used +// NB: This function cannot properly detect if the Python value should be +// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which +// can fit in a C long but Python creates a PyLong object for it. +// And because of that we are confused as to whether to convert to 32 or 64 +bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do + { + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + { + rc = false; + break; + } + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + l = (long) PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + PyErr_Clear(); + idc_var->set_int64(PyLong_AsLongLong(py_var)); + } while ( false ); + return rc; +} + +//------------------------------------------------------------------------- +// Parses a Python object as a long or long long +bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; +#define SETNUM(numexpr, is64_expr) \ + do \ + { \ + if ( num != NULL ) \ + *num = numexpr; \ + if ( is_64 != NULL ) \ + *is_64 = is64_expr; \ + } while ( false ) + + do + { + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + { + rc = false; + break; + } + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(l), false); + break; + } + + // Clear last error + PyErr_Clear(); + + // Can be fit into a C unsigned long? + unsigned long ul = PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ul), false); + break; + } + PyErr_Clear(); + + // Try to parse as int64 + PY_LONG_LONG ll = PyLong_AsLongLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ll), true); + break; + } + PyErr_Clear(); + + // Try to parse as uint64 + unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); + PyObject *err = PyErr_Occurred(); + if ( err == NULL ) + { + SETNUM(uint64(ull), true); + break; + } + // Negative number? _And_ it with uint64(-1) + rc = false; + if ( err == PyExc_TypeError ) + { + newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); + newref_t py_num(PyNumber_And(py_var, py_mask.o)); + if ( py_num != NULL && py_mask != NULL ) + { + PyErr_Clear(); + ull = PyLong_AsUnsignedLongLong(py_num.o); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ull), true); + rc = true; + } + } + } + PyErr_Clear(); + } while ( false ); + return rc; +#undef SETNUM +} + +//------------------------------------------------------------------------- +// Checks if a given object is of sequence type +bool PyW_IsSequenceType(PyObject *obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do + { + if ( !PySequence_Check(obj) ) + { + rc = false; + break; + } + + Py_ssize_t sz = PySequence_Size(obj); + if ( sz == -1 || PyErr_Occurred() != NULL ) + { + PyErr_Clear(); + rc = false; + break; + } + } while ( false ); + return rc; +} + +//------------------------------------------------------------------------- +// Returns the string representation of an object +bool PyW_ObjectToString(PyObject *obj, qstring *out) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_str(PyObject_Str(obj)); + bool ok = py_str != NULL; + if ( ok ) + *out = PyString_AsString(py_str.o); + else + out->qclear(); + return ok; +} + +//-------------------------------------------------------------------------- +// Checks if a Python error occured and fills the out parameter with the +// exception string +bool PyW_GetError(qstring *out, bool clear_err) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( PyErr_Occurred() == NULL ) + return false; + + // Error occurred but details not needed? + if ( out == NULL ) + { + // Just clear the error + if ( clear_err ) + PyErr_Clear(); + return true; + } + + // Get the exception info + PyObject *err_type, *err_value, *err_traceback, *py_ret(NULL); + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + if ( !clear_err ) + PyErr_Restore(err_type, err_value, err_traceback); + + // Resolve FormatExc() + ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); + + // Helper there? + if ( py_fmtexc != NULL ) + { + // Call helper + py_ret = PyObject_CallFunctionObjArgs( + py_fmtexc.o, + err_type, + err_value, + err_traceback, + NULL); + } + + // Clear the error + if ( clear_err ) + PyErr_Clear(); + + // Helper failed?! + if ( py_ret == NULL ) + { + // Just convert the 'value' part of the original error + py_ret = PyObject_Str(err_value); + } + + // No exception text? + if ( py_ret == NULL ) + { + *out = "IDAPython: unknown error!"; + } + else + { + *out = PyString_AsString(py_ret); + Py_DECREF(py_ret); + } + + if ( clear_err ) + { + Py_XDECREF(err_traceback); + Py_XDECREF(err_value); + Py_XDECREF(err_type); + } + return true; +} + +//------------------------------------------------------------------------- +bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstring s; + if ( !PyW_GetError(&s, clear_err) ) + return false; + + qstrncpy(buf, s.c_str(), bufsz); + return true; +} + +//------------------------------------------------------------------------- +// A loud version of PyGetError() which gets the error and displays it +// This method is used to display errors that occurred in a callback +bool PyW_ShowCbErr(const char *cb_name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + static qstring err_str; + if ( !PyW_GetError(&err_str) ) + return false; + + warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); + return true; +} + +//--------------------------------------------------------------------------- +void *pyobj_get_clink(PyObject *pyobj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Try to query the link attribute + ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); + void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; + return t; +} + +// +// +//------------------------------------------------------------------------ +/* +# +def parse_command_line(cmdline): + """ + Parses a space separated string (quotes and escape character are supported) + @param cmdline: The command line to parse + @return: A list of strings or None on failure + """ + pass +# +*/ +static PyObject *py_parse_command_line(const char *cmdline) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstrvec_t args; + if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 ) + Py_RETURN_NONE; + return qstrvec2pylist(args); +} + +//------------------------------------------------------------------------- +/* +# +def get_inf_structure(): + """ + Returns the global variable 'inf' (an instance of idainfo structure, see ida.hpp) + """ + pass +# +*/ +idainfo *get_inf_structure(void) +{ + return &inf; +} + +//------------------------------------------------------------------------- +// Declarations from Python.cpp +/* +# +def set_script_timeout(timeout): + """ + Changes the script timeout value. The script wait box dialog will be hidden and shown again when the timeout elapses. + See also L{disable_script_timeout}. + + @param timeout: This value is in seconds. + If this value is set to zero then the script will never timeout. + @return: Returns the old timeout value + """ + pass +# +*/ +int set_script_timeout(int timeout); + +/* +# +def disable_script_timeout(): + """ + Disables the script timeout and hides the script wait box. + Calling L{set_script_timeout} will not have any effects until the script is compiled and executed again + + @return: None + """ + pass +# +*/ +void disable_script_timeout(); + +/* +# +def enable_extlang_python(enable): + """ + Enables or disables Python extlang. + When enabled, all expressions will be evaluated by Python. + @param enable: Set to True to enable, False otherwise + """ + pass +# +*/ +void enable_extlang_python(bool enable); +void enable_python_cli(bool enable); + +/* +# +def RunPythonStatement(stmt): + """ + This is an IDC function exported from the Python plugin. + It is used to evaluate Python statements from IDC. + @param stmt: The statement to evaluate + @return: 0 - on success otherwise a string containing the error + """ + pass +# +*/ + +//--------------------------------------------------------------------------- +// qstrvec_t wrapper (INTERNAL! Don't expose. See py_idaapi.py) +//--------------------------------------------------------------------------- +static bool qstrvec_t_assign(PyObject *self, PyObject *other) +{ + qstrvec_t *lhs = qstrvec_t_get_clink(self); + qstrvec_t *rhs = qstrvec_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + *lhs = *rhs; + return true; +} + +static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + Py_RETURN_NONE; + else + return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)&sv->at(idx)); +} + + +static bool qstrvec_t_set( + PyObject *self, + size_t idx, + const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + (*sv)[idx] = s; + return true; +} + +static bool qstrvec_t_from_list( + PyObject *self, + PyObject *py_list) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t *sv = qstrvec_t_get_clink(self); + return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); +} + +static size_t qstrvec_t_size(PyObject *self) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + return sv == NULL ? 0 : sv->size(); +} + +static PyObject *qstrvec_t_get(PyObject *self, size_t idx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + Py_RETURN_NONE; + return PyString_FromString(sv->at(idx).c_str()); +} + +static bool qstrvec_t_add(PyObject *self, const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL ) + return false; + sv->push_back(s); + return true; +} + +static bool qstrvec_t_clear(PyObject *self, bool qclear) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL ) + return false; + + if ( qclear ) + sv->qclear(); + else + sv->clear(); + + return true; +} + +static bool qstrvec_t_insert( + PyObject *self, + size_t idx, + const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + sv->insert(sv->begin() + idx, s); + return true; +} + +static bool qstrvec_t_remove(PyObject *self, size_t idx) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + + sv->erase(sv->begin()+idx); + return true; +} + +//--------------------------------------------------------------------------- +// + +#endif diff --git a/pywraps/py_idaapi.py b/pywraps/py_idaapi.py index 6071ea3b..c7204dd7 100644 --- a/pywraps/py_idaapi.py +++ b/pywraps/py_idaapi.py @@ -1,605 +1,605 @@ -# ----------------------------------------------------------------------- -try: - import pywraps - pywraps_there = True -except: - pywraps_there = False - -import _idaapi -import random -import operator -import datetime - -# - -import struct -import traceback -import os -import sys -import bisect -import __builtin__ -import imp - -def require(modulename, package=None): - """ - Load, or reload a module. - - When under heavy development, a user's tool might consist of multiple - modules. If those are imported using the standard 'import' mechanism, - there is no guarantee that the Python implementation will re-read - and re-evaluate the module's Python code. In fact, it usually doesn't. - What should be done instead is 'reload()'-ing that module. - - This is a simple helper function that will do just that: In case the - module doesn't exist, it 'import's it, and if it does exist, - 'reload()'s it. - - For more information, see: . - """ - if modulename in sys.modules.keys(): - reload(sys.modules[modulename]) - else: - import importlib - import inspect - m = importlib.import_module(modulename, package) - frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1] - importer_module = inspect.getmodule(frame_obj) - if importer_module is None: # No importer module; called from command line - importer_module = sys.modules['__main__'] - setattr(importer_module, modulename, m) - sys.modules[modulename] = m - -# ----------------------------------------------------------------------- - -# Seek constants -SEEK_SET = 0 # from the file start -SEEK_CUR = 1 # from the current position -SEEK_END = 2 # from the file end - -# Plugin constants -PLUGIN_MOD = 0x0001 -PLUGIN_DRAW = 0x0002 -PLUGIN_SEG = 0x0004 -PLUGIN_UNL = 0x0008 -PLUGIN_HIDE = 0x0010 -PLUGIN_DBG = 0x0020 -PLUGIN_PROC = 0x0040 -PLUGIN_FIX = 0x0080 -PLUGIN_SKIP = 0 -PLUGIN_OK = 1 -PLUGIN_KEEP = 2 - -# PyIdc conversion object IDs -PY_ICID_INT64 = 0 -"""int64 object""" -PY_ICID_BYREF = 1 -"""byref object""" -PY_ICID_OPAQUE = 2 -"""opaque object""" - -# Step trace options (used with set_step_trace_options()) -ST_OVER_DEBUG_SEG = 0x01 -"""step tracing will be disabled when IP is in a debugger segment""" - -ST_OVER_LIB_FUNC = 0x02 -"""step tracing will be disabled when IP is in a library function""" - -# ----------------------------------------------------------------------- -class pyidc_opaque_object_t(object): - """This is the base class for all Python<->IDC opaque objects""" - __idc_cvt_id__ = PY_ICID_OPAQUE - -# ----------------------------------------------------------------------- -class py_clinked_object_t(pyidc_opaque_object_t): - """ - This is a utility and base class for C linked objects - """ - def __init__(self, lnk = None): - # static link: if a link was provided - self.__static_clink__ = True if lnk else False - - # Create link if it was not provided - self.__clink__ = lnk if lnk else self._create_clink() - - def __del__(self): - """Delete the link upon object destruction (only if not static)""" - self._free() - - def _free(self): - """Explicitly delete the link (only if not static)""" - if not self.__static_clink__ and self.__clink__ is not None: - self._del_clink(self.__clink__) - self.__clink__ = None - - def copy(self): - """Returns a new copy of this class""" - - # Create an unlinked instance - inst = self.__class__() - - # Assign self to the new instance - inst.assign(self) - - return inst - - # - # Methods to be overwritten - # - def _create_clink(self): - """ - Overwrite me. - Creates a new clink - @return: PyCObject representing the C link - """ - pass - - def _del_clink(self, lnk): - """ - Overwrite me. - This method deletes the link - """ - pass - - def _get_clink_ptr(self): - """ - Overwrite me. - Returns the C link pointer as a 64bit number - """ - pass - - def assign(self, other): - """ - Overwrite me. - This method allows you to assign an instance contents to anothers - @return: Boolean - """ - pass - - clink = property(lambda self: self.__clink__) - """Returns the C link as a PyObject""" - - clink_ptr = property(lambda self: self._get_clink_ptr()) - """Returns the C link pointer as a number""" - -# ----------------------------------------------------------------------- -class object_t(object): - """Helper class used to initialize empty objects""" - def __init__(self, **kwds): - self.__dict__ = kwds - - def __getitem__(self, idx): - """Allow access to object attributes by index (like dictionaries)""" - return getattr(self, idx) - -# ----------------------------------------------------------------------- -def _bounded_getitem_iterator(self): - """Helper function, to be set as __iter__ method for qvector-, or array-based classes.""" - for i in range(len(self)): - yield self[i] - -# ----------------------------------------------------------------------- -class plugin_t(pyidc_opaque_object_t): - """Base class for all scripted plugins.""" - pass - -# ----------------------------------------------------------------------- -class pyidc_cvt_helper__(object): - """ - This is a special helper object that helps detect which kind - of object is this python object wrapping and how to convert it - back and from IDC. - This object is characterized by its special attribute and its value - """ - def __init__(self, cvt_id, value): - self.__idc_cvt_id__ = cvt_id - self.value = value - - def __set_value(self, v): - self.__idc_cvt_value__ = v - def __get_value(self): - return self.__idc_cvt_value__ - value = property(__get_value, __set_value) - -# ----------------------------------------------------------------------- -class PyIdc_cvt_int64__(pyidc_cvt_helper__): - """Helper class for explicitly representing VT_INT64 values""" - - def __init__(self, v): - # id = 0 = int64 object - super(self.__class__, self).__init__(PY_ICID_INT64, v) - - # operation table - __op_table = \ - { - 0: lambda a, b: a + b, - 1: lambda a, b: a - b, - 2: lambda a, b: a * b, - 3: lambda a, b: a / b - } - # carries the operation given its number - def __op(self, op_n, other, rev=False): - a = self.value - # other operand of same type? then take its value field - if type(other) == type(self): - b = other.value - else: - b = other - if rev: - t = a - a = b - b = t - # construct a new object and return as the result - return self.__class__(self.__op_table[op_n](a, b)) - - # overloaded operators - def __add__(self, other): return self.__op(0, other) - def __sub__(self, other): return self.__op(1, other) - def __mul__(self, other): return self.__op(2, other) - def __div__(self, other): return self.__op(3, other) - def __radd__(self, other): return self.__op(0, other, True) - def __rsub__(self, other): return self.__op(1, other, True) - def __rmul__(self, other): return self.__op(2, other, True) - def __rdiv__(self, other): return self.__op(3, other, True) - -# ----------------------------------------------------------------------- -# qstrvec_t clinked object -class _qstrvec_t(py_clinked_object_t): - """ - WARNING: It is very unlikely an IDAPython user should ever, ever - have to use this type. It should only be used for IDAPython internals. - - For example, in py_askusingform.py, we ctypes-expose to the IDA - kernel & UI a qstrvec instance, in case a DropdownListControl is - constructed. - That's because that's what AskUsingForm expects, and we have no - choice but to make a DropdownListControl hold a qstrvec_t. - This is, afaict, the only situation where a Python - _qstrvec_t is required. - """ - - def __init__(self, items=None): - py_clinked_object_t.__init__(self) - # Populate the list if needed - if items: - self.from_list(items) - - def _create_clink(self): - return _idaapi.qstrvec_t_create() - - def _del_clink(self, lnk): - return _idaapi.qstrvec_t_destroy(lnk) - - def _get_clink_ptr(self): - return _idaapi.qstrvec_t_get_clink_ptr(self) - - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.qstrvec_t_assign(self, other) - - def __setitem__(self, idx, s): - """Sets string at the given index""" - return _idaapi.qstrvec_t_set(self, idx, s) - - def __getitem__(self, idx): - """Gets the string at the given index""" - return _idaapi.qstrvec_t_get(self, idx) - - def __get_size(self): - return _idaapi.qstrvec_t_size(self) - - size = property(__get_size) - """Returns the count of elements""" - - def addressof(self, idx): - """Returns the address (as number) of the qstring at the given index""" - return _idaapi.qstrvec_t_addressof(self, idx) - - def add(self, s): - """Add a string to the vector""" - return _idaapi.qstrvec_t_add(self, s) - - def from_list(self, lst): - """Populates the vector from a Python string list""" - return _idaapi.qstrvec_t_from_list(self, lst) - - def clear(self, qclear=False): - """ - Clears all strings from the vector. - @param qclear: Just reset the size but do not actually free the memory - """ - return _idaapi.qstrvec_t_clear(self, qclear) - - def insert(self, idx, s): - """Insert a string into the vector""" - return _idaapi.qstrvec_t_insert(self, idx, s) - - def remove(self, idx): - """Removes a string from the vector""" - return _idaapi.qstrvec_t_remove(self, idx) - -# ----------------------------------------------------------------------- -class PyIdc_cvt_refclass__(pyidc_cvt_helper__): - """Helper class for representing references to immutable objects""" - def __init__(self, v): - # id = one = byref object - super(self.__class__, self).__init__(PY_ICID_BYREF, v) - - def cstr(self): - """Returns the string as a C string (up to the zero termination)""" - return as_cstr(self.value) - -# ----------------------------------------------------------------------- -def as_cstr(val): - """ - Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) - It scans for the first \x00 and returns the string value up to that point. - """ - if isinstance(val, PyIdc_cvt_refclass__): - val = val.value - - n = val.find('\x00') - return val if n == -1 else val[:n] - -# ----------------------------------------------------------------------- -def as_unicode(s): - """Convenience function to convert a string into appropriate unicode format""" - # use UTF16 big/little endian, depending on the environment? - return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) - -# ----------------------------------------------------------------------- -def as_uint32(v): - """Returns a number as an unsigned int32 number""" - return v & 0xffffffff - -# ----------------------------------------------------------------------- -def as_int32(v): - """Returns a number as a signed int32 number""" - return -((~v & 0xffffffff)+1) - -# ----------------------------------------------------------------------- -def as_signed(v, nbits = 32): - """ - Returns a number as signed. The number of bits are specified by the user. - The MSB holds the sign. - """ - return -(( ~v & ((1 << nbits)-1) ) + 1) if v & (1 << nbits-1) else v - -# ---------------------------------------------------------------------- -def copy_bits(v, s, e=-1): - """ - Copy bits from a value - @param v: the value - @param s: starting bit (0-based) - @param e: ending bit - """ - # end-bit not specified? use start bit (thus extract one bit) - if e == -1: - e = s - # swap start and end if start > end - if s > e: - e, s = s, e - - mask = ~(((1 << (e-s+1))-1) << s) - - return (v & mask) >> s - -# ---------------------------------------------------------------------- -__struct_unpack_table = { - 1: ('b', 'B'), - 2: ('h', 'H'), - 4: ('l', 'L'), - 8: ('q', 'Q') -} - -# ---------------------------------------------------------------------- -def struct_unpack(buffer, signed = False, offs = 0): - """ - Unpack a buffer given its length and offset using struct.unpack_from(). - This function will know how to unpack the given buffer by using the lookup table '__struct_unpack_table' - If the buffer is of unknown length then None is returned. Otherwise the unpacked value is returned. - """ - # Supported length? - n = len(buffer) - if n not in __struct_unpack_table: - return None - # Conver to number - signed = 1 if signed else 0 - - # Unpack - return struct.unpack_from(__struct_unpack_table[n][signed], buffer, offs)[0] - - -# ------------------------------------------------------------ -def IDAPython_ExecSystem(cmd): - """ - Executes a command with popen(). - """ - try: - f = os.popen(cmd, "r") - s = ''.join(f.readlines()) - f.close() - return s - except Exception as e: - return "%s\n%s" % (str(e), traceback.format_exc()) - -# ------------------------------------------------------------ -def IDAPython_FormatExc(etype, value, tb, limit=None): - """ - This function is used to format an exception given the - values returned by a PyErr_Fetch() - """ - try: - return ''.join(traceback.format_exception(etype, value, tb, limit)) - except: - return str(value) - - -# ------------------------------------------------------------ -def IDAPython_ExecScript(script, g): - """ - Run the specified script. - It also addresses http://code.google.com/p/idapython/issues/detail?id=42 - - This function is used by the low-level plugin code. - """ - scriptpath = os.path.dirname(script) - if len(scriptpath) and scriptpath not in sys.path: - sys.path.append(scriptpath) - - argv = sys.argv - sys.argv = [ script ] - - # Adjust the __file__ path in the globals we pass to the script - old__file__ = g['__file__'] if '__file__' in g else '' - g['__file__'] = script - - try: - execfile(script, g) - PY_COMPILE_ERR = None - except Exception as e: - PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) - print(PY_COMPILE_ERR) - finally: - # Restore state - g['__file__'] = old__file__ - sys.argv = argv - - return PY_COMPILE_ERR - -# ------------------------------------------------------------ -def IDAPython_LoadProcMod(script, g): - """ - Load processor module. - """ - pname = g['__name__'] if g and g.has_key("__name__") else '__main__' - parent = sys.modules[pname] - - scriptpath, scriptname = os.path.split(script) - if len(scriptpath) and scriptpath not in sys.path: - sys.path.append(scriptpath) - - procmod_name = os.path.splitext(scriptname)[0] - procobj = None - fp = None - try: - fp, pathname, description = imp.find_module(procmod_name) - procmod = imp.load_module(procmod_name, fp, pathname, description) - if parent: - setattr(parent, procmod_name, procmod) - # export attrs from parent to processor module - parent_attrs = getattr(parent, '__all__', - (attr for attr in dir(parent) if not attr.startswith('_'))) - for pa in parent_attrs: - setattr(procmod, pa, getattr(parent, pa)) - # instantiate processor object - if getattr(procmod, 'PROCESSOR_ENTRY', None): - procobj = procmod.PROCESSOR_ENTRY() - PY_COMPILE_ERR = None - except Exception as e: - PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) - print(PY_COMPILE_ERR) - finally: - if fp: fp.close() - - sys.path.remove(scriptpath) - - return (PY_COMPILE_ERR, procobj) - -# ------------------------------------------------------------ -def IDAPython_UnLoadProcMod(script, g): - """ - Unload processor module. - """ - pname = g['__name__'] if g and g.has_key("__name__") else '__main__' - parent = sys.modules[pname] - - scriptname = os.path.split(script)[1] - procmod_name = os.path.splitext(scriptname)[0] - if getattr(parent, procmod_name, None): - delattr(parent, procmod_name) - del sys.modules[procmod_name] - PY_COMPILE_ERR = None - return PY_COMPILE_ERR - -# ---------------------------------------------------------------------- -class __IDAPython_Completion_Util(object): - """Internal utility class for auto-completion support""" - def __init__(self): - self.n = 0 - self.completion = None - self.lastmodule = None - - @staticmethod - def parse_identifier(line, prefix, prefix_start): - """ - Parse a line and extracts identifier - """ - id_start = prefix_start - while id_start > 0: - ch = line[id_start] - if not ch.isalpha() and ch != '.' and ch != '_': - id_start += 1 - break - id_start -= 1 - - return line[id_start:prefix_start + len(prefix)] - - @staticmethod - def dir_of(m, prefix): - return [x for x in dir(m) if x.startswith(prefix)] - - @classmethod - def get_completion(cls, id, prefix): - try: - m = sys.modules['__main__'] - - parts = id.split('.') - c = len(parts) - - for i in xrange(0, c-1): - m = getattr(m, parts[i]) - except Exception as e: - return (None, None) - else: - # search in the module - completion = cls.dir_of(m, prefix) - - # no completion found? looking from the global scope? then try the builtins - if not completion and c == 1: - completion = cls.dir_of(__builtin__, prefix) - - return (m, completion) if completion else (None, None) - - def __call__(self, prefix, n, line, prefix_start): - if n == 0: - self.n = n - id = self.parse_identifier(line, prefix, prefix_start) - self.lastmodule, self.completion = self.get_completion(id, prefix) - - if self.completion is None or n >= len(self.completion): - return None - - s = self.completion[n] - try: - attr = getattr(self.lastmodule, s) - # Is it callable? - if callable(attr): - return s + ("" if line.startswith("?") else "(") - # Is it iterable? - elif isinstance(attr, basestring) or getattr(attr, '__iter__', False): - return s + "[" - except: - pass - - return s - -# Instantiate an IDAPython command completion object (for use with IDA's CLI bar) -IDAPython_Completion = __IDAPython_Completion_Util() - -def _listify_types(*classes): - for cls in classes: - cls.__getitem__ = cls.at - cls.__len__ = cls.size - cls.__iter__ = _bounded_getitem_iterator - -# +# ----------------------------------------------------------------------- +try: + import pywraps + pywraps_there = True +except: + pywraps_there = False + +import _idaapi +import random +import operator +import datetime + +# + +import struct +import traceback +import os +import sys +import bisect +import __builtin__ +import imp + +def require(modulename, package=None): + """ + Load, or reload a module. + + When under heavy development, a user's tool might consist of multiple + modules. If those are imported using the standard 'import' mechanism, + there is no guarantee that the Python implementation will re-read + and re-evaluate the module's Python code. In fact, it usually doesn't. + What should be done instead is 'reload()'-ing that module. + + This is a simple helper function that will do just that: In case the + module doesn't exist, it 'import's it, and if it does exist, + 'reload()'s it. + + For more information, see: . + """ + if modulename in sys.modules.keys(): + reload(sys.modules[modulename]) + else: + import importlib + import inspect + m = importlib.import_module(modulename, package) + frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1] + importer_module = inspect.getmodule(frame_obj) + if importer_module is None: # No importer module; called from command line + importer_module = sys.modules['__main__'] + setattr(importer_module, modulename, m) + sys.modules[modulename] = m + +# ----------------------------------------------------------------------- + +# Seek constants +SEEK_SET = 0 # from the file start +SEEK_CUR = 1 # from the current position +SEEK_END = 2 # from the file end + +# Plugin constants +PLUGIN_MOD = 0x0001 +PLUGIN_DRAW = 0x0002 +PLUGIN_SEG = 0x0004 +PLUGIN_UNL = 0x0008 +PLUGIN_HIDE = 0x0010 +PLUGIN_DBG = 0x0020 +PLUGIN_PROC = 0x0040 +PLUGIN_FIX = 0x0080 +PLUGIN_SKIP = 0 +PLUGIN_OK = 1 +PLUGIN_KEEP = 2 + +# PyIdc conversion object IDs +PY_ICID_INT64 = 0 +"""int64 object""" +PY_ICID_BYREF = 1 +"""byref object""" +PY_ICID_OPAQUE = 2 +"""opaque object""" + +# Step trace options (used with set_step_trace_options()) +ST_OVER_DEBUG_SEG = 0x01 +"""step tracing will be disabled when IP is in a debugger segment""" + +ST_OVER_LIB_FUNC = 0x02 +"""step tracing will be disabled when IP is in a library function""" + +# ----------------------------------------------------------------------- +class pyidc_opaque_object_t(object): + """This is the base class for all Python<->IDC opaque objects""" + __idc_cvt_id__ = PY_ICID_OPAQUE + +# ----------------------------------------------------------------------- +class py_clinked_object_t(pyidc_opaque_object_t): + """ + This is a utility and base class for C linked objects + """ + def __init__(self, lnk = None): + # static link: if a link was provided + self.__static_clink__ = True if lnk else False + + # Create link if it was not provided + self.__clink__ = lnk if lnk else self._create_clink() + + def __del__(self): + """Delete the link upon object destruction (only if not static)""" + self._free() + + def _free(self): + """Explicitly delete the link (only if not static)""" + if not self.__static_clink__ and self.__clink__ is not None: + self._del_clink(self.__clink__) + self.__clink__ = None + + def copy(self): + """Returns a new copy of this class""" + + # Create an unlinked instance + inst = self.__class__() + + # Assign self to the new instance + inst.assign(self) + + return inst + + # + # Methods to be overwritten + # + def _create_clink(self): + """ + Overwrite me. + Creates a new clink + @return: PyCObject representing the C link + """ + pass + + def _del_clink(self, lnk): + """ + Overwrite me. + This method deletes the link + """ + pass + + def _get_clink_ptr(self): + """ + Overwrite me. + Returns the C link pointer as a 64bit number + """ + pass + + def assign(self, other): + """ + Overwrite me. + This method allows you to assign an instance contents to anothers + @return: Boolean + """ + pass + + clink = property(lambda self: self.__clink__) + """Returns the C link as a PyObject""" + + clink_ptr = property(lambda self: self._get_clink_ptr()) + """Returns the C link pointer as a number""" + +# ----------------------------------------------------------------------- +class object_t(object): + """Helper class used to initialize empty objects""" + def __init__(self, **kwds): + self.__dict__ = kwds + + def __getitem__(self, idx): + """Allow access to object attributes by index (like dictionaries)""" + return getattr(self, idx) + +# ----------------------------------------------------------------------- +def _bounded_getitem_iterator(self): + """Helper function, to be set as __iter__ method for qvector-, or array-based classes.""" + for i in range(len(self)): + yield self[i] + +# ----------------------------------------------------------------------- +class plugin_t(pyidc_opaque_object_t): + """Base class for all scripted plugins.""" + pass + +# ----------------------------------------------------------------------- +class pyidc_cvt_helper__(object): + """ + This is a special helper object that helps detect which kind + of object is this python object wrapping and how to convert it + back and from IDC. + This object is characterized by its special attribute and its value + """ + def __init__(self, cvt_id, value): + self.__idc_cvt_id__ = cvt_id + self.value = value + + def __set_value(self, v): + self.__idc_cvt_value__ = v + def __get_value(self): + return self.__idc_cvt_value__ + value = property(__get_value, __set_value) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_int64__(pyidc_cvt_helper__): + """Helper class for explicitly representing VT_INT64 values""" + + def __init__(self, v): + # id = 0 = int64 object + super(self.__class__, self).__init__(PY_ICID_INT64, v) + + # operation table + __op_table = \ + { + 0: lambda a, b: a + b, + 1: lambda a, b: a - b, + 2: lambda a, b: a * b, + 3: lambda a, b: a / b + } + # carries the operation given its number + def __op(self, op_n, other, rev=False): + a = self.value + # other operand of same type? then take its value field + if type(other) == type(self): + b = other.value + else: + b = other + if rev: + t = a + a = b + b = t + # construct a new object and return as the result + return self.__class__(self.__op_table[op_n](a, b)) + + # overloaded operators + def __add__(self, other): return self.__op(0, other) + def __sub__(self, other): return self.__op(1, other) + def __mul__(self, other): return self.__op(2, other) + def __div__(self, other): return self.__op(3, other) + def __radd__(self, other): return self.__op(0, other, True) + def __rsub__(self, other): return self.__op(1, other, True) + def __rmul__(self, other): return self.__op(2, other, True) + def __rdiv__(self, other): return self.__op(3, other, True) + +# ----------------------------------------------------------------------- +# qstrvec_t clinked object +class _qstrvec_t(py_clinked_object_t): + """ + WARNING: It is very unlikely an IDAPython user should ever, ever + have to use this type. It should only be used for IDAPython internals. + + For example, in py_askusingform.py, we ctypes-expose to the IDA + kernel & UI a qstrvec instance, in case a DropdownListControl is + constructed. + That's because that's what AskUsingForm expects, and we have no + choice but to make a DropdownListControl hold a qstrvec_t. + This is, afaict, the only situation where a Python + _qstrvec_t is required. + """ + + def __init__(self, items=None): + py_clinked_object_t.__init__(self) + # Populate the list if needed + if items: + self.from_list(items) + + def _create_clink(self): + return _idaapi.qstrvec_t_create() + + def _del_clink(self, lnk): + return _idaapi.qstrvec_t_destroy(lnk) + + def _get_clink_ptr(self): + return _idaapi.qstrvec_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.qstrvec_t_assign(self, other) + + def __setitem__(self, idx, s): + """Sets string at the given index""" + return _idaapi.qstrvec_t_set(self, idx, s) + + def __getitem__(self, idx): + """Gets the string at the given index""" + return _idaapi.qstrvec_t_get(self, idx) + + def __get_size(self): + return _idaapi.qstrvec_t_size(self) + + size = property(__get_size) + """Returns the count of elements""" + + def addressof(self, idx): + """Returns the address (as number) of the qstring at the given index""" + return _idaapi.qstrvec_t_addressof(self, idx) + + def add(self, s): + """Add a string to the vector""" + return _idaapi.qstrvec_t_add(self, s) + + def from_list(self, lst): + """Populates the vector from a Python string list""" + return _idaapi.qstrvec_t_from_list(self, lst) + + def clear(self, qclear=False): + """ + Clears all strings from the vector. + @param qclear: Just reset the size but do not actually free the memory + """ + return _idaapi.qstrvec_t_clear(self, qclear) + + def insert(self, idx, s): + """Insert a string into the vector""" + return _idaapi.qstrvec_t_insert(self, idx, s) + + def remove(self, idx): + """Removes a string from the vector""" + return _idaapi.qstrvec_t_remove(self, idx) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_refclass__(pyidc_cvt_helper__): + """Helper class for representing references to immutable objects""" + def __init__(self, v): + # id = one = byref object + super(self.__class__, self).__init__(PY_ICID_BYREF, v) + + def cstr(self): + """Returns the string as a C string (up to the zero termination)""" + return as_cstr(self.value) + +# ----------------------------------------------------------------------- +def as_cstr(val): + """ + Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) + It scans for the first \x00 and returns the string value up to that point. + """ + if isinstance(val, PyIdc_cvt_refclass__): + val = val.value + + n = val.find('\x00') + return val if n == -1 else val[:n] + +# ----------------------------------------------------------------------- +def as_unicode(s): + """Convenience function to convert a string into appropriate unicode format""" + # use UTF16 big/little endian, depending on the environment? + return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) + +# ----------------------------------------------------------------------- +def as_uint32(v): + """Returns a number as an unsigned int32 number""" + return v & 0xffffffff + +# ----------------------------------------------------------------------- +def as_int32(v): + """Returns a number as a signed int32 number""" + return -((~v & 0xffffffff)+1) + +# ----------------------------------------------------------------------- +def as_signed(v, nbits = 32): + """ + Returns a number as signed. The number of bits are specified by the user. + The MSB holds the sign. + """ + return -(( ~v & ((1 << nbits)-1) ) + 1) if v & (1 << nbits-1) else v + +# ---------------------------------------------------------------------- +def copy_bits(v, s, e=-1): + """ + Copy bits from a value + @param v: the value + @param s: starting bit (0-based) + @param e: ending bit + """ + # end-bit not specified? use start bit (thus extract one bit) + if e == -1: + e = s + # swap start and end if start > end + if s > e: + e, s = s, e + + mask = ~(((1 << (e-s+1))-1) << s) + + return (v & mask) >> s + +# ---------------------------------------------------------------------- +__struct_unpack_table = { + 1: ('b', 'B'), + 2: ('h', 'H'), + 4: ('l', 'L'), + 8: ('q', 'Q') +} + +# ---------------------------------------------------------------------- +def struct_unpack(buffer, signed = False, offs = 0): + """ + Unpack a buffer given its length and offset using struct.unpack_from(). + This function will know how to unpack the given buffer by using the lookup table '__struct_unpack_table' + If the buffer is of unknown length then None is returned. Otherwise the unpacked value is returned. + """ + # Supported length? + n = len(buffer) + if n not in __struct_unpack_table: + return None + # Conver to number + signed = 1 if signed else 0 + + # Unpack + return struct.unpack_from(__struct_unpack_table[n][signed], buffer, offs)[0] + + +# ------------------------------------------------------------ +def IDAPython_ExecSystem(cmd): + """ + Executes a command with popen(). + """ + try: + f = os.popen(cmd, "r") + s = ''.join(f.readlines()) + f.close() + return s + except Exception as e: + return "%s\n%s" % (str(e), traceback.format_exc()) + +# ------------------------------------------------------------ +def IDAPython_FormatExc(etype, value, tb, limit=None): + """ + This function is used to format an exception given the + values returned by a PyErr_Fetch() + """ + try: + return ''.join(traceback.format_exception(etype, value, tb, limit)) + except: + return str(value) + + +# ------------------------------------------------------------ +def IDAPython_ExecScript(script, g): + """ + Run the specified script. + It also addresses http://code.google.com/p/idapython/issues/detail?id=42 + + This function is used by the low-level plugin code. + """ + scriptpath = os.path.dirname(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + argv = sys.argv + sys.argv = [ script ] + + # Adjust the __file__ path in the globals we pass to the script + old__file__ = g['__file__'] if '__file__' in g else '' + g['__file__'] = script + + try: + execfile(script, g) + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + finally: + # Restore state + g['__file__'] = old__file__ + sys.argv = argv + + return PY_COMPILE_ERR + +# ------------------------------------------------------------ +def IDAPython_LoadProcMod(script, g): + """ + Load processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptpath, scriptname = os.path.split(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + procmod_name = os.path.splitext(scriptname)[0] + procobj = None + fp = None + try: + fp, pathname, description = imp.find_module(procmod_name) + procmod = imp.load_module(procmod_name, fp, pathname, description) + if parent: + setattr(parent, procmod_name, procmod) + # export attrs from parent to processor module + parent_attrs = getattr(parent, '__all__', + (attr for attr in dir(parent) if not attr.startswith('_'))) + for pa in parent_attrs: + setattr(procmod, pa, getattr(parent, pa)) + # instantiate processor object + if getattr(procmod, 'PROCESSOR_ENTRY', None): + procobj = procmod.PROCESSOR_ENTRY() + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + finally: + if fp: fp.close() + + sys.path.remove(scriptpath) + + return (PY_COMPILE_ERR, procobj) + +# ------------------------------------------------------------ +def IDAPython_UnLoadProcMod(script, g): + """ + Unload processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptname = os.path.split(script)[1] + procmod_name = os.path.splitext(scriptname)[0] + if getattr(parent, procmod_name, None): + delattr(parent, procmod_name) + del sys.modules[procmod_name] + PY_COMPILE_ERR = None + return PY_COMPILE_ERR + +# ---------------------------------------------------------------------- +class __IDAPython_Completion_Util(object): + """Internal utility class for auto-completion support""" + def __init__(self): + self.n = 0 + self.completion = None + self.lastmodule = None + + @staticmethod + def parse_identifier(line, prefix, prefix_start): + """ + Parse a line and extracts identifier + """ + id_start = prefix_start + while id_start > 0: + ch = line[id_start] + if not ch.isalpha() and ch != '.' and ch != '_': + id_start += 1 + break + id_start -= 1 + + return line[id_start:prefix_start + len(prefix)] + + @staticmethod + def dir_of(m, prefix): + return [x for x in dir(m) if x.startswith(prefix)] + + @classmethod + def get_completion(cls, id, prefix): + try: + m = sys.modules['__main__'] + + parts = id.split('.') + c = len(parts) + + for i in xrange(0, c-1): + m = getattr(m, parts[i]) + except Exception as e: + return (None, None) + else: + # search in the module + completion = cls.dir_of(m, prefix) + + # no completion found? looking from the global scope? then try the builtins + if not completion and c == 1: + completion = cls.dir_of(__builtin__, prefix) + + return (m, completion) if completion else (None, None) + + def __call__(self, prefix, n, line, prefix_start): + if n == 0: + self.n = n + id = self.parse_identifier(line, prefix, prefix_start) + self.lastmodule, self.completion = self.get_completion(id, prefix) + + if self.completion is None or n >= len(self.completion): + return None + + s = self.completion[n] + try: + attr = getattr(self.lastmodule, s) + # Is it callable? + if callable(attr): + return s + ("" if line.startswith("?") else "(") + # Is it iterable? + elif isinstance(attr, basestring) or getattr(attr, '__iter__', False): + return s + "[" + except: + pass + + return s + +# Instantiate an IDAPython command completion object (for use with IDA's CLI bar) +IDAPython_Completion = __IDAPython_Completion_Util() + +def _listify_types(*classes): + for cls in classes: + cls.__getitem__ = cls.at + cls.__len__ = cls.size + cls.__iter__ = _bounded_getitem_iterator + +# diff --git a/pywraps/py_idaview.hpp b/pywraps/py_idaview.hpp index 5d4bcc62..2c6c251f 100644 --- a/pywraps/py_idaview.hpp +++ b/pywraps/py_idaview.hpp @@ -68,7 +68,7 @@ bool py_idaview_t::Unbind(PyObject *self) py_idaview_t *_this = view_extract_this(self); if ( _this == NULL ) return false; - _this->unbind(); + _this->unbind(true); return true; } diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp index fe4e0d6c..7177fa2e 100644 --- a/pywraps/py_idp.hpp +++ b/pywraps/py_idp.hpp @@ -405,7 +405,7 @@ static PyObject *ph_get_operand_info( tid, _py_getreg, regvalues.begin(), - &opinf) != 0 ) + &opinf) > 0 ) { break; } @@ -1598,7 +1598,7 @@ static const regval_t *idaapi _py_getreg( { for ( int i=dbg->registers_size - 1; i >= 0; i-- ) { - if ( stricmp(name, dbg->registers[i].name) == 0 ) + if ( strieq(name, dbg->registers(i).name) ) return ®vals[i]; } static regval_t rv; diff --git a/pywraps/py_kernwin.py b/pywraps/py_kernwin.py index 1d2b6c55..cdaa267d 100644 --- a/pywraps/py_kernwin.py +++ b/pywraps/py_kernwin.py @@ -1,121 +1,121 @@ -# ----------------------------------------------------------------------- -# Standalone and testing code -import sys -try: - import pywraps - pywraps_there = True - print "Using pywraps" -except: - pywraps_there = False - print "Not using pywraps" - -try: - import _idaapi -except: - print "Please try me from inside IDA" - sys.exit(0) - -if pywraps_there: - _idaapi.execute_sync = pywraps.py_execute_sync - _idaapi.add_hotkey = pywraps.py_add_hotkey - _idaapi.del_hotkey = pywraps.py_del_hotkey - -# ----------------------------------------------------------------------- -# -DP_LEFT = 0x0001 -DP_TOP = 0x0002 -DP_RIGHT = 0x0004 -DP_BOTTOM = 0x0008 -DP_INSIDE = 0x0010 -# if not before, then it is after -# (use DP_INSIDE | DP_BEFORE to insert a tab before a given tab) -# this flag alone cannot be used to determine orientation -DP_BEFORE = 0x0020 -# used with combination of other flags -DP_TAB = 0x0040 -DP_FLOATING = 0x0080 - -# ---------------------------------------------------------------------- -def load_custom_icon(file_name=None, data=None, format=None): - """ - Loads a custom icon and returns an identifier that can be used with other APIs - - If file_name is passed then the other two arguments are ignored. - - @param file_name: The icon file name - @param data: The icon data - @param format: The icon data format - - @return: Icon id or 0 on failure. - Use free_custom_icon() to free it - """ - if file_name is not None: - return _idaapi.py_load_custom_icon_fn(file_name) - elif not (data is None and format is None): - return _idaapi.py_load_custom_icon_data(data, format) - else: - return 0 - -# ---------------------------------------------------------------------- -def asklong(defval, format): - res, val = _idaapi._asklong(defval, format) - - if res == 1: - return val - else: - return None - -# ---------------------------------------------------------------------- -def askaddr(defval, format): - res, ea = _idaapi._askaddr(defval, format) - - if res == 1: - return ea - else: - return None - -# ---------------------------------------------------------------------- -def askseg(defval, format): - res, sel = _idaapi._askseg(defval, format) - - if res == 1: - return sel - else: - return None - -# ---------------------------------------------------------------------- -class action_handler_t: - def __init__(self): - pass - - def activate(self, ctx): - return 0 - - def update(self, ctx): - pass - -# - -# ---------------------------------------------------------------------- -from threading import Thread -import time - -# ---------------------------------------------------------------------- -def myfunction(cnt): - i = 1 - while i <= cnt: - print "i=", i - i += 1 - time.sleep(1) - - print "done!" - -def test_thread(): - t = Thread(target=myfunction, args=(2,)) - - t.start() - t.join() - -# ---------------------------------------------------------------------- -def hotkey_func1(): - print "Hello from hotkey handler in Python!" +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Not using pywraps" + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +if pywraps_there: + _idaapi.execute_sync = pywraps.py_execute_sync + _idaapi.add_hotkey = pywraps.py_add_hotkey + _idaapi.del_hotkey = pywraps.py_del_hotkey + +# ----------------------------------------------------------------------- +# +DP_LEFT = 0x0001 +DP_TOP = 0x0002 +DP_RIGHT = 0x0004 +DP_BOTTOM = 0x0008 +DP_INSIDE = 0x0010 +# if not before, then it is after +# (use DP_INSIDE | DP_BEFORE to insert a tab before a given tab) +# this flag alone cannot be used to determine orientation +DP_BEFORE = 0x0020 +# used with combination of other flags +DP_TAB = 0x0040 +DP_FLOATING = 0x0080 + +# ---------------------------------------------------------------------- +def load_custom_icon(file_name=None, data=None, format=None): + """ + Loads a custom icon and returns an identifier that can be used with other APIs + + If file_name is passed then the other two arguments are ignored. + + @param file_name: The icon file name + @param data: The icon data + @param format: The icon data format + + @return: Icon id or 0 on failure. + Use free_custom_icon() to free it + """ + if file_name is not None: + return _idaapi.py_load_custom_icon_fn(file_name) + elif not (data is None and format is None): + return _idaapi.py_load_custom_icon_data(data, format) + else: + return 0 + +# ---------------------------------------------------------------------- +def asklong(defval, format): + res, val = _idaapi._asklong(defval, format) + + if res == 1: + return val + else: + return None + +# ---------------------------------------------------------------------- +def askaddr(defval, format): + res, ea = _idaapi._askaddr(defval, format) + + if res == 1: + return ea + else: + return None + +# ---------------------------------------------------------------------- +def askseg(defval, format): + res, sel = _idaapi._askseg(defval, format) + + if res == 1: + return sel + else: + return None + +# ---------------------------------------------------------------------- +class action_handler_t: + def __init__(self): + pass + + def activate(self, ctx): + return 0 + + def update(self, ctx): + pass + +# + +# ---------------------------------------------------------------------- +from threading import Thread +import time + +# ---------------------------------------------------------------------- +def myfunction(cnt): + i = 1 + while i <= cnt: + print "i=", i + i += 1 + time.sleep(1) + + print "done!" + +def test_thread(): + t = Thread(target=myfunction, args=(2,)) + + t.start() + t.join() + +# ---------------------------------------------------------------------- +def hotkey_func1(): + print "Hello from hotkey handler in Python!" diff --git a/pywraps/py_lines.hpp b/pywraps/py_lines.hpp index 1476a4ff..2661a34d 100644 --- a/pywraps/py_lines.hpp +++ b/pywraps/py_lines.hpp @@ -1,214 +1,214 @@ -#ifndef __PYWRAPS__LINES__ -#define __PYWRAPS__LINES__ - -// -//------------------------------------------------------------------------ -static PyObject *py_get_user_defined_prefix = NULL; -static void idaapi s_py_get_user_defined_prefix( - ea_t ea, - int lnnum, - int indent, - const char *line, - char *buf, - size_t bufsize) -{ - PYW_GIL_GET; - newref_t py_ret( - PyObject_CallFunction( - py_get_user_defined_prefix, - PY_FMT64 "iis" PY_FMT64, - ea, lnnum, indent, line, bufsize)); - - // Error? Display it - // No error? Copy the buffer - if ( !PyW_ShowCbErr("py_get_user_defined_prefix") ) - { - Py_ssize_t py_len; - char *py_str; - if ( PyString_AsStringAndSize(py_ret.o, &py_str, &py_len) != -1 ) - { - memcpy(buf, py_str, qmin(bufsize, py_len)); - if ( py_len < bufsize ) - buf[py_len] = '\0'; - } - } -} -// - -//------------------------------------------------------------------------ - -// - -//------------------------------------------------------------------------ -/* -# -def set_user_defined_prefix(width, callback): - """ - User-defined line-prefixes are displayed just after the autogenerated - line prefixes. In order to use them, the plugin should call the - following function to specify its width and contents. - @param width: the width of the user-defined prefix - @param callback: a get_user_defined_prefix callback to get the contents of the prefix. - Its arguments: - ea - linear address - lnnum - line number - indent - indent of the line contents (-1 means the default instruction) - indent and is used for instruction itself. see explanations for printf_line() - line - the line to be generated. the line usually contains color tags this argument - can be examined to decide whether to generated the prefix - bufsize- the maximum allowed size of the output buffer - It returns a buffer of size < bufsize - - In order to remove the callback before unloading the plugin, specify the width = 0 or the callback = None - """ - pass -# -*/ -static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( width == 0 || pycb == Py_None ) - { - // Release old callback reference - Py_XDECREF(py_get_user_defined_prefix); - - // ...and clear it - py_get_user_defined_prefix = NULL; - - // Uninstall user defind prefix - set_user_defined_prefix(0, NULL); - } - else if ( PyCallable_Check(pycb) ) - { - // Release old callback reference - Py_XDECREF(py_get_user_defined_prefix); - - // Copy new callback and hold a reference - py_get_user_defined_prefix = pycb; - Py_INCREF(py_get_user_defined_prefix); - - set_user_defined_prefix(width, s_py_get_user_defined_prefix); - } - else - { - Py_RETURN_FALSE; - } - Py_RETURN_TRUE; -} - -//------------------------------------------------------------------------- -/* -# -def tag_remove(colstr): - """ - Remove color escape sequences from a string - @param colstr: the colored string with embedded tags - @return: - None on failure - or a new string w/o the tags - """ - pass -# -*/ -PyObject *py_tag_remove(const char *instr) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - size_t sz = strlen(instr); - char *buf = new char[sz + 5]; - if ( buf == NULL ) - Py_RETURN_NONE; - - ssize_t r = tag_remove(instr, buf, sz); - PyObject *res; - if ( r < 0 ) - { - Py_INCREF(Py_None); - res = Py_None; - } - else - { - res = PyString_FromString(buf); - } - delete [] buf; - return res; -} - -//------------------------------------------------------------------------- -PyObject *py_tag_addr(ea_t ea) -{ - char buf[100]; - tag_addr(buf, buf + sizeof(buf), ea); - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyString_FromString(buf); -} - -//------------------------------------------------------------------------- -int py_tag_skipcode(const char *line) -{ - return tag_skipcode(line)-line; -} - -//------------------------------------------------------------------------- -int py_tag_skipcodes(const char *line) -{ - return tag_skipcodes(line)-line; -} - -//------------------------------------------------------------------------- -int py_tag_advance(const char *line, int cnt) -{ - return tag_advance(line, cnt)-line; -} - -//------------------------------------------------------------------------- -/* -# -def generate_disassembly(ea, max_lines, as_stack, notags): - """ - Generate disassembly lines (many lines) and put them into a buffer - - @param ea: address to generate disassembly for - @param max_lines: how many lines max to generate - @param as_stack: Display undefined items as 2/4/8 bytes - @return: - - None on failure - - tuple(most_important_line_number, tuple(lines)) : Returns a tuple containing - the most important line number and a tuple of generated lines - """ - pass -# -*/ -PyObject *py_generate_disassembly( - ea_t ea, - int max_lines, - bool as_stack, - bool notags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( max_lines <= 0 ) - Py_RETURN_NONE; - - qstring qbuf; - char **lines = new char *[max_lines]; - int lnnum; - int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); - - newref_t py_tuple(PyTuple_New(nlines)); - for ( int i=0; i -#endif +#ifndef __PYWRAPS__LINES__ +#define __PYWRAPS__LINES__ + +// +//------------------------------------------------------------------------ +static PyObject *py_get_user_defined_prefix = NULL; +static void idaapi s_py_get_user_defined_prefix( + ea_t ea, + int lnnum, + int indent, + const char *line, + char *buf, + size_t bufsize) +{ + PYW_GIL_GET; + newref_t py_ret( + PyObject_CallFunction( + py_get_user_defined_prefix, + PY_FMT64 "iis" PY_FMT64, + ea, lnnum, indent, line, bufsize)); + + // Error? Display it + // No error? Copy the buffer + if ( !PyW_ShowCbErr("py_get_user_defined_prefix") ) + { + Py_ssize_t py_len; + char *py_str; + if ( PyString_AsStringAndSize(py_ret.o, &py_str, &py_len) != -1 ) + { + memcpy(buf, py_str, qmin(bufsize, py_len)); + if ( py_len < bufsize ) + buf[py_len] = '\0'; + } + } +} +// + +//------------------------------------------------------------------------ + +// + +//------------------------------------------------------------------------ +/* +# +def set_user_defined_prefix(width, callback): + """ + User-defined line-prefixes are displayed just after the autogenerated + line prefixes. In order to use them, the plugin should call the + following function to specify its width and contents. + @param width: the width of the user-defined prefix + @param callback: a get_user_defined_prefix callback to get the contents of the prefix. + Its arguments: + ea - linear address + lnnum - line number + indent - indent of the line contents (-1 means the default instruction) + indent and is used for instruction itself. see explanations for printf_line() + line - the line to be generated. the line usually contains color tags this argument + can be examined to decide whether to generated the prefix + bufsize- the maximum allowed size of the output buffer + It returns a buffer of size < bufsize + + In order to remove the callback before unloading the plugin, specify the width = 0 or the callback = None + """ + pass +# +*/ +static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( width == 0 || pycb == Py_None ) + { + // Release old callback reference + Py_XDECREF(py_get_user_defined_prefix); + + // ...and clear it + py_get_user_defined_prefix = NULL; + + // Uninstall user defind prefix + set_user_defined_prefix(0, NULL); + } + else if ( PyCallable_Check(pycb) ) + { + // Release old callback reference + Py_XDECREF(py_get_user_defined_prefix); + + // Copy new callback and hold a reference + py_get_user_defined_prefix = pycb; + Py_INCREF(py_get_user_defined_prefix); + + set_user_defined_prefix(width, s_py_get_user_defined_prefix); + } + else + { + Py_RETURN_FALSE; + } + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +/* +# +def tag_remove(colstr): + """ + Remove color escape sequences from a string + @param colstr: the colored string with embedded tags + @return: + None on failure + or a new string w/o the tags + """ + pass +# +*/ +PyObject *py_tag_remove(const char *instr) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + size_t sz = strlen(instr); + char *buf = new char[sz + 5]; + if ( buf == NULL ) + Py_RETURN_NONE; + + ssize_t r = tag_remove(instr, buf, sz); + PyObject *res; + if ( r < 0 ) + { + Py_INCREF(Py_None); + res = Py_None; + } + else + { + res = PyString_FromString(buf); + } + delete [] buf; + return res; +} + +//------------------------------------------------------------------------- +PyObject *py_tag_addr(ea_t ea) +{ + char buf[100]; + tag_addr(buf, buf + sizeof(buf), ea); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +int py_tag_skipcode(const char *line) +{ + return tag_skipcode(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_skipcodes(const char *line) +{ + return tag_skipcodes(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_advance(const char *line, int cnt) +{ + return tag_advance(line, cnt)-line; +} + +//------------------------------------------------------------------------- +/* +# +def generate_disassembly(ea, max_lines, as_stack, notags): + """ + Generate disassembly lines (many lines) and put them into a buffer + + @param ea: address to generate disassembly for + @param max_lines: how many lines max to generate + @param as_stack: Display undefined items as 2/4/8 bytes + @return: + - None on failure + - tuple(most_important_line_number, tuple(lines)) : Returns a tuple containing + the most important line number and a tuple of generated lines + """ + pass +# +*/ +PyObject *py_generate_disassembly( + ea_t ea, + int max_lines, + bool as_stack, + bool notags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( max_lines <= 0 ) + Py_RETURN_NONE; + + qstring qbuf; + char **lines = new char *[max_lines]; + int lnnum; + int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); + + newref_t py_tuple(PyTuple_New(nlines)); + for ( int i=0; i +#endif diff --git a/pywraps/py_lines.py b/pywraps/py_lines.py index 3c72541d..dfb1f004 100644 --- a/pywraps/py_lines.py +++ b/pywraps/py_lines.py @@ -1,34 +1,34 @@ -# - -# ---------------- Color escape sequence defitions ------------------------- -COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 -SCOLOR_FG_MAX = '\x28' # Max color number -SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 -SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 -SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 -SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 -SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 -SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 -SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded - -# ---------------- Line prefix colors -------------------------------------- -PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) - -def requires_color_esc(c): - """ - Checks if the given character requires escaping - @param c: character (string of one char) - @return: Boolean - """ - t = ord(c[0]) - return c >= COLOR_ON and c <= COLOR_INV - -def COLSTR(str, tag): - """ - Utility function to create a colored line - @param str: The string - @param tag: Color tag constant. One of SCOLOR_XXXX - """ - return SCOLOR_ON + tag + str + SCOLOR_OFF + tag - -# +# + +# ---------------- Color escape sequence defitions ------------------------- +COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 +SCOLOR_FG_MAX = '\x28' # Max color number +SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 +SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 +SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 +SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 +SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 +SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 +SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded + +# ---------------- Line prefix colors -------------------------------------- +PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) + +def requires_color_esc(c): + """ + Checks if the given character requires escaping + @param c: character (string of one char) + @return: Boolean + """ + t = ord(c[0]) + return c >= COLOR_ON and c <= COLOR_INV + +def COLSTR(str, tag): + """ + Utility function to create a colored line + @param str: The string + @param tag: Color tag constant. One of SCOLOR_XXXX + """ + return SCOLOR_ON + tag + str + SCOLOR_OFF + tag + +# diff --git a/pywraps/py_linput.hpp b/pywraps/py_linput.hpp index e5d000b6..4dd725c7 100644 --- a/pywraps/py_linput.hpp +++ b/pywraps/py_linput.hpp @@ -1,417 +1,417 @@ -#ifndef __PY_IDA_LINPUT__ -#define __PY_IDA_LINPUT__ - -//-------------------------------------------------------------------------- -// -/* -# -class loader_input_t(pyidc_opaque_object_t): - """A helper class to work with linput_t related functions. - This class is also used by file loaders scripts. - """ - def __init__(self): - pass - - def close(self): - """Closes the file""" - pass - - def open(self, filename, remote = False): - """Opens a file (or a remote file) - @return: Boolean - """ - pass - - def set_linput(self, linput): - """Links the current loader_input_t instance to a linput_t instance""" - pass - - @staticmethod - def from_fp(fp): - """A static method to construct an instance from a FILE*""" - pass - - def open_memory(self, start, size): - """Create a linput for process memory (By internally calling idaapi.create_memory_linput()) - This linput will use dbg->read_memory() to read data - @param start: starting address of the input - @param size: size of the memory area to represent as linput - if unknown, may be passed as 0 - """ - pass - - def seek(self, pos, whence = SEEK_SET): - """Set input source position - @return: the new position (not 0 as fseek!) - """ - pass - - def tell(self): - """Returns the current position""" - pass - - def getz(self, sz, fpos = -1): - """Returns a zero terminated string at the given position - @param sz: maximum size of the string - @param fpos: if != -1 then seek will be performed before reading - @return: The string or None on failure. - """ - pass - - def gets(self, len): - """Reads a line from the input file. Returns the read line or None""" - pass - - def read(self, size): - """Reads from the file. Returns the buffer or None""" - pass - - def readbytes(self, size, big_endian): - """Similar to read() but it respect the endianness""" - pass - - def file2base(self, pos, ea1, ea2, patchable): - """ - Load portion of file into the database - This function will include (ea1..ea2) into the addressing space of the - program (make it enabled) - @param li: pointer ot input source - @param pos: position in the file - @param (ea1..ea2): range of destination linear addresses - @param patchable: should the kernel remember correspondance of - file offsets to linear addresses. - @return: 1-ok,0-read error, a warning is displayed - """ - pass - - def get_char(self): - """Reads a single character from the file. Returns None if EOF or the read character""" - pass - - def opened(self): - """Checks if the file is opened or not""" - pass -# -*/ -class loader_input_t -{ -private: - linput_t *li; - int own; - qstring fn; - enum - { - OWN_NONE = 0, // li not created yet - OWN_CREATE = 1, // Owns li because we created it - OWN_FROM_LI = 2, // No ownership we borrowed the li from another class - OWN_FROM_FP = 3, // We got an li instance from an fp instance, we have to unmake_linput() on Close - }; - - //-------------------------------------------------------------------------- - void _from_cobject(PyObject *pycobject) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); - } - - //-------------------------------------------------------------------------- - void assign(const loader_input_t &rhs) - { - fn = rhs.fn; - li = rhs.li; - own = OWN_FROM_LI; - } - - //-------------------------------------------------------------------------- - loader_input_t(const loader_input_t &rhs) - { - assign(rhs); - } -public: - // Special attribute that tells the pyvar_to_idcvar how to convert this - // class from and to IDC. The value of this variable must be set to two - int __idc_cvt_id__; - //-------------------------------------------------------------------------- - loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( pycobject != NULL && PyCObject_Check(pycobject) ) - _from_cobject(pycobject); - } - - //-------------------------------------------------------------------------- - void close() - { - if ( li == NULL ) - return; - - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - if ( own == OWN_CREATE ) - close_linput(li); - else if ( own == OWN_FROM_FP ) - unmake_linput(li); - Py_END_ALLOW_THREADS; - li = NULL; - own = OWN_NONE; - } - - //-------------------------------------------------------------------------- - ~loader_input_t() - { - close(); - } - - //-------------------------------------------------------------------------- - bool open(const char *filename, bool remote = false) - { - close(); - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - li = open_linput(filename, remote); - if ( li != NULL ) - { - // Save file name - fn = filename; - own = OWN_CREATE; - } - Py_END_ALLOW_THREADS; - return li != NULL; - } - - //-------------------------------------------------------------------------- - void set_linput(linput_t *linput) - { - close(); - own = OWN_FROM_LI; - li = linput; - fn.sprnt("", linput); - } - - //-------------------------------------------------------------------------- - static loader_input_t *from_linput(linput_t *linput) - { - loader_input_t *l = new loader_input_t(); - l->set_linput(linput); - return l; - } - - //-------------------------------------------------------------------------- - // This method can be used to pass a linput_t* from C code - static loader_input_t *from_cobject(PyObject *pycobject) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(pycobject) ) - return NULL; - loader_input_t *l = new loader_input_t(); - l->_from_cobject(pycobject); - return l; - } - - //-------------------------------------------------------------------------- - static loader_input_t *from_fp(FILE *fp) - { - PYW_GIL_GET; - loader_input_t *l = NULL; - Py_BEGIN_ALLOW_THREADS; - linput_t *fp_li = make_linput(fp); - if ( fp_li != NULL ) - { - l = new loader_input_t(); - l->own = OWN_FROM_FP; - l->fn.sprnt("", fp); - l->li = fp_li; - } - Py_END_ALLOW_THREADS; - return l; - } - - //-------------------------------------------------------------------------- - linput_t *get_linput() - { - return li; - } - - //-------------------------------------------------------------------------- - bool open_memory(ea_t start, asize_t size = 0) - { - PYW_GIL_GET; - linput_t *l; - Py_BEGIN_ALLOW_THREADS; - l = create_memory_linput(start, size); - if ( l != NULL ) - { - close(); - li = l; - fn = ""; - own = OWN_CREATE; - } - Py_END_ALLOW_THREADS; - return l != NULL; - } - - //-------------------------------------------------------------------------- - int32 seek(int32 pos, int whence = SEEK_SET) - { - int32 r; - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - r = qlseek(li, pos, whence); - Py_END_ALLOW_THREADS; - return r; - } - - //-------------------------------------------------------------------------- - int32 tell() - { - int32 r; - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - r = qltell(li); - Py_END_ALLOW_THREADS; - return r; - } - - //-------------------------------------------------------------------------- - PyObject *getz(size_t sz, int32 fpos = -1) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(sz + 5); - if ( buf == NULL ) - break; - Py_BEGIN_ALLOW_THREADS; - qlgetz(li, fpos, buf, sz); - Py_END_ALLOW_THREADS; - PyObject *ret = PyString_FromString(buf); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *gets(size_t len) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(len + 5); - if ( buf == NULL ) - break; - bool ok; - Py_BEGIN_ALLOW_THREADS; - ok = qlgets(buf, len, li) != NULL; - Py_END_ALLOW_THREADS; - if ( !ok ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromString(buf); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *read(size_t size) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - ssize_t r; - Py_BEGIN_ALLOW_THREADS; - r = qlread(li, buf, size); - Py_END_ALLOW_THREADS; - if ( r == -1 ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - bool opened() - { - return li != NULL; - } - - //-------------------------------------------------------------------------- - PyObject *readbytes(size_t size, bool big_endian) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - int r; - Py_BEGIN_ALLOW_THREADS; - r = lreadbytes(li, buf, size, big_endian); - Py_END_ALLOW_THREADS; - if ( r == -1 ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) - { - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = ::file2base(li, pos, ea1, ea2, patchable); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int32 size() - { - int32 rc; - Py_BEGIN_ALLOW_THREADS; - rc = qlsize(li); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - PyObject *filename() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyString_FromString(fn.c_str()); - } - - //-------------------------------------------------------------------------- - PyObject *get_char() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int ch; - Py_BEGIN_ALLOW_THREADS; - ch = qlgetc(li); - Py_END_ALLOW_THREADS; - if ( ch == EOF ) - Py_RETURN_NONE; - return Py_BuildValue("c", ch); - } -}; -// - -#endif +#ifndef __PY_IDA_LINPUT__ +#define __PY_IDA_LINPUT__ + +//-------------------------------------------------------------------------- +// +/* +# +class loader_input_t(pyidc_opaque_object_t): + """A helper class to work with linput_t related functions. + This class is also used by file loaders scripts. + """ + def __init__(self): + pass + + def close(self): + """Closes the file""" + pass + + def open(self, filename, remote = False): + """Opens a file (or a remote file) + @return: Boolean + """ + pass + + def set_linput(self, linput): + """Links the current loader_input_t instance to a linput_t instance""" + pass + + @staticmethod + def from_fp(fp): + """A static method to construct an instance from a FILE*""" + pass + + def open_memory(self, start, size): + """Create a linput for process memory (By internally calling idaapi.create_memory_linput()) + This linput will use dbg->read_memory() to read data + @param start: starting address of the input + @param size: size of the memory area to represent as linput + if unknown, may be passed as 0 + """ + pass + + def seek(self, pos, whence = SEEK_SET): + """Set input source position + @return: the new position (not 0 as fseek!) + """ + pass + + def tell(self): + """Returns the current position""" + pass + + def getz(self, sz, fpos = -1): + """Returns a zero terminated string at the given position + @param sz: maximum size of the string + @param fpos: if != -1 then seek will be performed before reading + @return: The string or None on failure. + """ + pass + + def gets(self, len): + """Reads a line from the input file. Returns the read line or None""" + pass + + def read(self, size): + """Reads from the file. Returns the buffer or None""" + pass + + def readbytes(self, size, big_endian): + """Similar to read() but it respect the endianness""" + pass + + def file2base(self, pos, ea1, ea2, patchable): + """ + Load portion of file into the database + This function will include (ea1..ea2) into the addressing space of the + program (make it enabled) + @param li: pointer ot input source + @param pos: position in the file + @param (ea1..ea2): range of destination linear addresses + @param patchable: should the kernel remember correspondance of + file offsets to linear addresses. + @return: 1-ok,0-read error, a warning is displayed + """ + pass + + def get_char(self): + """Reads a single character from the file. Returns None if EOF or the read character""" + pass + + def opened(self): + """Checks if the file is opened or not""" + pass +# +*/ +class loader_input_t +{ +private: + linput_t *li; + int own; + qstring fn; + enum + { + OWN_NONE = 0, // li not created yet + OWN_CREATE = 1, // Owns li because we created it + OWN_FROM_LI = 2, // No ownership we borrowed the li from another class + OWN_FROM_FP = 3, // We got an li instance from an fp instance, we have to unmake_linput() on Close + }; + + //-------------------------------------------------------------------------- + void _from_cobject(PyObject *pycobject) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); + } + + //-------------------------------------------------------------------------- + void assign(const loader_input_t &rhs) + { + fn = rhs.fn; + li = rhs.li; + own = OWN_FROM_LI; + } + + //-------------------------------------------------------------------------- + loader_input_t(const loader_input_t &rhs) + { + assign(rhs); + } +public: + // Special attribute that tells the pyvar_to_idcvar how to convert this + // class from and to IDC. The value of this variable must be set to two + int __idc_cvt_id__; + //-------------------------------------------------------------------------- + loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( pycobject != NULL && PyCObject_Check(pycobject) ) + _from_cobject(pycobject); + } + + //-------------------------------------------------------------------------- + void close() + { + if ( li == NULL ) + return; + + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + if ( own == OWN_CREATE ) + close_linput(li); + else if ( own == OWN_FROM_FP ) + unmake_linput(li); + Py_END_ALLOW_THREADS; + li = NULL; + own = OWN_NONE; + } + + //-------------------------------------------------------------------------- + ~loader_input_t() + { + close(); + } + + //-------------------------------------------------------------------------- + bool open(const char *filename, bool remote = false) + { + close(); + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + li = open_linput(filename, remote); + if ( li != NULL ) + { + // Save file name + fn = filename; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return li != NULL; + } + + //-------------------------------------------------------------------------- + void set_linput(linput_t *linput) + { + close(); + own = OWN_FROM_LI; + li = linput; + fn.sprnt("", linput); + } + + //-------------------------------------------------------------------------- + static loader_input_t *from_linput(linput_t *linput) + { + loader_input_t *l = new loader_input_t(); + l->set_linput(linput); + return l; + } + + //-------------------------------------------------------------------------- + // This method can be used to pass a linput_t* from C code + static loader_input_t *from_cobject(PyObject *pycobject) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(pycobject) ) + return NULL; + loader_input_t *l = new loader_input_t(); + l->_from_cobject(pycobject); + return l; + } + + //-------------------------------------------------------------------------- + static loader_input_t *from_fp(FILE *fp) + { + PYW_GIL_GET; + loader_input_t *l = NULL; + Py_BEGIN_ALLOW_THREADS; + linput_t *fp_li = make_linput(fp); + if ( fp_li != NULL ) + { + l = new loader_input_t(); + l->own = OWN_FROM_FP; + l->fn.sprnt("", fp); + l->li = fp_li; + } + Py_END_ALLOW_THREADS; + return l; + } + + //-------------------------------------------------------------------------- + linput_t *get_linput() + { + return li; + } + + //-------------------------------------------------------------------------- + bool open_memory(ea_t start, asize_t size = 0) + { + PYW_GIL_GET; + linput_t *l; + Py_BEGIN_ALLOW_THREADS; + l = create_memory_linput(start, size); + if ( l != NULL ) + { + close(); + li = l; + fn = ""; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return l != NULL; + } + + //-------------------------------------------------------------------------- + int32 seek(int32 pos, int whence = SEEK_SET) + { + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qlseek(li, pos, whence); + Py_END_ALLOW_THREADS; + return r; + } + + //-------------------------------------------------------------------------- + int32 tell() + { + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qltell(li); + Py_END_ALLOW_THREADS; + return r; + } + + //-------------------------------------------------------------------------- + PyObject *getz(size_t sz, int32 fpos = -1) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(sz + 5); + if ( buf == NULL ) + break; + Py_BEGIN_ALLOW_THREADS; + qlgetz(li, fpos, buf, sz); + Py_END_ALLOW_THREADS; + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *gets(size_t len) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(len + 5); + if ( buf == NULL ) + break; + bool ok; + Py_BEGIN_ALLOW_THREADS; + ok = qlgets(buf, len, li) != NULL; + Py_END_ALLOW_THREADS; + if ( !ok ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *read(size_t size) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + ssize_t r; + Py_BEGIN_ALLOW_THREADS; + r = qlread(li, buf, size); + Py_END_ALLOW_THREADS; + if ( r == -1 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + bool opened() + { + return li != NULL; + } + + //-------------------------------------------------------------------------- + PyObject *readbytes(size_t size, bool big_endian) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + int r; + Py_BEGIN_ALLOW_THREADS; + r = lreadbytes(li, buf, size, big_endian); + Py_END_ALLOW_THREADS; + if ( r == -1 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) + { + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = ::file2base(li, pos, ea1, ea2, patchable); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int32 size() + { + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qlsize(li); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + PyObject *filename() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyString_FromString(fn.c_str()); + } + + //-------------------------------------------------------------------------- + PyObject *get_char() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qlgetc(li); + Py_END_ALLOW_THREADS; + if ( ch == EOF ) + Py_RETURN_NONE; + return Py_BuildValue("c", ch); + } +}; +// + +#endif diff --git a/pywraps/py_loader.hpp b/pywraps/py_loader.hpp index 4eb78ff7..99493b9f 100644 --- a/pywraps/py_loader.hpp +++ b/pywraps/py_loader.hpp @@ -1,92 +1,92 @@ -#ifndef __PY_LOADER___ -#define __PY_LOADER___ - -//------------------------------------------------------------------------ -// - -//------------------------------------------------------------------------ -/* -# -def mem2base(mem, ea, fpos): - """ - Load database from the memory. - @param mem: the buffer - @param ea: start linear addresses - @param fpos: position in the input file the data is taken from. - if == -1, then no file position correspond to the data. - @return: - - Returns zero if the passed buffer was not a string - - Otherwise 1 is returned - """ - pass -# -*/ -static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) -{ - Py_ssize_t len; - char *buf; - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) - return 0; - } - - return mem2base((void *)buf, ea, ea+len, fpos); -} - -//------------------------------------------------------------------------ -/* -# -def load_plugin(name): - """ - Loads a plugin - @return: - - None if plugin could not be loaded - - An opaque object representing the loaded plugin - """ - pass -# -*/ -static PyObject *py_load_plugin(const char *name) -{ - plugin_t *r = load_plugin(name); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( r == NULL ) - Py_RETURN_NONE; - else - return PyCObject_FromVoidPtr(r, NULL); -} - -//------------------------------------------------------------------------ -/* -# -def run_plugin(plg): - """ - Runs a plugin - @param plg: A plugin object (returned by load_plugin()) - @return: Boolean - """ - pass -# -*/ -static bool py_run_plugin(PyObject *plg, int arg) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(plg) ) - { - return false; - } - else - { - plugin_t *p = (plugin_t *)PyCObject_AsVoidPtr(plg); - bool rc; - Py_BEGIN_ALLOW_THREADS; - rc = run_plugin(p, arg); - Py_END_ALLOW_THREADS; - return rc; - } -} - -// - -#endif +#ifndef __PY_LOADER___ +#define __PY_LOADER___ + +//------------------------------------------------------------------------ +// + +//------------------------------------------------------------------------ +/* +# +def mem2base(mem, ea, fpos): + """ + Load database from the memory. + @param mem: the buffer + @param ea: start linear addresses + @param fpos: position in the input file the data is taken from. + if == -1, then no file position correspond to the data. + @return: + - Returns zero if the passed buffer was not a string + - Otherwise 1 is returned + """ + pass +# +*/ +static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) +{ + Py_ssize_t len; + char *buf; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) + return 0; + } + + return mem2base((void *)buf, ea, ea+len, fpos); +} + +//------------------------------------------------------------------------ +/* +# +def load_plugin(name): + """ + Loads a plugin + @return: + - None if plugin could not be loaded + - An opaque object representing the loaded plugin + """ + pass +# +*/ +static PyObject *py_load_plugin(const char *name) +{ + plugin_t *r = load_plugin(name); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( r == NULL ) + Py_RETURN_NONE; + else + return PyCObject_FromVoidPtr(r, NULL); +} + +//------------------------------------------------------------------------ +/* +# +def run_plugin(plg): + """ + Runs a plugin + @param plg: A plugin object (returned by load_plugin()) + @return: Boolean + """ + pass +# +*/ +static bool py_run_plugin(PyObject *plg, int arg) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(plg) ) + { + return false; + } + else + { + plugin_t *p = (plugin_t *)PyCObject_AsVoidPtr(plg); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = run_plugin(p, arg); + Py_END_ALLOW_THREADS; + return rc; + } +} + +// + +#endif diff --git a/pywraps/py_nalt.hpp b/pywraps/py_nalt.hpp index bf8ae460..4564d82e 100644 --- a/pywraps/py_nalt.hpp +++ b/pywraps/py_nalt.hpp @@ -1,546 +1,546 @@ -#ifndef __PY_IDA_NALT__ -#define __PY_IDA_NALT__ - -// - -//------------------------------------------------------------------------- -// callback for enumerating imports -// ea: import address -// name: import name (NULL if imported by ordinal) -// ord: import ordinal (0 for imports by name) -// param: user parameter passed to enum_import_names() -// return: 1-ok, 0-stop enumeration -static int idaapi py_import_enum_cb( - ea_t ea, - const char *name, - uval_t ord, - void *param) -{ - // If no name, try to get the name associated with the 'ea'. It may be coming from IDS - char name_buf[MAXSTR]; - if ( name == NULL ) - name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf)); - - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_name; - if ( name == NULL ) - py_name = borref_t(Py_None); - else - py_name = newref_t(PyString_FromString(name)); - - newref_t py_ord(Py_BuildValue(PY_FMT64, pyul_t(ord))); - newref_t py_ea(Py_BuildValue(PY_FMT64, pyul_t(ea))); - newref_t py_result( - PyObject_CallFunctionObjArgs( - (PyObject *)param, - py_ea.o, - py_name.o, - py_ord.o, - NULL)); - return py_result != NULL && PyObject_IsTrue(py_result.o) ? 1 : 0; -} - -//------------------------------------------------------------------------- -switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) - return NULL; - - switch_info_ex_t *r; - newref_t attr(PyObject_GetAttrString(self, S_CLINK_NAME)); - return PyCObject_Check(attr.o) ? ((switch_info_ex_t *) PyCObject_AsVoidPtr(attr.o)) : NULL; -} -// - -// - -//------------------------------------------------------------------------- -/* -# -def get_import_module_name(path, fname, callback): - """ - Returns the name of an imported module given its index - @return: None or the module name - """ - pass -# -*/ -static PyObject *py_get_import_module_name(int mod_index) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - char buf[MAXSTR]; - if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) - Py_RETURN_NONE; - - return PyString_FromString(buf); -} - -//------------------------------------------------------------------------- -/* -# -def get_switch_info_ex(ea): - """ - Returns the a switch_info_ex_t structure containing the information about the switch. - Please refer to the SDK sample 'uiswitch' - @return: None or switch_info_ex_t instance - """ - pass -# -*/ -PyObject *py_get_switch_info_ex(ea_t ea) -{ - switch_info_ex_t *ex = new switch_info_ex_t(); - ref_t py_obj; - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 - || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) - { - delete ex; - Py_RETURN_NONE; - } - py_obj.incref(); - return py_obj.o; -} - -//------------------------------------------------------------------------- -/* -# -def create_switch_xrefs(insn_ea, si): - """ - This function creates xrefs from the indirect jump. - - Usually there is no need to call this function directly because the kernel - will call it for switch tables - - Note: Custom switch information are not supported yet. - - @param insn_ea: address of the 'indirect jump' instruction - @param si: switch information - - @return: Boolean - """ - pass -# -*/ -idaman bool ida_export py_create_switch_xrefs( - ea_t insn_ea, - PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return false; - - create_switch_xrefs(insn_ea, swi); - return true; -} - -//------------------------------------------------------------------------- -struct cases_and_targets_t -{ - casevec_t cases; - eavec_t targets; -}; - -//------------------------------------------------------------------------- -/* -# -def calc_switch_cases(insn_ea, si): - """ - Get information about a switch's cases. - - The returned information can be used as follows: - - for idx in xrange(len(results.cases)): - cur_case = results.cases[idx] - for cidx in xrange(len(cur_case)): - print "case: %d" % cur_case[cidx] - print " goto 0x%x" % results.targets[idx] - - @param insn_ea: address of the 'indirect jump' instruction - @param si: switch information - - @return: a structure with 2 members: 'cases', and 'targets'. - """ - pass -# -*/ -idaman cases_and_targets_t *ida_export py_calc_switch_cases( - ea_t insn_ea, - PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return NULL; - - cases_and_targets_t *ct = new cases_and_targets_t; - if ( !calc_switch_cases(insn_ea, swi, &ct->cases, &ct->targets) ) - { - delete ct; - return NULL; - } - - return ct; -} - - -//------------------------------------------------------------------------- -/* -# -def create_switch_table(insn_ea, si): - """ - Create switch table from the switch information - - @param insn_ea: address of the 'indirect jump' instruction - @param si: switch information - - @return: Boolean - """ - pass -# -*/ -idaman bool ida_export py_create_switch_table( - ea_t insn_ea, - PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return false; - - create_switch_table(insn_ea, swi); - return true; -} - -//------------------------------------------------------------------------- -/* -# -def set_switch_info_ex(ea, switch_info_ex): - """ - Saves the switch information in the database - Please refer to the SDK sample 'uiswitch' - @return: Boolean - """ - pass -# -*/ -bool py_set_switch_info_ex(ea_t ea, PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return false; - - set_switch_info_ex(ea, swi); - return true; -} - -//------------------------------------------------------------------------- -/* -# -def del_switch_info_ex(ea): - """ - Deletes stored switch information - """ - pass -# -*/ -void py_del_switch_info_ex(ea_t ea) -{ - del_switch_info_ex(ea); -} - -//------------------------------------------------------------------------- -/* -# -def enum_import_names(mod_index, callback): - """ - Enumerate imports from a specific module. - Please refer to ex_imports.py example. - - @param mod_index: The module index - @param callback: A callable object that will be invoked with an ea, name (could be None) and ordinal. - @return: 1-finished ok, -1 on error, otherwise callback return value (<=0) - """ - pass -# -*/ -static int py_enum_import_names(int mod_index, PyObject *py_cb) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCallable_Check(py_cb) ) - return -1; - return enum_import_names(mod_index, py_import_enum_cb, py_cb); -} - -//------------------------------------------------------------------------- -static PyObject *switch_info_ex_t_create() -{ - switch_info_ex_t *inst = new switch_info_ex_t(); - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(inst, NULL); -} - -//--------------------------------------------------------------------------- -static bool switch_info_ex_t_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); - delete inst; - return true; -} - -static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) -{ - switch_info_ex_t *lhs = switch_info_ex_t_get_clink(self); - switch_info_ex_t *rhs = switch_info_ex_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -// Auto generated - begin -// - -static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", (char)link->regdtyp); -} -static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->regdtyp = (char)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_flags2(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("i", link->flags2); -} -static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->flags2 = (int)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_jcases(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("i", link->jcases); -} -static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->jcases = (int)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_regnum(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("i", (int)link->regnum); -} -static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->regnum = (int)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_flags(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", (ushort)link->flags); -} -static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->flags = (uint16)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_ncases(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", (uint16)link->ncases); -} -static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->ncases = (ushort)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_defjump(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); -} -static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - uint64 v(0); PyW_GetNumber(value, &v); - link->defjump = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_jumps(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->jumps); -} -static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - uint64 v(0); PyW_GetNumber(value, &v); - link->jumps = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_elbase(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); -} -static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->elbase = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_startea(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); -} -static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->startea = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_custom(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); -} -static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->custom = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); -} -static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->ind_lowcase = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->values); -} -static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->values = (pyul_t)v; -} - -// -// Auto generated - end -// -//------------------------------------------------------------------------- -// - -#endif +#ifndef __PY_IDA_NALT__ +#define __PY_IDA_NALT__ + +// + +//------------------------------------------------------------------------- +// callback for enumerating imports +// ea: import address +// name: import name (NULL if imported by ordinal) +// ord: import ordinal (0 for imports by name) +// param: user parameter passed to enum_import_names() +// return: 1-ok, 0-stop enumeration +static int idaapi py_import_enum_cb( + ea_t ea, + const char *name, + uval_t ord, + void *param) +{ + // If no name, try to get the name associated with the 'ea'. It may be coming from IDS + qstring name_buf; + if ( name == NULL && get_true_name(&name_buf, ea) > 0 ) + name = name_buf.begin(); + + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_name; + if ( name == NULL ) + py_name = borref_t(Py_None); + else + py_name = newref_t(PyString_FromString(name)); + + newref_t py_ord(Py_BuildValue(PY_FMT64, pyul_t(ord))); + newref_t py_ea(Py_BuildValue(PY_FMT64, pyul_t(ea))); + newref_t py_result( + PyObject_CallFunctionObjArgs( + (PyObject *)param, + py_ea.o, + py_name.o, + py_ord.o, + NULL)); + return py_result != NULL && PyObject_IsTrue(py_result.o) ? 1 : 0; +} + +//------------------------------------------------------------------------- +switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) + return NULL; + + switch_info_ex_t *r; + newref_t attr(PyObject_GetAttrString(self, S_CLINK_NAME)); + return PyCObject_Check(attr.o) ? ((switch_info_ex_t *) PyCObject_AsVoidPtr(attr.o)) : NULL; +} +// + +// + +//------------------------------------------------------------------------- +/* +# +def get_import_module_name(path, fname, callback): + """ + Returns the name of an imported module given its index + @return: None or the module name + """ + pass +# +*/ +static PyObject *py_get_import_module_name(int mod_index) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + char buf[MAXSTR]; + if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) + Py_RETURN_NONE; + + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +/* +# +def get_switch_info_ex(ea): + """ + Returns the a switch_info_ex_t structure containing the information about the switch. + Please refer to the SDK sample 'uiswitch' + @return: None or switch_info_ex_t instance + """ + pass +# +*/ +PyObject *py_get_switch_info_ex(ea_t ea) +{ + switch_info_ex_t *ex = new switch_info_ex_t(); + ref_t py_obj; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 + || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) + { + delete ex; + Py_RETURN_NONE; + } + py_obj.incref(); + return py_obj.o; +} + +//------------------------------------------------------------------------- +/* +# +def create_switch_xrefs(insn_ea, si): + """ + This function creates xrefs from the indirect jump. + + Usually there is no need to call this function directly because the kernel + will call it for switch tables + + Note: Custom switch information are not supported yet. + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: Boolean + """ + pass +# +*/ +idaman bool ida_export py_create_switch_xrefs( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + create_switch_xrefs(insn_ea, swi); + return true; +} + +//------------------------------------------------------------------------- +struct cases_and_targets_t +{ + casevec_t cases; + eavec_t targets; +}; + +//------------------------------------------------------------------------- +/* +# +def calc_switch_cases(insn_ea, si): + """ + Get information about a switch's cases. + + The returned information can be used as follows: + + for idx in xrange(len(results.cases)): + cur_case = results.cases[idx] + for cidx in xrange(len(cur_case)): + print "case: %d" % cur_case[cidx] + print " goto 0x%x" % results.targets[idx] + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: a structure with 2 members: 'cases', and 'targets'. + """ + pass +# +*/ +idaman cases_and_targets_t *ida_export py_calc_switch_cases( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return NULL; + + cases_and_targets_t *ct = new cases_and_targets_t; + if ( !calc_switch_cases(insn_ea, swi, &ct->cases, &ct->targets) ) + { + delete ct; + return NULL; + } + + return ct; +} + + +//------------------------------------------------------------------------- +/* +# +def create_switch_table(insn_ea, si): + """ + Create switch table from the switch information + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: Boolean + """ + pass +# +*/ +idaman bool ida_export py_create_switch_table( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + create_switch_table(insn_ea, swi); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def set_switch_info_ex(ea, switch_info_ex): + """ + Saves the switch information in the database + Please refer to the SDK sample 'uiswitch' + @return: Boolean + """ + pass +# +*/ +bool py_set_switch_info_ex(ea_t ea, PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + set_switch_info_ex(ea, swi); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def del_switch_info_ex(ea): + """ + Deletes stored switch information + """ + pass +# +*/ +void py_del_switch_info_ex(ea_t ea) +{ + del_switch_info_ex(ea); +} + +//------------------------------------------------------------------------- +/* +# +def enum_import_names(mod_index, callback): + """ + Enumerate imports from a specific module. + Please refer to ex_imports.py example. + + @param mod_index: The module index + @param callback: A callable object that will be invoked with an ea, name (could be None) and ordinal. + @return: 1-finished ok, -1 on error, otherwise callback return value (<=0) + """ + pass +# +*/ +static int py_enum_import_names(int mod_index, PyObject *py_cb) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCallable_Check(py_cb) ) + return -1; + return enum_import_names(mod_index, py_import_enum_cb, py_cb); +} + +//------------------------------------------------------------------------- +static PyObject *switch_info_ex_t_create() +{ + switch_info_ex_t *inst = new switch_info_ex_t(); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(inst, NULL); +} + +//--------------------------------------------------------------------------- +static bool switch_info_ex_t_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); + delete inst; + return true; +} + +static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) +{ + switch_info_ex_t *lhs = switch_info_ex_t_get_clink(self); + switch_info_ex_t *rhs = switch_info_ex_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +// Auto generated - begin +// + +static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", (char)link->regdtyp); +} +static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->regdtyp = (char)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_flags2(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", link->flags2); +} +static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->flags2 = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_jcases(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", link->jcases); +} +static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->jcases = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_regnum(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", (int)link->regnum); +} +static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->regnum = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_flags(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", (ushort)link->flags); +} +static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->flags = (uint16)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_ncases(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", (uint16)link->ncases); +} +static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->ncases = (ushort)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_defjump(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); +} +static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 v(0); PyW_GetNumber(value, &v); + link->defjump = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_jumps(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->jumps); +} +static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 v(0); PyW_GetNumber(value, &v); + link->jumps = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_elbase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); +} +static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->elbase = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_startea(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); +} +static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->startea = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_custom(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); +} +static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->custom = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); +} +static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->ind_lowcase = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->values); +} +static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->values = (pyul_t)v; +} + +// +// Auto generated - end +// +//------------------------------------------------------------------------- +// + +#endif diff --git a/pywraps/py_nalt.py b/pywraps/py_nalt.py index c1b16627..a27c5a9d 100644 --- a/pywraps/py_nalt.py +++ b/pywraps/py_nalt.py @@ -1,191 +1,191 @@ -# -SWI_SPARSE = 0x1 -"""sparse switch ( value table present ) otherwise lowcase present""" -SWI_V32 = 0x2 -"""32-bit values in table""" -SWI_J32 = 0x4 -"""32-bit jump offsets""" -SWI_VSPLIT = 0x8 -"""value table is split (only for 32-bit values)""" -SWI_DEFAULT = 0x10 -"""default case is present""" -SWI_END_IN_TBL = 0x20 -"""switchend in table (default entry)""" -SWI_JMP_INV = 0x40 -"""jumptable is inversed (last entry is for first entry in values table)""" -SWI_SHIFT_MASK = 0x180 -"""use formula (element*shift + elbase) to find jump targets""" - -SWI_ELBASE = 0x200 -"""elbase is present (if not and shift!=0, endof(jumpea) is used)""" -SWI_JSIZE = 0x400 -"""jump offset expansion bit""" - -SWI_VSIZE = 0x800 -"""value table element size expansion bit""" - -SWI_SEPARATE = 0x1000 -"""do not create an array of individual dwords""" - -SWI_SIGNED = 0x2000 -"""jump table entries are signed""" - -SWI_CUSTOM = 0x4000 -"""custom jump table - ph.create_switch_xrefs will be called to create code xrefs for the table. it must return 2. custom jump table must be created by the module""" - -SWI_EXTENDED = 0x8000 -"""this is switch_info_ex_t""" - -SWI2_INDIRECT = 0x0001 -"""value table elements are used as indexes into the jump table""" -SWI2_SUBTRACT = 0x0002 -"""table values are subtracted from the elbase instead of being addded""" - -# -------------------------------------------------------------------------- -class switch_info_ex_t(py_clinked_object_t): - def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) - - def _create_clink(self): - return _idaapi.switch_info_ex_t_create() - - def _del_clink(self, lnk): - return _idaapi.switch_info_ex_t_destroy(lnk) - - def assign(self, other): - return _idaapi.switch_info_ex_t_assign(self, other) - - def is_indirect(self): - return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_INDIRECT) != 0 - - def is_subtract(self): - return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_SUBTRACT) != 0 - - def get_jtable_size(self): - return self.jcases if self.is_indirect() else self.ncases - - def get_lowcase(self): - return self.ind_lowcase if self.is_indirect() else self.lowcase - - def set_expr(self, r, dt): - self.regnum = r - self.regdtyp = dt - - def get_shift(self): - return (self.flags & SWI_SHIFT_MASK) >> 7 - - def set_shift(self, shift): - self.flags &= ~SWI_SHIFT_MASK - self.flags |= ((shift & 3) << 7) - - def get_jtable_element_size(self): - code = self.flags & (SWI_J32|SWI_JSIZE) - if code == 0: return 2 - elif code == SWI_J32: return 4 - elif code == SWI_JSIZE: return 1 - else: return 8 - - def set_jtable_element_size(self, size): - self.flags &= ~(SWI_J32|SWI_JSIZE) - if size == 4: self.flags |= SWI_J32 - elif size == 1: self.flags |= SWI_JSIZE - elif size == 8: self.flags |= SWI_J32|SWI_JSIZE - elif size != 2: return False - return True - - def get_vtable_element_size(self): - code = self.flags & (SWI_V32|SWI_VSIZE) - if code == 0: return 2 - elif code == SWI_V32: return 4 - elif code == SWI_VSIZE: return 1 - return 8 - - def set_vtable_element_size(self, size): - self.flags &= ~SWI_V32|SWI_VSIZE - if size == 4: self.flags |= SWI_V32 - elif size == 1: self.flags |= SWI_VSIZE - elif size == 8: self.flags |= SWI_V32|SWI_VSIZE - elif size != 2: return False - return True - - # - # Autogenerated - # - def __get_regdtyp__(self): - return _idaapi.switch_info_ex_t_get_regdtyp(self) - def __set_regdtyp__(self, v): - _idaapi.switch_info_ex_t_set_regdtyp(self, v) - def __get_flags2__(self): - return _idaapi.switch_info_ex_t_get_flags2(self) - def __set_flags2__(self, v): - _idaapi.switch_info_ex_t_set_flags2(self, v) - def __get_jcases__(self): - return _idaapi.switch_info_ex_t_get_jcases(self) - def __set_jcases__(self, v): - _idaapi.switch_info_ex_t_set_jcases(self, v) - def __get_regnum__(self): - return _idaapi.switch_info_ex_t_get_regnum(self) - def __set_regnum__(self, v): - _idaapi.switch_info_ex_t_set_regnum(self, v) - def __get_flags__(self): - return _idaapi.switch_info_ex_t_get_flags(self) - def __set_flags__(self, v): - _idaapi.switch_info_ex_t_set_flags(self, v) - def __get_ncases__(self): - return _idaapi.switch_info_ex_t_get_ncases(self) - def __set_ncases__(self, v): - _idaapi.switch_info_ex_t_set_ncases(self, v) - def __get_defjump__(self): - return _idaapi.switch_info_ex_t_get_defjump(self) - def __set_defjump__(self, v): - _idaapi.switch_info_ex_t_set_defjump(self, v) - def __get_jumps__(self): - return _idaapi.switch_info_ex_t_get_jumps(self) - def __set_jumps__(self, v): - _idaapi.switch_info_ex_t_set_jumps(self, v) - def __get_elbase__(self): - return _idaapi.switch_info_ex_t_get_elbase(self) - def __set_elbase__(self, v): - _idaapi.switch_info_ex_t_set_elbase(self, v) - def __get_startea__(self): - return _idaapi.switch_info_ex_t_get_startea(self) - def __set_startea__(self, v): - _idaapi.switch_info_ex_t_set_startea(self, v) - def __get_custom__(self): - return _idaapi.switch_info_ex_t_get_custom(self) - def __set_custom__(self, v): - _idaapi.switch_info_ex_t_set_custom(self, v) - def __get_ind_lowcase__(self): - return _idaapi.switch_info_ex_t_get_ind_lowcase(self) - def __set_ind_lowcase__(self, v): - _idaapi.switch_info_ex_t_set_ind_lowcase(self, v) - def __get_values_lowcase__(self): - return _idaapi.switch_info_ex_t_get_values_lowcase(self) - def __set_values_lowcase__(self, v): - _idaapi.switch_info_ex_t_set_values_lowcase(self, v) - regdtyp = property(__get_regdtyp__, __set_regdtyp__) - """size of the switch expression register as dtyp""" - flags2 = property(__get_flags2__, __set_flags2__) - jcases = property(__get_jcases__, __set_jcases__) - """number of entries in the jump table (SWI2_INDIRECT)""" - regnum = property(__get_regnum__, __set_regnum__) - """the switch expression as a register number""" - flags = property(__get_flags__, __set_flags__) - """the switch expression as a register number""" - ncases = property(__get_ncases__, __set_ncases__) - """number of cases (excluding default)""" - defjump = property(__get_defjump__, __set_defjump__) - """default jump address""" - jumps = property(__get_jumps__, __set_jumps__) - """jump table address""" - elbase = property(__get_elbase__, __set_elbase__) - """element base""" - startea = property(__get_startea__, __set_startea__) - """start of switch idiom""" - custom = property(__get_custom__, __set_custom__) - """information for custom tables (filled and used by modules)""" - ind_lowcase = property(__get_ind_lowcase__, __set_ind_lowcase__) - values = property(__get_values_lowcase__, __set_values_lowcase__) - lowcase = property(__get_values_lowcase__, __set_values_lowcase__) - +# +SWI_SPARSE = 0x1 +"""sparse switch ( value table present ) otherwise lowcase present""" +SWI_V32 = 0x2 +"""32-bit values in table""" +SWI_J32 = 0x4 +"""32-bit jump offsets""" +SWI_VSPLIT = 0x8 +"""value table is split (only for 32-bit values)""" +SWI_DEFAULT = 0x10 +"""default case is present""" +SWI_END_IN_TBL = 0x20 +"""switchend in table (default entry)""" +SWI_JMP_INV = 0x40 +"""jumptable is inversed (last entry is for first entry in values table)""" +SWI_SHIFT_MASK = 0x180 +"""use formula (element*shift + elbase) to find jump targets""" + +SWI_ELBASE = 0x200 +"""elbase is present (if not and shift!=0, endof(jumpea) is used)""" +SWI_JSIZE = 0x400 +"""jump offset expansion bit""" + +SWI_VSIZE = 0x800 +"""value table element size expansion bit""" + +SWI_SEPARATE = 0x1000 +"""do not create an array of individual dwords""" + +SWI_SIGNED = 0x2000 +"""jump table entries are signed""" + +SWI_CUSTOM = 0x4000 +"""custom jump table - ph.create_switch_xrefs will be called to create code xrefs for the table. it must return 2. custom jump table must be created by the module""" + +SWI_EXTENDED = 0x8000 +"""this is switch_info_ex_t""" + +SWI2_INDIRECT = 0x0001 +"""value table elements are used as indexes into the jump table""" +SWI2_SUBTRACT = 0x0002 +"""table values are subtracted from the elbase instead of being addded""" + +# -------------------------------------------------------------------------- +class switch_info_ex_t(py_clinked_object_t): + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + def _create_clink(self): + return _idaapi.switch_info_ex_t_create() + + def _del_clink(self, lnk): + return _idaapi.switch_info_ex_t_destroy(lnk) + + def assign(self, other): + return _idaapi.switch_info_ex_t_assign(self, other) + + def is_indirect(self): + return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_INDIRECT) != 0 + + def is_subtract(self): + return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_SUBTRACT) != 0 + + def get_jtable_size(self): + return self.jcases if self.is_indirect() else self.ncases + + def get_lowcase(self): + return self.ind_lowcase if self.is_indirect() else self.lowcase + + def set_expr(self, r, dt): + self.regnum = r + self.regdtyp = dt + + def get_shift(self): + return (self.flags & SWI_SHIFT_MASK) >> 7 + + def set_shift(self, shift): + self.flags &= ~SWI_SHIFT_MASK + self.flags |= ((shift & 3) << 7) + + def get_jtable_element_size(self): + code = self.flags & (SWI_J32|SWI_JSIZE) + if code == 0: return 2 + elif code == SWI_J32: return 4 + elif code == SWI_JSIZE: return 1 + else: return 8 + + def set_jtable_element_size(self, size): + self.flags &= ~(SWI_J32|SWI_JSIZE) + if size == 4: self.flags |= SWI_J32 + elif size == 1: self.flags |= SWI_JSIZE + elif size == 8: self.flags |= SWI_J32|SWI_JSIZE + elif size != 2: return False + return True + + def get_vtable_element_size(self): + code = self.flags & (SWI_V32|SWI_VSIZE) + if code == 0: return 2 + elif code == SWI_V32: return 4 + elif code == SWI_VSIZE: return 1 + return 8 + + def set_vtable_element_size(self, size): + self.flags &= ~SWI_V32|SWI_VSIZE + if size == 4: self.flags |= SWI_V32 + elif size == 1: self.flags |= SWI_VSIZE + elif size == 8: self.flags |= SWI_V32|SWI_VSIZE + elif size != 2: return False + return True + + # + # Autogenerated + # + def __get_regdtyp__(self): + return _idaapi.switch_info_ex_t_get_regdtyp(self) + def __set_regdtyp__(self, v): + _idaapi.switch_info_ex_t_set_regdtyp(self, v) + def __get_flags2__(self): + return _idaapi.switch_info_ex_t_get_flags2(self) + def __set_flags2__(self, v): + _idaapi.switch_info_ex_t_set_flags2(self, v) + def __get_jcases__(self): + return _idaapi.switch_info_ex_t_get_jcases(self) + def __set_jcases__(self, v): + _idaapi.switch_info_ex_t_set_jcases(self, v) + def __get_regnum__(self): + return _idaapi.switch_info_ex_t_get_regnum(self) + def __set_regnum__(self, v): + _idaapi.switch_info_ex_t_set_regnum(self, v) + def __get_flags__(self): + return _idaapi.switch_info_ex_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.switch_info_ex_t_set_flags(self, v) + def __get_ncases__(self): + return _idaapi.switch_info_ex_t_get_ncases(self) + def __set_ncases__(self, v): + _idaapi.switch_info_ex_t_set_ncases(self, v) + def __get_defjump__(self): + return _idaapi.switch_info_ex_t_get_defjump(self) + def __set_defjump__(self, v): + _idaapi.switch_info_ex_t_set_defjump(self, v) + def __get_jumps__(self): + return _idaapi.switch_info_ex_t_get_jumps(self) + def __set_jumps__(self, v): + _idaapi.switch_info_ex_t_set_jumps(self, v) + def __get_elbase__(self): + return _idaapi.switch_info_ex_t_get_elbase(self) + def __set_elbase__(self, v): + _idaapi.switch_info_ex_t_set_elbase(self, v) + def __get_startea__(self): + return _idaapi.switch_info_ex_t_get_startea(self) + def __set_startea__(self, v): + _idaapi.switch_info_ex_t_set_startea(self, v) + def __get_custom__(self): + return _idaapi.switch_info_ex_t_get_custom(self) + def __set_custom__(self, v): + _idaapi.switch_info_ex_t_set_custom(self, v) + def __get_ind_lowcase__(self): + return _idaapi.switch_info_ex_t_get_ind_lowcase(self) + def __set_ind_lowcase__(self, v): + _idaapi.switch_info_ex_t_set_ind_lowcase(self, v) + def __get_values_lowcase__(self): + return _idaapi.switch_info_ex_t_get_values_lowcase(self) + def __set_values_lowcase__(self, v): + _idaapi.switch_info_ex_t_set_values_lowcase(self, v) + regdtyp = property(__get_regdtyp__, __set_regdtyp__) + """size of the switch expression register as dtyp""" + flags2 = property(__get_flags2__, __set_flags2__) + jcases = property(__get_jcases__, __set_jcases__) + """number of entries in the jump table (SWI2_INDIRECT)""" + regnum = property(__get_regnum__, __set_regnum__) + """the switch expression as a register number""" + flags = property(__get_flags__, __set_flags__) + """the switch expression as a register number""" + ncases = property(__get_ncases__, __set_ncases__) + """number of cases (excluding default)""" + defjump = property(__get_defjump__, __set_defjump__) + """default jump address""" + jumps = property(__get_jumps__, __set_jumps__) + """jump table address""" + elbase = property(__get_elbase__, __set_elbase__) + """element base""" + startea = property(__get_startea__, __set_startea__) + """start of switch idiom""" + custom = property(__get_custom__, __set_custom__) + """information for custom tables (filled and used by modules)""" + ind_lowcase = property(__get_ind_lowcase__, __set_ind_lowcase__) + values = property(__get_values_lowcase__, __set_values_lowcase__) + lowcase = property(__get_values_lowcase__, __set_values_lowcase__) + # \ No newline at end of file diff --git a/pywraps/py_name.hpp b/pywraps/py_name.hpp index 0ca1794c..e36b991e 100644 --- a/pywraps/py_name.hpp +++ b/pywraps/py_name.hpp @@ -1,26 +1,38 @@ -//------------------------------------------------------------------------ -// -//------------------------------------------------------------------------ -PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) -{ - // Get debug names - ea_name_vec_t names; - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_BEGIN_ALLOW_THREADS; - get_debug_names(ea1, ea2, names); - Py_END_ALLOW_THREADS; - PyObject *dict = Py_BuildValue("{}"); - if (dict != NULL) - { - for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) - { - PyDict_SetItem(dict, - Py_BuildValue(PY_FMT64, it->ea), - PyString_FromString(it->name.c_str())); - } - } - return dict; -} -//------------------------------------------------------------------------ -// -//------------------------------------------------------------------------ +//------------------------------------------------------------------------- +// +// + +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ +PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) +{ + // Get debug names + ea_name_vec_t names; + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_BEGIN_ALLOW_THREADS; + get_debug_names(ea1, ea2, names); + Py_END_ALLOW_THREADS; + PyObject *dict = Py_BuildValue("{}"); + if (dict != NULL) + { + for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + { + PyDict_SetItem(dict, + Py_BuildValue(PY_FMT64, it->ea), + PyString_FromString(it->name.c_str())); + } + } + return dict; +} + +//------------------------------------------------------------------------- +inline qstring py_get_ea_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, gtn_flags); + return out; +} +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ diff --git a/pywraps/py_name.py b/pywraps/py_name.py index 2f6e01d1..0cf43cb6 100644 --- a/pywraps/py_name.py +++ b/pywraps/py_name.py @@ -1,52 +1,54 @@ -import bisect - -# - -class NearestName: - """ - Utility class to help find the nearest name in a given ea/name dictionary - """ - def __init__(self, ea_names): - self.update(ea_names) - - - def update(self, ea_names): - """Updates the ea/names map""" - self._names = ea_names - self._addrs = ea_names.keys() - self._addrs.sort() - - - def find(self, ea): - """ - Returns a tupple (ea, name, pos) that is the nearest to the passed ea - If no name is matched then None is returned - """ - pos = bisect.bisect_left(self._addrs, ea) - # no match - if pos >= len(self._addrs): - return None - # exact match? - if self._addrs[pos] != ea: - pos -= 1 # go to previous element - if pos < 0: - return None - return self[pos] - - - def _get_item(self, index): - ea = self._addrs[index] - return (ea, self._names[ea], index) - - - def __iter__(self): - return (self._get_item(index) for index in xrange(0, len(self._addrs))) - - - def __getitem__(self, index): - """Returns the tupple (ea, name, index)""" - if index > len(self._addrs): - raise StopIteration - return self._get_item(index) - -# \ No newline at end of file +import bisect + +# + +class NearestName: + """ + Utility class to help find the nearest name in a given ea/name dictionary + """ + def __init__(self, ea_names): + self.update(ea_names) + + + def update(self, ea_names): + """Updates the ea/names map""" + self._names = ea_names + self._addrs = ea_names.keys() + self._addrs.sort() + + + def find(self, ea): + """ + Returns a tupple (ea, name, pos) that is the nearest to the passed ea + If no name is matched then None is returned + """ + pos = bisect.bisect_left(self._addrs, ea) + # no match + if pos >= len(self._addrs): + return None + # exact match? + if self._addrs[pos] != ea: + pos -= 1 # go to previous element + if pos < 0: + return None + return self[pos] + + + def _get_item(self, index): + ea = self._addrs[index] + return (ea, self._names[ea], index) + + + def __iter__(self): + return (self._get_item(index) for index in xrange(0, len(self._addrs))) + + + def __getitem__(self, index): + """Returns the tupple (ea, name, index)""" + if index > len(self._addrs): + raise StopIteration + return self._get_item(index) + +extract_name = extract_name2 + +# diff --git a/pywraps/py_notifywhen.hpp b/pywraps/py_notifywhen.hpp index 61fef9cb..2ecbf3c6 100644 --- a/pywraps/py_notifywhen.hpp +++ b/pywraps/py_notifywhen.hpp @@ -1,310 +1,310 @@ -#ifndef __PYWRAPS_NOTIFY_WHEN__ -#define __PYWRAPS_NOTIFY_WHEN__ - -//------------------------------------------------------------------------ -// -//------------------------------------------------------------------------ - -//------------------------------------------------------------------------ -class pywraps_notify_when_t -{ - ref_vec_t table[NW_EVENTSCNT]; - qstring err; - bool in_notify; - struct notify_when_args_t - { - int when; - PyObject *py_callable; - }; - typedef qvector notify_when_args_vec_t; - notify_when_args_vec_t delayed_notify_when_list; - - //------------------------------------------------------------------------ - static int idaapi idp_callback(void *ud, int event_id, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; - switch ( event_id ) - { - case processor_t::newfile: - case processor_t::oldfile: - { - int old = event_id == processor_t::oldfile ? 1 : 0; - char *dbname = va_arg(va, char *); - _this->notify(NW_OPENIDB_SLOT, old); - } - break; - case processor_t::closebase: - _this->notify(NW_CLOSEIDB_SLOT); - break; - } - // event not processed, let other plugins or the processor module handle it - return 0; - } - - //------------------------------------------------------------------------ - bool unnotify_when(int when, PyObject *py_callable) - { - int cnt = 0; - for ( int slot=0; slot 0; - } - - //------------------------------------------------------------------------ - void register_callback(int slot, PyObject *py_callable) - { - borref_t callable_ref(py_callable); - ref_vec_t &tbl = table[slot]; - ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); - - // Already added - if ( it != it_end ) - return; - - // Insert the element - tbl.push_back(callable_ref); - } - - //------------------------------------------------------------------------ - void unregister_callback(int slot, PyObject *py_callable) - { - borref_t callable_ref(py_callable); - ref_vec_t &tbl = table[slot]; - ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); - - // Not found? - if ( it == it_end ) - return; - - // Delete the element - tbl.erase(it); - } - -public: - //------------------------------------------------------------------------ - bool init() - { - return hook_to_notification_point(HT_IDP, idp_callback, this); - } - - //------------------------------------------------------------------------ - bool deinit() - { - // Uninstall all objects - ref_vec_t::iterator it, it_end; - for ( int slot=0; sloto); - } - // ...and remove the notification - return unhook_from_notification_point(HT_IDP, idp_callback, this); - } - - //------------------------------------------------------------------------ - bool notify_when(int when, PyObject *py_callable) - { - // While in notify() do not allow insertion or deletion to happen on the spot - // Instead we will queue them so that notify() will carry the action when it finishes - // dispatching the notification handlers - if ( in_notify ) - { - notify_when_args_t &args = delayed_notify_when_list.push_back(); - args.when = when; - args.py_callable = py_callable; - return true; - } - // Uninstalling the notification? - if ( (when & NW_REMOVE) != 0 ) - return unnotify_when(when & ~NW_REMOVE, py_callable); - - int cnt = 0; - for ( int slot=0; slot 0; - } - - //------------------------------------------------------------------------ - bool notify(int slot, ...) - { - va_list va; - va_start(va, slot); - bool ok = notify_va(slot, va); - va_end(va); - return ok; - } - - //------------------------------------------------------------------------ - bool notify_va(int slot, va_list va) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Sanity bounds check! - if ( slot < 0 || slot >= NW_EVENTSCNT ) - return false; - - bool ok = true; - in_notify = true; - int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; - - { - for (ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); - it != it_end; - ++it) - { - // Form the notification code - newref_t py_code(PyInt_FromLong(1 << slot)); - ref_t py_result; - switch ( slot ) - { - case NW_CLOSEIDB_SLOT: - case NW_INITIDA_SLOT: - case NW_TERMIDA_SLOT: - { - py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); - break; - } - case NW_OPENIDB_SLOT: - { - newref_t py_old(PyInt_FromLong(old)); - py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); - } - break; - } - if ( PyW_GetError(&err) || py_result == NULL ) - { - PyErr_Clear(); - warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); - ok = false; - } - } - } - in_notify = false; - - // Process any delayed notify_when() calls that - if ( !delayed_notify_when_list.empty() ) - { - for (notify_when_args_vec_t::iterator it = delayed_notify_when_list.begin(), it_end=delayed_notify_when_list.end(); - it != it_end; - ++it) - { - notify_when(it->when, it->py_callable); - } - delayed_notify_when_list.qclear(); - } - - return ok; - } - - //------------------------------------------------------------------------ - pywraps_notify_when_t() - { - in_notify = false; - } -}; - -static pywraps_notify_when_t *g_nw = NULL; - -//------------------------------------------------------------------------ -// Initializes the notify_when mechanism -// (Normally called by IDAPython plugin.init()) -bool pywraps_nw_init() -{ - if ( g_nw != NULL ) - return true; - - g_nw = new pywraps_notify_when_t(); - if ( g_nw->init() ) - return true; - - // Things went bad, undo! - delete g_nw; - g_nw = NULL; - return false; -} - -//------------------------------------------------------------------------ -bool pywraps_nw_notify(int slot, ...) -{ - if ( g_nw == NULL ) - return false; - - // Appears to be called from 'driver_notifywhen.cpp', which - // itself is called from possibly non-python code. - // I.e., we must acquire the GIL. - PYW_GIL_GET; - va_list va; - va_start(va, slot); - bool ok = g_nw->notify_va(slot, va); - va_end(va); - - return ok; -} - -//------------------------------------------------------------------------ -// Deinitializes the notify_when mechanism -bool pywraps_nw_term() -{ - if ( g_nw == NULL ) - return true; - - // If could not deinitialize then return w/o stopping nw - if ( !g_nw->deinit() ) - return false; - - // Cleanup - delete g_nw; - g_nw = NULL; - return true; -} - -// -//------------------------------------------------------------------------ - -// - -//------------------------------------------------------------------------ -/* -# -def notify_when(when, callback): - """ - Register a callback that will be called when an event happens. - @param when: one of NW_XXXX constants - @param callback: This callback prototype varies depending on the 'when' parameter: - The general callback format: - def notify_when_callback(nw_code) - In the case of NW_OPENIDB: - def notify_when_callback(nw_code, is_old_database) - @return: Boolean - """ - pass -# -*/ -static bool notify_when(int when, PyObject *py_callable) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( g_nw == NULL || !PyCallable_Check(py_callable) ) - return false; - return g_nw->notify_when(when, py_callable); -} - -// -//------------------------------------------------------------------------ - -#endif +#ifndef __PYWRAPS_NOTIFY_WHEN__ +#define __PYWRAPS_NOTIFY_WHEN__ + +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +class pywraps_notify_when_t +{ + ref_vec_t table[NW_EVENTSCNT]; + qstring err; + bool in_notify; + struct notify_when_args_t + { + int when; + PyObject *py_callable; + }; + typedef qvector notify_when_args_vec_t; + notify_when_args_vec_t delayed_notify_when_list; + + //------------------------------------------------------------------------ + static int idaapi idp_callback(void *ud, int event_id, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; + switch ( event_id ) + { + case processor_t::newfile: + case processor_t::oldfile: + { + int old = event_id == processor_t::oldfile ? 1 : 0; + char *dbname = va_arg(va, char *); + _this->notify(NW_OPENIDB_SLOT, old); + } + break; + case processor_t::closebase: + _this->notify(NW_CLOSEIDB_SLOT); + break; + } + // event not processed, let other plugins or the processor module handle it + return 0; + } + + //------------------------------------------------------------------------ + bool unnotify_when(int when, PyObject *py_callable) + { + int cnt = 0; + for ( int slot=0; slot 0; + } + + //------------------------------------------------------------------------ + void register_callback(int slot, PyObject *py_callable) + { + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); + + // Already added + if ( it != it_end ) + return; + + // Insert the element + tbl.push_back(callable_ref); + } + + //------------------------------------------------------------------------ + void unregister_callback(int slot, PyObject *py_callable) + { + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); + + // Not found? + if ( it == it_end ) + return; + + // Delete the element + tbl.erase(it); + } + +public: + //------------------------------------------------------------------------ + bool init() + { + return hook_to_notification_point(HT_IDP, idp_callback, this); + } + + //------------------------------------------------------------------------ + bool deinit() + { + // Uninstall all objects + ref_vec_t::iterator it, it_end; + for ( int slot=0; sloto); + } + // ...and remove the notification + return unhook_from_notification_point(HT_IDP, idp_callback, this); + } + + //------------------------------------------------------------------------ + bool notify_when(int when, PyObject *py_callable) + { + // While in notify() do not allow insertion or deletion to happen on the spot + // Instead we will queue them so that notify() will carry the action when it finishes + // dispatching the notification handlers + if ( in_notify ) + { + notify_when_args_t &args = delayed_notify_when_list.push_back(); + args.when = when; + args.py_callable = py_callable; + return true; + } + // Uninstalling the notification? + if ( (when & NW_REMOVE) != 0 ) + return unnotify_when(when & ~NW_REMOVE, py_callable); + + int cnt = 0; + for ( int slot=0; slot 0; + } + + //------------------------------------------------------------------------ + bool notify(int slot, ...) + { + va_list va; + va_start(va, slot); + bool ok = notify_va(slot, va); + va_end(va); + return ok; + } + + //------------------------------------------------------------------------ + bool notify_va(int slot, va_list va) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Sanity bounds check! + if ( slot < 0 || slot >= NW_EVENTSCNT ) + return false; + + bool ok = true; + in_notify = true; + int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; + + { + for (ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); + it != it_end; + ++it) + { + // Form the notification code + newref_t py_code(PyInt_FromLong(1 << slot)); + ref_t py_result; + switch ( slot ) + { + case NW_CLOSEIDB_SLOT: + case NW_INITIDA_SLOT: + case NW_TERMIDA_SLOT: + { + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); + break; + } + case NW_OPENIDB_SLOT: + { + newref_t py_old(PyInt_FromLong(old)); + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); + } + break; + } + if ( PyW_GetError(&err) || py_result == NULL ) + { + PyErr_Clear(); + warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); + ok = false; + } + } + } + in_notify = false; + + // Process any delayed notify_when() calls that + if ( !delayed_notify_when_list.empty() ) + { + for (notify_when_args_vec_t::iterator it = delayed_notify_when_list.begin(), it_end=delayed_notify_when_list.end(); + it != it_end; + ++it) + { + notify_when(it->when, it->py_callable); + } + delayed_notify_when_list.qclear(); + } + + return ok; + } + + //------------------------------------------------------------------------ + pywraps_notify_when_t() + { + in_notify = false; + } +}; + +static pywraps_notify_when_t *g_nw = NULL; + +//------------------------------------------------------------------------ +// Initializes the notify_when mechanism +// (Normally called by IDAPython plugin.init()) +bool pywraps_nw_init() +{ + if ( g_nw != NULL ) + return true; + + g_nw = new pywraps_notify_when_t(); + if ( g_nw->init() ) + return true; + + // Things went bad, undo! + delete g_nw; + g_nw = NULL; + return false; +} + +//------------------------------------------------------------------------ +bool pywraps_nw_notify(int slot, ...) +{ + if ( g_nw == NULL ) + return false; + + // Appears to be called from 'driver_notifywhen.cpp', which + // itself is called from possibly non-python code. + // I.e., we must acquire the GIL. + PYW_GIL_GET; + va_list va; + va_start(va, slot); + bool ok = g_nw->notify_va(slot, va); + va_end(va); + + return ok; +} + +//------------------------------------------------------------------------ +// Deinitializes the notify_when mechanism +bool pywraps_nw_term() +{ + if ( g_nw == NULL ) + return true; + + // If could not deinitialize then return w/o stopping nw + if ( !g_nw->deinit() ) + return false; + + // Cleanup + delete g_nw; + g_nw = NULL; + return true; +} + +// +//------------------------------------------------------------------------ + +// + +//------------------------------------------------------------------------ +/* +# +def notify_when(when, callback): + """ + Register a callback that will be called when an event happens. + @param when: one of NW_XXXX constants + @param callback: This callback prototype varies depending on the 'when' parameter: + The general callback format: + def notify_when_callback(nw_code) + In the case of NW_OPENIDB: + def notify_when_callback(nw_code, is_old_database) + @return: Boolean + """ + pass +# +*/ +static bool notify_when(int when, PyObject *py_callable) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( g_nw == NULL || !PyCallable_Check(py_callable) ) + return false; + return g_nw->notify_when(when, py_callable); +} + +// +//------------------------------------------------------------------------ + +#endif diff --git a/pywraps/py_notifywhen.py b/pywraps/py_notifywhen.py index bb070417..4789d9c4 100644 --- a/pywraps/py_notifywhen.py +++ b/pywraps/py_notifywhen.py @@ -1,63 +1,63 @@ -# ----------------------------------------------------------------------- -# Standalone and testing code -import sys -try: - import pywraps - pywraps_there = True - print "Using pywraps" -except: - pywraps_there = False - print "Not using pywraps" - -try: - import _idaapi -except: - print "Please try me from inside IDA" - sys.exit(0) - -import struct - -if pywraps_there: - _idaapi.notify_when = pywraps.notify_when - -# ----------------------------------------------------------------------- -# -# The general callback format of notify_when() is: -# def notify_when_callback(nw_code) -# In the case of NW_OPENIDB, the callback is: -# def notify_when_callback(nw_code, is_old_database) -NW_OPENIDB = 0x0001 -"""Notify when the database is opened. Its callback is of the form: def notify_when_callback(nw_code, is_old_database)""" -NW_CLOSEIDB = 0x0002 -"""Notify when the database is closed. Its callback is of the form: def notify_when_callback(nw_code)""" -NW_INITIDA = 0x0004 -"""Notify when the IDA starts. Its callback is of the form: def notify_when_callback(nw_code)""" -NW_TERMIDA = 0x0008 -"""Notify when the IDA terminates. Its callback is of the form: def notify_when_callback(nw_code)""" -NW_REMOVE = 0x0010 -"""Use this flag with other flags to uninstall a notifywhen callback""" - -# -# ----------------------------------------------------------------------- - -def nw_openidb(code, old): - print "Open IDB, old=", old - -def nw_closeidb(code): - print "Close IDB" - -def nw_openclose(code, old = None): - if code == NW_CLOSEIDB: - print "openclose: Close IDB" - elif code == NW_OPENIDB: - print "openclose: Open IDB, old=", old - -def nw_closeida(code): - import ctypes - user32 = ctypes.windll.user32 - user32.MessageBoxA(0, "Close IDA", "Info", 0) - -print "registering nw_openidb->", _idaapi.notify_when(NW_OPENIDB, nw_openidb) -print "registering nw_closeidb->", _idaapi.notify_when(NW_CLOSEIDB, nw_closeidb) -print "registering nw_openclose->", _idaapi.notify_when(NW_OPENIDB|NW_CLOSEIDB, nw_openclose) -print "registering nw_closeida->", _idaapi.notify_when(NW_TERMIDA, nw_closeida) +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Not using pywraps" + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +import struct + +if pywraps_there: + _idaapi.notify_when = pywraps.notify_when + +# ----------------------------------------------------------------------- +# +# The general callback format of notify_when() is: +# def notify_when_callback(nw_code) +# In the case of NW_OPENIDB, the callback is: +# def notify_when_callback(nw_code, is_old_database) +NW_OPENIDB = 0x0001 +"""Notify when the database is opened. Its callback is of the form: def notify_when_callback(nw_code, is_old_database)""" +NW_CLOSEIDB = 0x0002 +"""Notify when the database is closed. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_INITIDA = 0x0004 +"""Notify when the IDA starts. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_TERMIDA = 0x0008 +"""Notify when the IDA terminates. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_REMOVE = 0x0010 +"""Use this flag with other flags to uninstall a notifywhen callback""" + +# +# ----------------------------------------------------------------------- + +def nw_openidb(code, old): + print "Open IDB, old=", old + +def nw_closeidb(code): + print "Close IDB" + +def nw_openclose(code, old = None): + if code == NW_CLOSEIDB: + print "openclose: Close IDB" + elif code == NW_OPENIDB: + print "openclose: Open IDB, old=", old + +def nw_closeida(code): + import ctypes + user32 = ctypes.windll.user32 + user32.MessageBoxA(0, "Close IDA", "Info", 0) + +print "registering nw_openidb->", _idaapi.notify_when(NW_OPENIDB, nw_openidb) +print "registering nw_closeidb->", _idaapi.notify_when(NW_CLOSEIDB, nw_closeidb) +print "registering nw_openclose->", _idaapi.notify_when(NW_OPENIDB|NW_CLOSEIDB, nw_openclose) +print "registering nw_closeida->", _idaapi.notify_when(NW_TERMIDA, nw_closeida) diff --git a/pywraps/py_plgform.hpp b/pywraps/py_plgform.hpp index 8797766b..7538511b 100644 --- a/pywraps/py_plgform.hpp +++ b/pywraps/py_plgform.hpp @@ -1,157 +1,157 @@ -#ifndef __PY_PLGFORM__ -#define __PY_PLGFORM__ - -// -//--------------------------------------------------------------------------- -class plgform_t -{ -private: - ref_t py_obj; - TForm *form; - - static int idaapi s_callback(void *ud, int notification_code, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - plgform_t *_this = (plgform_t *)ud; - if ( notification_code == ui_tform_visible ) - { - TForm *form = va_arg(va, TForm *); - if ( form == _this->form ) - { - // Qt: QWidget* - // G: HWND - // We wrap and pass as a CObject in the hope that a Python UI framework - // can unwrap a CObject and get the hwnd/widget back - newref_t py_result( - PyObject_CallMethod( - _this->py_obj.o, - (char *)S_ON_CREATE, "O", - PyCObject_FromVoidPtr(form, NULL))); - PyW_ShowCbErr(S_ON_CREATE); - } - } - else if ( notification_code == ui_tform_invisible ) - { - TForm *form = va_arg(va, TForm *); - if ( form == _this->form ) - { - { - newref_t py_result( - PyObject_CallMethod( - _this->py_obj.o, - (char *)S_ON_CLOSE, "O", - PyCObject_FromVoidPtr(form, NULL))); - PyW_ShowCbErr(S_ON_CLOSE); - } - _this->unhook(); - } - } - return 0; - } - - void unhook() - { - unhook_from_notification_point(HT_UI, s_callback, this); - form = NULL; - - // Call DECREF at last, since it may trigger __del__ - PYW_GIL_CHECK_LOCKED_SCOPE(); - py_obj = ref_t(); - } - -public: - plgform_t(): form(NULL) - { - } - - bool show( - PyObject *obj, - const char *caption, - int options) - { - // Already displayed? - TForm *f = find_tform(caption); - if ( f != NULL ) - { - // Our form? - if ( f == form ) - { - // Switch to it - switchto_tform(form, true); - return true; - } - // Fail to create - return false; - } - - // Create a form - form = create_tform(caption, NULL); - if ( form == NULL ) - return false; - - if ( !hook_to_notification_point(HT_UI, s_callback, this) ) - { - form = NULL; - return false; - } - - py_obj = borref_t(obj); - - if ( is_idaq() ) - options |= FORM_QWIDGET; - - this->form = form; - open_tform(form, options); - return true; - } - - void close(int options = 0) - { - if ( form != NULL ) - close_tform(form, options); - } - - static PyObject *create() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new plgform_t(), destroy); - } - - static void destroy(void *obj) - { - delete (plgform_t *)obj; - } -}; -// - -// -//--------------------------------------------------------------------------- -#define DECL_PLGFORM PYW_GIL_CHECK_LOCKED_SCOPE(); plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); -static PyObject *plgform_new() -{ - return plgform_t::create(); -} - -static bool plgform_show( - PyObject *py_link, - PyObject *py_obj, - const char *caption, - int options = FORM_TAB|FORM_MENU|FORM_RESTORE) -{ - DECL_PLGFORM; - return plgform->show(py_obj, caption, options); -} - -static void plgform_close( - PyObject *py_link, - int options) -{ - DECL_PLGFORM; - plgform->close(options); -} -#undef DECL_PLGFORM -// - -#endif // __PY_PLGFORM__ +#ifndef __PY_PLGFORM__ +#define __PY_PLGFORM__ + +// +//--------------------------------------------------------------------------- +class plgform_t +{ +private: + ref_t py_obj; + TForm *form; + + static int idaapi s_callback(void *ud, int notification_code, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + plgform_t *_this = (plgform_t *)ud; + if ( notification_code == ui_tform_visible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + // Qt: QWidget* + // G: HWND + // We wrap and pass as a CObject in the hope that a Python UI framework + // can unwrap a CObject and get the hwnd/widget back + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CREATE, "O", + PyCObject_FromVoidPtr(form, NULL))); + PyW_ShowCbErr(S_ON_CREATE); + } + } + else if ( notification_code == ui_tform_invisible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + { + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CLOSE, "O", + PyCObject_FromVoidPtr(form, NULL))); + PyW_ShowCbErr(S_ON_CLOSE); + } + _this->unhook(); + } + } + return 0; + } + + void unhook() + { + unhook_from_notification_point(HT_UI, s_callback, this); + form = NULL; + + // Call DECREF at last, since it may trigger __del__ + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_obj = ref_t(); + } + +public: + plgform_t(): form(NULL) + { + } + + bool show( + PyObject *obj, + const char *caption, + int options) + { + // Already displayed? + TForm *f = find_tform(caption); + if ( f != NULL ) + { + // Our form? + if ( f == form ) + { + // Switch to it + switchto_tform(form, true); + return true; + } + // Fail to create + return false; + } + + // Create a form + form = create_tform(caption, NULL); + if ( form == NULL ) + return false; + + if ( !hook_to_notification_point(HT_UI, s_callback, this) ) + { + form = NULL; + return false; + } + + py_obj = borref_t(obj); + + if ( is_idaq() ) + options |= FORM_QWIDGET; + + this->form = form; + open_tform(form, options); + return true; + } + + void close(int options = 0) + { + if ( form != NULL ) + close_tform(form, options); + } + + static PyObject *create() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new plgform_t(), destroy); + } + + static void destroy(void *obj) + { + delete (plgform_t *)obj; + } +}; +// + +// +//--------------------------------------------------------------------------- +#define DECL_PLGFORM PYW_GIL_CHECK_LOCKED_SCOPE(); plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); +static PyObject *plgform_new() +{ + return plgform_t::create(); +} + +static bool plgform_show( + PyObject *py_link, + PyObject *py_obj, + const char *caption, + int options = FORM_TAB|FORM_MENU|FORM_RESTORE) +{ + DECL_PLGFORM; + return plgform->show(py_obj, caption, options); +} + +static void plgform_close( + PyObject *py_link, + int options) +{ + DECL_PLGFORM; + plgform->close(options); +} +#undef DECL_PLGFORM +// + +#endif // __PY_PLGFORM__ diff --git a/pywraps/py_plgform.py b/pywraps/py_plgform.py index dc4c5e3c..bddb02f2 100644 --- a/pywraps/py_plgform.py +++ b/pywraps/py_plgform.py @@ -1,121 +1,121 @@ -import _idaapi - -# -class PluginForm(object): - """ - PluginForm class. - - This form can be used to host additional controls. Please check the PyQt example. - """ - - FORM_MDI = 0x01 - """start by default as MDI (obsolete)""" - FORM_TAB = 0x02 - """attached by default to a tab""" - FORM_RESTORE = 0x04 - """restore state from desktop config""" - FORM_ONTOP = 0x08 - """form should be "ontop""" - FORM_MENU = 0x10 - """form must be listed in the windows menu (automatically set for all plugins)""" - FORM_CENTERED = 0x20 - """form will be centered on the screen""" - FORM_PERSIST = 0x40 - """form will persist until explicitly closed with Close()""" - - - def __init__(self): - """ - """ - self.__clink__ = _idaapi.plgform_new() - - - - def Show(self, caption, options = 0): - """ - Creates the form if not was not created or brings to front if it was already created - - @param caption: The form caption - @param options: One of PluginForm.FORM_ constants - """ - options |= PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE - return _idaapi.plgform_show(self.__clink__, self, caption, options) - - - @staticmethod - def FormToPyQtWidget(form, ctx = sys.modules['__main__']): - """ - Use this method to convert a TForm* to a QWidget to be used by PyQt - - @param ctx: Context. Reference to a module that already imported SIP and QtGui modules - """ - return ctx.sip.wrapinstance(ctx.sip.voidptr(form).__int__(), ctx.QtGui.QWidget) - - - @staticmethod - def FormToPySideWidget(form, ctx = sys.modules['__main__']): - """ - Use this method to convert a TForm* to a QWidget to be used by PySide - - @param ctx: Context. Reference to a module that already imported QtGui module - """ - if form is None: - return None - if type(form).__name__ == "SwigPyObject": - # Since 'form' is a SwigPyObject, we first need to convert it to a PyCObject. - # However, there's no easy way of doing it, so we'll use a rather brutal approach: - # converting the SwigPyObject to a 'long' (will go through 'SwigPyObject_long', - # that will return the pointer's value as a long), and then convert that value - # back to a pointer into a PyCObject. - ptr_l = long(form) - from ctypes import pythonapi, c_void_p, py_object - pythonapi.PyCObject_FromVoidPtr.restype = py_object - pythonapi.PyCObject_AsVoidPtr.argtypes = [c_void_p, c_void_p] - form = pythonapi.PyCObject_FromVoidPtr(ptr_l, 0) - return ctx.QtGui.QWidget.FromCObject(form) - - - def OnCreate(self, form): - """ - This event is called when the plugin form is created. - The programmer should populate the form when this event is triggered. - - @return: None - """ - pass - - - def OnClose(self, form): - """ - Called when the plugin form is closed - - @return: None - """ - pass - - - def Close(self, options): - """ - Closes the form. - - @param options: Close options (FORM_SAVE, FORM_NO_CONTEXT, ...) - - @return: None - """ - return _idaapi.plgform_close(self.__clink__, options) - - FORM_SAVE = 0x1 - """Save state in desktop config""" - - FORM_NO_CONTEXT = 0x2 - """Don't change the current context (useful for toolbars)""" - - FORM_DONT_SAVE_SIZE = 0x4 - """Don't save size of the window""" - - FORM_CLOSE_LATER = 0x8 - """This flag should be used when Close() is called from an event handler""" -# - -plg = PluginForm() -plg.Show("This is it") +import _idaapi + +# +class PluginForm(object): + """ + PluginForm class. + + This form can be used to host additional controls. Please check the PyQt example. + """ + + FORM_MDI = 0x01 + """start by default as MDI (obsolete)""" + FORM_TAB = 0x02 + """attached by default to a tab""" + FORM_RESTORE = 0x04 + """restore state from desktop config""" + FORM_ONTOP = 0x08 + """form should be "ontop""" + FORM_MENU = 0x10 + """form must be listed in the windows menu (automatically set for all plugins)""" + FORM_CENTERED = 0x20 + """form will be centered on the screen""" + FORM_PERSIST = 0x40 + """form will persist until explicitly closed with Close()""" + + + def __init__(self): + """ + """ + self.__clink__ = _idaapi.plgform_new() + + + + def Show(self, caption, options = 0): + """ + Creates the form if not was not created or brings to front if it was already created + + @param caption: The form caption + @param options: One of PluginForm.FORM_ constants + """ + options |= PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE + return _idaapi.plgform_show(self.__clink__, self, caption, options) + + + @staticmethod + def FormToPyQtWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PyQt + + @param ctx: Context. Reference to a module that already imported SIP and QtGui modules + """ + return ctx.sip.wrapinstance(ctx.sip.voidptr(form).__int__(), ctx.QtGui.QWidget) + + + @staticmethod + def FormToPySideWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PySide + + @param ctx: Context. Reference to a module that already imported QtGui module + """ + if form is None: + return None + if type(form).__name__ == "SwigPyObject": + # Since 'form' is a SwigPyObject, we first need to convert it to a PyCObject. + # However, there's no easy way of doing it, so we'll use a rather brutal approach: + # converting the SwigPyObject to a 'long' (will go through 'SwigPyObject_long', + # that will return the pointer's value as a long), and then convert that value + # back to a pointer into a PyCObject. + ptr_l = long(form) + from ctypes import pythonapi, c_void_p, py_object + pythonapi.PyCObject_FromVoidPtr.restype = py_object + pythonapi.PyCObject_AsVoidPtr.argtypes = [c_void_p, c_void_p] + form = pythonapi.PyCObject_FromVoidPtr(ptr_l, 0) + return ctx.QtGui.QWidget.FromCObject(form) + + + def OnCreate(self, form): + """ + This event is called when the plugin form is created. + The programmer should populate the form when this event is triggered. + + @return: None + """ + pass + + + def OnClose(self, form): + """ + Called when the plugin form is closed + + @return: None + """ + pass + + + def Close(self, options): + """ + Closes the form. + + @param options: Close options (FORM_SAVE, FORM_NO_CONTEXT, ...) + + @return: None + """ + return _idaapi.plgform_close(self.__clink__, options) + + FORM_SAVE = 0x1 + """Save state in desktop config""" + + FORM_NO_CONTEXT = 0x2 + """Don't change the current context (useful for toolbars)""" + + FORM_DONT_SAVE_SIZE = 0x4 + """Don't save size of the window""" + + FORM_CLOSE_LATER = 0x8 + """This flag should be used when Close() is called from an event handler""" +# + +plg = PluginForm() +plg.Show("This is it") diff --git a/pywraps/py_qfile.hpp b/pywraps/py_qfile.hpp index 7b993fcb..bbd68649 100644 --- a/pywraps/py_qfile.hpp +++ b/pywraps/py_qfile.hpp @@ -1,407 +1,407 @@ -#ifndef __PY_IDA_QFILE__ -#define __PY_IDA_QFILE__ - -// -/* -# -class qfile_t(pyidc_opaque_object_t): - """A helper class to work with FILE related functions.""" - def __init__(self): - pass - - def close(self): - """Closes the file""" - pass - - def open(self, filename, mode): - """Opens a file - @param filename: the file name - @param mode: The mode string, ala fopen() style - @return: Boolean - """ - pass - - def set_linput(self, linput): - """Links the current loader_input_t instance to a linput_t instance""" - pass - - @staticmethod - def tmpfile(): - """A static method to construct an instance using a temporary file""" - pass - - def seek(self, pos, whence = SEEK_SET): - """Set input source position - @return: the new position (not 0 as fseek!) - """ - pass - - def tell(self): - """Returns the current position""" - pass - - def gets(self, len): - """Reads a line from the input file. Returns the read line or None""" - pass - - def read(self, size): - """Reads from the file. Returns the buffer or None""" - pass - - def write(self, buf): - """Writes to the file. Returns 0 or the number of bytes written""" - pass - - def readbytes(self, size, big_endian): - """Similar to read() but it respect the endianness""" - pass - - def writebytes(self, size, big_endian): - """Similar to write() but it respect the endianness""" - pass - - def flush(self): - pass - - def get_char(self): - """Reads a single character from the file. Returns None if EOF or the read character""" - pass - - def put_char(self): - """Writes a single character to the file""" - pass - - def opened(self): - """Checks if the file is opened or not""" - pass -# -*/ -class qfile_t -{ -private: - FILE *fp; - bool own; - qstring fn; - - //-------------------------------------------------------------------------- - void assign(const qfile_t &rhs) - { - fn = rhs.fn; - fp = rhs.fp; - own = false; - } - //-------------------------------------------------------------------------- - bool _from_fp(FILE *fp) - { - if ( fp == NULL ) - return false; - own = false; - fn.sprnt("", fp); - this->fp = fp; - return true; - } - inline void _from_cobject(PyObject *pycobject) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); - } -public: - int __idc_cvt_id__; - //-------------------------------------------------------------------------- - qfile_t(const qfile_t &rhs) - { - assign(rhs); - } - - //-------------------------------------------------------------------------- - qfile_t(PyObject *pycobject = NULL) - { - fp = NULL; - own = true; - fn.qclear(); - __idc_cvt_id__ = PY_ICID_OPAQUE; - bool ok; - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - ok = pycobject != NULL && PyCObject_Check(pycobject); - } - if ( ok ) - _from_cobject(pycobject); - } - - //-------------------------------------------------------------------------- - bool opened() - { - return fp != NULL; - } - - //-------------------------------------------------------------------------- - void close() - { - if ( fp == NULL ) - return; - if ( own ) - { - Py_BEGIN_ALLOW_THREADS; - qfclose(fp); - Py_END_ALLOW_THREADS; - } - fp = NULL; - own = true; - } - - //-------------------------------------------------------------------------- - ~qfile_t() - { - close(); - } - - //-------------------------------------------------------------------------- - bool open(const char *filename, const char *mode) - { - close(); - Py_BEGIN_ALLOW_THREADS; - fp = qfopen(filename, mode); - Py_END_ALLOW_THREADS; - if ( fp == NULL ) - return false; - // Save file name - fn = filename; - own = true; - return true; - } - - //-------------------------------------------------------------------------- - static qfile_t *from_fp(FILE *fp) - { - if ( fp == NULL ) - return NULL; - qfile_t *qf = new qfile_t(); - qf->own = false; - qf->fn.sprnt("", fp); - qf->fp = fp; - return qf; - } - - //-------------------------------------------------------------------------- - // This method can be used to pass a FILE* from C code - static qfile_t *from_cobject(PyObject *pycobject) - { - return PyCObject_Check(pycobject) ? from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)) : NULL; - } - - //-------------------------------------------------------------------------- - static qfile_t *tmpfile() - { - FILE *fp; - Py_BEGIN_ALLOW_THREADS; - fp = qtmpfile(); - Py_END_ALLOW_THREADS; - return from_fp(fp); - } - - //-------------------------------------------------------------------------- - FILE *get_fp() - { - return fp; - } - - //-------------------------------------------------------------------------- - int seek(int32 offset, int whence = SEEK_SET) - { - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfseek(fp, offset, whence); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int32 tell() - { - int32 rc; - Py_BEGIN_ALLOW_THREADS; - rc = qftell(fp); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - PyObject *readbytes(int size, bool big_endian) - { - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - PYW_GIL_CHECK_LOCKED_SCOPE(); - int r; - Py_BEGIN_ALLOW_THREADS; - r = freadbytes(fp, buf, size, big_endian); - Py_END_ALLOW_THREADS; - if ( r != 0 ) - { - free(buf); - break; - } - - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *read(int size) - { - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - PYW_GIL_CHECK_LOCKED_SCOPE(); - int r; - Py_BEGIN_ALLOW_THREADS; - r = qfread(fp, buf, size); - Py_END_ALLOW_THREADS; - if ( r <= 0 ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *gets(int size) - { - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - PYW_GIL_CHECK_LOCKED_SCOPE(); - char *p; - Py_BEGIN_ALLOW_THREADS; - p = qfgets(buf, size, fp); - Py_END_ALLOW_THREADS; - if ( p == NULL ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromString(buf); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - int writebytes(PyObject *py_buf, bool big_endian) - { - Py_ssize_t sz; - void *buf; - PYW_GIL_CHECK_LOCKED_SCOPE(); - sz = PyString_GET_SIZE(py_buf); - buf = (void *)PyString_AS_STRING(py_buf); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = fwritebytes(fp, buf, int(sz), big_endian); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int write(PyObject *py_buf) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_buf) ) - return 0; - // Just so that there is no risk that the buffer returned by - // 'PyString_AS_STRING' gets deallocated within the - // Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_buf_ref(py_buf); - void *p = (void *)PyString_AS_STRING(py_buf); - Py_ssize_t sz = PyString_GET_SIZE(py_buf); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfwrite(fp, p, sz); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int puts(const char *str) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfputs(str, fp); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int32 size() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int32 r; - Py_BEGIN_ALLOW_THREADS; - int pos = qfseek(fp, 0, SEEK_END); - r = qftell(fp); - qfseek(fp, pos, SEEK_SET); - Py_END_ALLOW_THREADS; - return r; - } - - //-------------------------------------------------------------------------- - int flush() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qflush(fp); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - PyObject *filename() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyString_FromString(fn.c_str()); - } - - //-------------------------------------------------------------------------- - PyObject *get_char() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int ch; - Py_BEGIN_ALLOW_THREADS; - ch = qfgetc(fp); - Py_END_ALLOW_THREADS; - if ( ch == EOF ) - Py_RETURN_NONE; - return Py_BuildValue("c", ch); - } - - //-------------------------------------------------------------------------- - int put_char(char chr) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfputc(chr, fp); - Py_END_ALLOW_THREADS; - return rc; - } -}; -// - -#endif +#ifndef __PY_IDA_QFILE__ +#define __PY_IDA_QFILE__ + +// +/* +# +class qfile_t(pyidc_opaque_object_t): + """A helper class to work with FILE related functions.""" + def __init__(self): + pass + + def close(self): + """Closes the file""" + pass + + def open(self, filename, mode): + """Opens a file + @param filename: the file name + @param mode: The mode string, ala fopen() style + @return: Boolean + """ + pass + + def set_linput(self, linput): + """Links the current loader_input_t instance to a linput_t instance""" + pass + + @staticmethod + def tmpfile(): + """A static method to construct an instance using a temporary file""" + pass + + def seek(self, pos, whence = SEEK_SET): + """Set input source position + @return: the new position (not 0 as fseek!) + """ + pass + + def tell(self): + """Returns the current position""" + pass + + def gets(self, len): + """Reads a line from the input file. Returns the read line or None""" + pass + + def read(self, size): + """Reads from the file. Returns the buffer or None""" + pass + + def write(self, buf): + """Writes to the file. Returns 0 or the number of bytes written""" + pass + + def readbytes(self, size, big_endian): + """Similar to read() but it respect the endianness""" + pass + + def writebytes(self, size, big_endian): + """Similar to write() but it respect the endianness""" + pass + + def flush(self): + pass + + def get_char(self): + """Reads a single character from the file. Returns None if EOF or the read character""" + pass + + def put_char(self): + """Writes a single character to the file""" + pass + + def opened(self): + """Checks if the file is opened or not""" + pass +# +*/ +class qfile_t +{ +private: + FILE *fp; + bool own; + qstring fn; + + //-------------------------------------------------------------------------- + void assign(const qfile_t &rhs) + { + fn = rhs.fn; + fp = rhs.fp; + own = false; + } + //-------------------------------------------------------------------------- + bool _from_fp(FILE *fp) + { + if ( fp == NULL ) + return false; + own = false; + fn.sprnt("", fp); + this->fp = fp; + return true; + } + inline void _from_cobject(PyObject *pycobject) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); + } +public: + int __idc_cvt_id__; + //-------------------------------------------------------------------------- + qfile_t(const qfile_t &rhs) + { + assign(rhs); + } + + //-------------------------------------------------------------------------- + qfile_t(PyObject *pycobject = NULL) + { + fp = NULL; + own = true; + fn.qclear(); + __idc_cvt_id__ = PY_ICID_OPAQUE; + bool ok; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + ok = pycobject != NULL && PyCObject_Check(pycobject); + } + if ( ok ) + _from_cobject(pycobject); + } + + //-------------------------------------------------------------------------- + bool opened() + { + return fp != NULL; + } + + //-------------------------------------------------------------------------- + void close() + { + if ( fp == NULL ) + return; + if ( own ) + { + Py_BEGIN_ALLOW_THREADS; + qfclose(fp); + Py_END_ALLOW_THREADS; + } + fp = NULL; + own = true; + } + + //-------------------------------------------------------------------------- + ~qfile_t() + { + close(); + } + + //-------------------------------------------------------------------------- + bool open(const char *filename, const char *mode) + { + close(); + Py_BEGIN_ALLOW_THREADS; + fp = qfopen(filename, mode); + Py_END_ALLOW_THREADS; + if ( fp == NULL ) + return false; + // Save file name + fn = filename; + own = true; + return true; + } + + //-------------------------------------------------------------------------- + static qfile_t *from_fp(FILE *fp) + { + if ( fp == NULL ) + return NULL; + qfile_t *qf = new qfile_t(); + qf->own = false; + qf->fn.sprnt("", fp); + qf->fp = fp; + return qf; + } + + //-------------------------------------------------------------------------- + // This method can be used to pass a FILE* from C code + static qfile_t *from_cobject(PyObject *pycobject) + { + return PyCObject_Check(pycobject) ? from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)) : NULL; + } + + //-------------------------------------------------------------------------- + static qfile_t *tmpfile() + { + FILE *fp; + Py_BEGIN_ALLOW_THREADS; + fp = qtmpfile(); + Py_END_ALLOW_THREADS; + return from_fp(fp); + } + + //-------------------------------------------------------------------------- + FILE *get_fp() + { + return fp; + } + + //-------------------------------------------------------------------------- + int seek(int32 offset, int whence = SEEK_SET) + { + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfseek(fp, offset, whence); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int32 tell() + { + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qftell(fp); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + PyObject *readbytes(int size, bool big_endian) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = freadbytes(fp, buf, size, big_endian); + Py_END_ALLOW_THREADS; + if ( r != 0 ) + { + free(buf); + break; + } + + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *read(int size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = qfread(fp, buf, size); + Py_END_ALLOW_THREADS; + if ( r <= 0 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *gets(int size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + PYW_GIL_CHECK_LOCKED_SCOPE(); + char *p; + Py_BEGIN_ALLOW_THREADS; + p = qfgets(buf, size, fp); + Py_END_ALLOW_THREADS; + if ( p == NULL ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + int writebytes(PyObject *py_buf, bool big_endian) + { + Py_ssize_t sz; + void *buf; + PYW_GIL_CHECK_LOCKED_SCOPE(); + sz = PyString_GET_SIZE(py_buf); + buf = (void *)PyString_AS_STRING(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = fwritebytes(fp, buf, int(sz), big_endian); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int write(PyObject *py_buf) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_buf) ) + return 0; + // Just so that there is no risk that the buffer returned by + // 'PyString_AS_STRING' gets deallocated within the + // Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_buf_ref(py_buf); + void *p = (void *)PyString_AS_STRING(py_buf); + Py_ssize_t sz = PyString_GET_SIZE(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfwrite(fp, p, sz); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int puts(const char *str) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputs(str, fp); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int32 size() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int32 r; + Py_BEGIN_ALLOW_THREADS; + int pos = qfseek(fp, 0, SEEK_END); + r = qftell(fp); + qfseek(fp, pos, SEEK_SET); + Py_END_ALLOW_THREADS; + return r; + } + + //-------------------------------------------------------------------------- + int flush() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qflush(fp); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + PyObject *filename() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyString_FromString(fn.c_str()); + } + + //-------------------------------------------------------------------------- + PyObject *get_char() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qfgetc(fp); + Py_END_ALLOW_THREADS; + if ( ch == EOF ) + Py_RETURN_NONE; + return Py_BuildValue("c", ch); + } + + //-------------------------------------------------------------------------- + int put_char(char chr) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputc(chr, fp); + Py_END_ALLOW_THREADS; + return rc; + } +}; +// + +#endif diff --git a/pywraps/py_typeinf.hpp b/pywraps/py_typeinf.hpp index d15d02cc..e04c625f 100644 --- a/pywraps/py_typeinf.hpp +++ b/pywraps/py_typeinf.hpp @@ -1,642 +1,733 @@ -#ifndef __PY_TYPEINF__ -#define __PY_TYPEINF__ - -// -//------------------------------------------------------------------------- -PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) -{ - tinfo_t tif; - qstring name; - qtype fields, type; - bool ok = parse_decl2(ti, decl, &name, &tif, flags); - if ( ok ) - ok = tif.serialize(&type, &fields, NULL, SUDT_FAST); - - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("(sss)", - name.c_str(), - (char *)type.c_str(), - (char *)fields.c_str()); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -/* -# -def calc_type_size(ti, tp): - """ - Returns the size of a type - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @return: - - None on failure - - The size of the type - """ - pass -# -*/ -PyObject *py_calc_type_size(const til_t *ti, PyObject *tp) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyString_Check(tp) ) - { - // To avoid release of 'data' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t tpref(tp); - const type_t *data = (type_t *)PyString_AsString(tp); - size_t sz; - Py_BEGIN_ALLOW_THREADS; - tinfo_t tif; - tif.deserialize(ti, &data, NULL, NULL); - sz = tif.get_size(); - Py_END_ALLOW_THREADS; - if ( sz != BADSIZE ) - return PyInt_FromLong(sz); - Py_RETURN_NONE; - } - else - { - PyErr_SetString(PyExc_ValueError, "String expected!"); - return NULL; - } -} - -//------------------------------------------------------------------------- -/* -# -def apply_type(ti, ea, tp_name, py_type, py_fields, flags) - """ - Apply the specified type to the address - @param ti: Type info library. 'idaapi.cvar.idati' can be used. - @param py_type: type string - @param py_fields: fields string (may be empty or None) - @param ea: the address of the object - @param flags: combination of TINFO_... constants or 0 - @return: Boolean - """ - pass -# -*/ -static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - const type_t *type = (const type_t *) PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - bool rc; - Py_BEGIN_ALLOW_THREADS; - struc_t *sptr; - member_t *mptr = get_member_by_id(ea, &sptr); - if ( type[0] == '\0' ) - { - if ( mptr != NULL ) - { - rc = mptr->has_ti(); - if ( rc ) - del_member_tinfo(sptr, mptr); - } - else - { - rc = has_ti(ea); - if ( rc ) - del_tinfo2(ea); - } - } - else - { - tinfo_t tif; - rc = tif.deserialize(ti, &type, &fields, NULL); - if ( rc ) - { - if ( mptr != NULL ) - rc = set_member_tinfo2(sptr, mptr, 0, tif, 0); - else - rc = apply_tinfo2(ea, tif, flags); - } - } - Py_END_ALLOW_THREADS; - return rc; -} - -//------------------------------------------------------------------------- -/* -# -def print_type(ea, one_line): - """ - Returns the type of an item - @return: - - None on failure - - The type string with a semicolon. Can be used directly with idc.SetType() - """ - pass -# -*/ -static PyObject *py_print_type(ea_t ea, bool one_line) -{ - char buf[64*MAXSTR]; - int flags = PRTYPE_SEMI | (one_line ? PRTYPE_1LINE : PRTYPE_MULTI); - bool ok = print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return PyString_FromString(buf); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -/* -# -def py_unpack_object_from_idb(ti, tp, fields, ea, pio_flags = 0): - """ - Unpacks from the database at 'ea' to an object. - Please refer to unpack_object_from_bv() - """ - pass -# -*/ -PyObject *py_unpack_object_from_idb( - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - ea_t ea, - int pio_flags = 0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Unpack - type_t *type = (type_t *) PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - idc_value_t idc_obj; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = unpack_object_from_idb( - &idc_obj, - ti, - type, - fields, - ea, - NULL, - pio_flags); - Py_END_ALLOW_THREADS; - - // Unpacking failed? - if ( err != eOk ) - return Py_BuildValue("(ii)", 0, err); - - // Convert - ref_t py_ret; - err = idcvar_to_pyvar(idc_obj, &py_ret); - - // Conversion failed? - if ( err != CIP_OK ) - return Py_BuildValue("(ii)", 0, err); - else - return Py_BuildValue("(iO)", 1, py_ret.o); -} - -//------------------------------------------------------------------------- -/* -# -def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0): - """ - Unpacks a buffer into an object. - Returns the error_t returned by idaapi.pack_object_to_idb - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @param fields: fields string (may be empty or None) - @param bytes: the bytes to unpack - @param pio_flags: flags used while unpacking - @return: - - tuple(0, err) on failure - - tuple(1, obj) on success - """ - pass -# -*/ -PyObject *py_unpack_object_from_bv( - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - PyObject *py_bytes, - int pio_flags = 0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) || !PyString_Check(py_bytes) ) - { - PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); - return NULL; - } - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Get type strings - type_t *type = (type_t *) PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - - // Make a byte vector - bytevec_t bytes; - bytes.resize(PyString_Size(py_bytes)); - memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); - - idc_value_t idc_obj; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = unpack_object_from_bv( - &idc_obj, - ti, - type, - fields, - bytes, - pio_flags); - Py_END_ALLOW_THREADS; - - // Unpacking failed? - if ( err != eOk ) - return Py_BuildValue("(ii)", 0, err); - - // Convert - ref_t py_ret; - err = idcvar_to_pyvar(idc_obj, &py_ret); - - // Conversion failed? - if ( err != CIP_OK ) - return Py_BuildValue("(ii)", 0, err); - - return Py_BuildValue("(iO)", 1, py_ret.o); -} - -//------------------------------------------------------------------------- -/* -# -def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0): - """ - Write a typed object to the database. - Raises an exception if wrong parameters were passed or conversion fails - Returns the error_t returned by idaapi.pack_object_to_idb - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @param fields: fields string (may be empty or None) - @param ea: ea to be used while packing - @param pio_flags: flags used while unpacking - """ - pass -# -*/ -PyObject *py_pack_object_to_idb( - PyObject *py_obj, - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - ea_t ea, - int pio_flags = 0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // Convert Python object to IDC object - idc_value_t idc_obj; - borref_t py_obj_ref(py_obj); - if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) - return NULL; - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Get type strings - type_t *type = (type_t *)PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - - // Pack - // error_t err; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); - Py_END_ALLOW_THREADS; - return PyInt_FromLong(err); -} - -//------------------------------------------------------------------------- -/* -# -def pack_object_to_bv(obj, ti, tp, fields, base_ea, pio_flags = 0): - """ - Packs a typed object to a string - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @param fields: fields string (may be empty or None) - @param base_ea: base ea used to relocate the pointers in the packed object - @param pio_flags: flags used while unpacking - @return: - tuple(0, err_code) on failure - tuple(1, packed_buf) on success - """ - pass -# -*/ -// Returns a tuple(Boolean, PackedBuffer or Error Code) -PyObject *py_pack_object_to_bv( - PyObject *py_obj, - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - ea_t base_ea, - int pio_flags=0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // Convert Python object to IDC object - idc_value_t idc_obj; - borref_t py_obj_ref(py_obj); - if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) - return NULL; - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Get type strings - type_t *type = (type_t *)PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - - // Pack - relobj_t bytes; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = pack_object_to_bv( - &idc_obj, - ti, - type, - fields, - &bytes, - NULL, - pio_flags); - if ( err == eOk && !bytes.relocate(base_ea, inf.mf) ) - err = -1; - Py_END_ALLOW_THREADS; - if ( err == eOk ) - return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); - else - return Py_BuildValue("(ii)", 0, err); -} - -//------------------------------------------------------------------------- -/* Parse types from a string or file. See ParseTypes() in idc.py */ -int idc_parse_types(const char *input, int flags) -{ - int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; - - if ((flags & 1) != 0) - hti |= HTI_FIL; - - return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); -} - -//------------------------------------------------------------------------- -PyObject *py_idc_get_type_raw(ea_t ea) -{ - qtype type, fields; - bool ok = get_tinfo(ea, &type, &fields); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -PyObject *py_idc_get_local_type_raw(int ordinal) -{ - const type_t *type; - const p_list *fields; - bool ok = get_numbered_type(idati, ordinal, &type, &fields); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("(ss)", (char *)type, (char *)fields); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) -{ - tinfo_t tif; - if ( guess_tinfo2(ea, &tif) ) - { - qstring out; - if ( tif.print(&out) ) - return qstrncpy(buf, out.begin(), bufsize); - } - return NULL; -} - -//------------------------------------------------------------------------- -char *idc_get_type(ea_t ea, char *buf, size_t bufsize) -{ - tinfo_t tif; - if ( get_tinfo2(ea, &tif) ) - { - qstring out; - if ( tif.print(&out) ) - { - qstrncpy(buf, out.c_str(), bufsize); - return buf; - } - } - return NULL; -} - -//------------------------------------------------------------------------- -int idc_set_local_type(int ordinal, const char *dcl, int flags) -{ - if (dcl == NULL || dcl[0] == '\0') - { - if ( !del_numbered_type(idati, ordinal) ) - return 0; - } - else - { - tinfo_t tif; - qstring name; - if ( !parse_decl2(idati, dcl, &name, &tif, flags) ) - return 0; - - if ( ordinal <= 0 ) - { - if ( !name.empty() ) - ordinal = get_type_ordinal(idati, name.begin()); - - if ( ordinal <= 0 ) - ordinal = alloc_type_ordinal(idati); - } - - if ( tif.set_numbered_type(idati, ordinal, 0, name.c_str()) != TERR_OK ) - return 0; - } - return ordinal; -} - -//------------------------------------------------------------------------- -int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) -{ - tinfo_t tif; - if ( !tif.get_numbered_type(idati, ordinal) ) - { - buf[0] = 0; - return false; - } - - qstring res; - const char *name = get_numbered_type_name(idati, ordinal); - if ( !tif.print(&res, name, flags, 2, 40) ) - { - buf[0] = 0; - return false; - } - - qstrncpy(buf, res.begin(), maxsize); - return true; -} - -//------------------------------------------------------------------------- -PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - qstring res; - const type_t *type = (type_t *)PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - bool ok; - Py_BEGIN_ALLOW_THREADS; - tinfo_t tif; - ok = tif.deserialize(idati, &type, &fields, NULL) - && tif.print(&res, name, flags, 2, 40); - Py_END_ALLOW_THREADS; - if ( ok ) - return PyString_FromString(res.begin()); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) -{ - const char *name = get_numbered_type_name(idati, ordinal); - if ( name == NULL ) - return false; - - qstrncpy(buf, name, bufsize); - return true; -} -// - -// -//------------------------------------------------------------------------- -// A set of tinfo_t & details objects that were created from IDAPython. -// This is necessary in order to clear all the "type details" that are -// associated, in the kernel, with the tinfo_t instances. -// -// Unfortunately the IDAPython plugin has to terminate _after_ the IDB is -// closed, but the "type details" must be cleared _before_ the IDB is closed. -static qvector py_tinfo_t_vec; -static qvector py_ptr_type_data_t_vec; -static qvector py_array_type_data_t_vec; -static qvector py_func_type_data_t_vec; -static qvector py_udt_type_data_t_vec; - -static void __clear(tinfo_t *inst) { inst->clear(); } -static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); } -static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); } -static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); } -static void __clear(udt_type_data_t *inst) { inst->clear(); } - -void til_clear_python_tinfo_t_instances(void) -{ - // Pre-emptive strike: clear all the python-exposed tinfo_t - // (& related types) instances: if that were not done here, - // ~tinfo_t() calls happening as part of the python shutdown - // process will try and clear() their details. ..but the kernel's - // til-related functions will already have deleted those details - // at that point. - // - // NOTE: Don't clear() the arrays of pointers. All the python-exposed - // instances will be deleted through the python shutdown/ref-decrementing - // process anyway (which will cause til_deregister_..() calls), and the - // entries will be properly pulled out of the vector when that happens. -#define BATCH_CLEAR(Type) \ - do \ - { \ - for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \ - __clear(py_##Type##_vec[i]); \ - } while ( false ) - - BATCH_CLEAR(tinfo_t); - BATCH_CLEAR(ptr_type_data_t); - BATCH_CLEAR(array_type_data_t); - BATCH_CLEAR(func_type_data_t); - BATCH_CLEAR(udt_type_data_t); -#undef BATCH_CLEAR -} - -#define DEF_REG_UNREG_REFCOUNTED(Type) \ - void til_register_python_##Type##_instance(Type *inst) \ - { \ - /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \ - /* to an object's tinfo_t property will end up trying to register it. */ \ - py_##Type##_vec.add_unique(inst); \ - } \ - \ - void til_deregister_python_##Type##_instance(Type *inst) \ - { \ - qvector::iterator found = py_##Type##_vec.find(inst); \ - if ( found != py_##Type##_vec.end() ) \ - { \ - __clear(inst); \ - /* tif->clear();*/ \ - py_##Type##_vec.erase(found); \ - } \ - } - -DEF_REG_UNREG_REFCOUNTED(tinfo_t); -DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t); -DEF_REG_UNREG_REFCOUNTED(array_type_data_t); -DEF_REG_UNREG_REFCOUNTED(func_type_data_t); -DEF_REG_UNREG_REFCOUNTED(udt_type_data_t); - -#undef DEF_REG_UNREG_REFCOUNTED - -// - - -#endif +#ifndef __PY_TYPEINF__ +#define __PY_TYPEINF__ + +// +//------------------------------------------------------------------------- +PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) +{ + tinfo_t tif; + qstring name; + qtype fields, type; + bool ok = parse_decl2(ti, decl, &name, &tif, flags); + if ( ok ) + ok = tif.serialize(&type, &fields, NULL, SUDT_FAST); + + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(sss)", + name.c_str(), + (char *)type.c_str(), + (char *)fields.c_str()); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +def calc_type_size(ti, tp): + """ + Returns the size of a type + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @return: + - None on failure + - The size of the type + """ + pass +# +*/ +PyObject *py_calc_type_size(const til_t *ti, PyObject *tp) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(tp) ) + { + // To avoid release of 'data' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t tpref(tp); + const type_t *data = (type_t *)PyString_AsString(tp); + size_t sz; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + tif.deserialize(ti, &data, NULL, NULL); + sz = tif.get_size(); + Py_END_ALLOW_THREADS; + if ( sz != BADSIZE ) + return PyInt_FromLong(sz); + Py_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_ValueError, "String expected!"); + return NULL; + } +} + +//------------------------------------------------------------------------- +/* +# +def apply_type(ti, ea, tp_name, py_type, py_fields, flags) + """ + Apply the specified type to the address + @param ti: Type info library. 'idaapi.cvar.idati' can be used. + @param py_type: type string + @param py_fields: fields string (may be empty or None) + @param ea: the address of the object + @param flags: combination of TINFO_... constants or 0 + @return: Boolean + """ + pass +# +*/ +static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + const type_t *type = (const type_t *) PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + bool rc; + Py_BEGIN_ALLOW_THREADS; + struc_t *sptr; + member_t *mptr = get_member_by_id(ea, &sptr); + if ( type[0] == '\0' ) + { + if ( mptr != NULL ) + { + rc = mptr->has_ti(); + if ( rc ) + del_member_tinfo(sptr, mptr); + } + else + { + rc = has_ti(ea); + if ( rc ) + del_tinfo2(ea); + } + } + else + { + tinfo_t tif; + rc = tif.deserialize(ti, &type, &fields, NULL); + if ( rc ) + { + if ( mptr != NULL ) + rc = set_member_tinfo2(sptr, mptr, 0, tif, 0); + else + rc = apply_tinfo2(ea, tif, flags); + } + } + Py_END_ALLOW_THREADS; + return rc; +} + +//------------------------------------------------------------------------- +/* +# +def print_type(ea, one_line): + """ + Returns the type of an item + @return: + - None on failure + - The type string with a semicolon. Can be used directly with idc.SetType() + """ + pass +# +*/ +static PyObject *py_print_type(ea_t ea, bool one_line) +{ + qstring out; + int flags = PRTYPE_SEMI | (one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + bool ok = print_type3(&out, ea, one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return PyString_FromString(out.begin()); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +def py_unpack_object_from_idb(ti, tp, fields, ea, pio_flags = 0): + """ + Unpacks from the database at 'ea' to an object. + Please refer to unpack_object_from_bv() + """ + pass +# +*/ +PyObject *py_unpack_object_from_idb( + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t ea, + int pio_flags = 0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Unpack + type_t *type = (type_t *) PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + idc_value_t idc_obj; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_idb( + &idc_obj, + ti, + type, + fields, + ea, + NULL, + pio_flags); + Py_END_ALLOW_THREADS; + + // Unpacking failed? + if ( err != eOk ) + return Py_BuildValue("(ii)", 0, err); + + // Convert + ref_t py_ret; + err = idcvar_to_pyvar(idc_obj, &py_ret); + + // Conversion failed? + if ( err != CIP_OK ) + return Py_BuildValue("(ii)", 0, err); + else + return Py_BuildValue("(iO)", 1, py_ret.o); +} + +//------------------------------------------------------------------------- +/* +# +def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0): + """ + Unpacks a buffer into an object. + Returns the error_t returned by idaapi.pack_object_to_idb + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: fields string (may be empty or None) + @param bytes: the bytes to unpack + @param pio_flags: flags used while unpacking + @return: + - tuple(0, err) on failure + - tuple(1, obj) on success + """ + pass +# +*/ +PyObject *py_unpack_object_from_bv( + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + PyObject *py_bytes, + int pio_flags = 0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) || !PyString_Check(py_bytes) ) + { + PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + + // Make a byte vector + bytevec_t bytes; + bytes.resize(PyString_Size(py_bytes)); + memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); + + idc_value_t idc_obj; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_bv( + &idc_obj, + ti, + type, + fields, + bytes, + pio_flags); + Py_END_ALLOW_THREADS; + + // Unpacking failed? + if ( err != eOk ) + return Py_BuildValue("(ii)", 0, err); + + // Convert + ref_t py_ret; + err = idcvar_to_pyvar(idc_obj, &py_ret); + + // Conversion failed? + if ( err != CIP_OK ) + return Py_BuildValue("(ii)", 0, err); + + return Py_BuildValue("(iO)", 1, py_ret.o); +} + +//------------------------------------------------------------------------- +/* +# +def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0): + """ + Write a typed object to the database. + Raises an exception if wrong parameters were passed or conversion fails + Returns the error_t returned by idaapi.pack_object_to_idb + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: fields string (may be empty or None) + @param ea: ea to be used while packing + @param pio_flags: flags used while unpacking + """ + pass +# +*/ +PyObject *py_pack_object_to_idb( + PyObject *py_obj, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t ea, + int pio_flags = 0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) + return NULL; + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Get type strings + type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + + // Pack + // error_t err; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + Py_END_ALLOW_THREADS; + return PyInt_FromLong(err); +} + +//------------------------------------------------------------------------- +/* +# +def pack_object_to_bv(obj, ti, tp, fields, base_ea, pio_flags = 0): + """ + Packs a typed object to a string + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: fields string (may be empty or None) + @param base_ea: base ea used to relocate the pointers in the packed object + @param pio_flags: flags used while unpacking + @return: + tuple(0, err_code) on failure + tuple(1, packed_buf) on success + """ + pass +# +*/ +// Returns a tuple(Boolean, PackedBuffer or Error Code) +PyObject *py_pack_object_to_bv( + PyObject *py_obj, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t base_ea, + int pio_flags=0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) + return NULL; + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Get type strings + type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + + // Pack + relobj_t bytes; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_bv( + &idc_obj, + ti, + type, + fields, + &bytes, + NULL, + pio_flags); + if ( err == eOk && !bytes.relocate(base_ea, inf.mf) ) + err = -1; + Py_END_ALLOW_THREADS; + if ( err == eOk ) + return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); + else + return Py_BuildValue("(ii)", 0, err); +} + +//------------------------------------------------------------------------- +/* Parse types from a string or file. See ParseTypes() in idc.py */ +int idc_parse_types(const char *input, int flags) +{ + int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; + + if ((flags & 1) != 0) + hti |= HTI_FIL; + + return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_type_raw(ea_t ea) +{ + qtype type, fields; + bool ok = get_tinfo(ea, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_local_type_raw(int ordinal) +{ + const type_t *type; + const p_list *fields; + bool ok = get_numbered_type(idati, ordinal, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type, (char *)fields); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( guess_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + return qstrncpy(buf, out.begin(), bufsize); + } + return NULL; +} + +//------------------------------------------------------------------------- +char *idc_get_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( get_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + { + qstrncpy(buf, out.c_str(), bufsize); + return buf; + } + } + return NULL; +} + +//------------------------------------------------------------------------- +int idc_set_local_type(int ordinal, const char *dcl, int flags) +{ + if (dcl == NULL || dcl[0] == '\0') + { + if ( !del_numbered_type(idati, ordinal) ) + return 0; + } + else + { + tinfo_t tif; + qstring name; + if ( !parse_decl2(idati, dcl, &name, &tif, flags) ) + return 0; + + if ( ordinal <= 0 ) + { + if ( !name.empty() ) + ordinal = get_type_ordinal(idati, name.begin()); + + if ( ordinal <= 0 ) + ordinal = alloc_type_ordinal(idati); + } + + if ( tif.set_numbered_type(idati, ordinal, 0, name.c_str()) != TERR_OK ) + return 0; + } + return ordinal; +} + +//------------------------------------------------------------------------- +int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) +{ + tinfo_t tif; + if ( !tif.get_numbered_type(idati, ordinal) ) + { + buf[0] = 0; + return false; + } + + qstring res; + const char *name = get_numbered_type_name(idati, ordinal); + if ( !tif.print(&res, name, flags, 2, 40) ) + { + buf[0] = 0; + return false; + } + + qstrncpy(buf, res.begin(), maxsize); + return true; +} + +//------------------------------------------------------------------------- +PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + qstring res; + const type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + bool ok; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + ok = tif.deserialize(idati, &type, &fields, NULL) + && tif.print(&res, name, flags, 2, 40); + Py_END_ALLOW_THREADS; + if ( ok ) + return PyString_FromString(res.begin()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) +{ + const char *name = get_numbered_type_name(idati, ordinal); + if ( name == NULL ) + return false; + + qstrncpy(buf, name, bufsize); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def get_named_type(til, name, ntf_flags): + """ + Get a type data by its name. + @param til: the type library + @param name: the type name + @param ntf_flags: a combination of NTF_* constants + @return: + None on failure + tuple(code, type_str, fields_str, cmt, field_cmts, sclass, value) on success + """ + pass +# +*/ +PyObject *py_get_named_type(const til_t *til, const char *name, int ntf_flags) +{ + const type_t *type = NULL; + const p_list *fields = NULL, *field_cmts = NULL; + const char *cmt = NULL; + sclass_t sclass = sc_unk; + uint32 value = 0; + int code = get_named_type(til, name, ntf_flags, &type, &fields, &cmt, &field_cmts, &sclass, &value); + if ( code == 0 ) + Py_RETURN_NONE; + PyObject *tuple = PyTuple_New(7); + int idx = 0; +#define ADD(Expr) PyTuple_SetItem(tuple, idx++, (Expr)) +#define ADD_OR_NONE(Cond, Expr) \ + do \ + { \ + if ( Cond ) \ + { \ + ADD(Expr); \ + } \ + else \ + { \ + Py_INCREF(Py_None); \ + ADD(Py_None); \ + } \ + } while ( false ) + + ADD(PyInt_FromLong(long(code))); + ADD(PyString_FromString((const char *) type)); + ADD_OR_NONE(fields != NULL, PyString_FromString((const char *) fields)); + ADD_OR_NONE(cmt != NULL, PyString_FromString(cmt)); + ADD_OR_NONE(field_cmts != NULL, PyString_FromString((const char *) field_cmts)); + ADD(PyInt_FromLong(long(sclass))); + ADD(PyLong_FromUnsignedLong(long(value))); +#undef ADD_OR_NONE +#undef ADD + return tuple; +} + +//------------------------------------------------------------------------- +PyObject *py_get_named_type64(const til_t *til, const char *name, int ntf_flags) +{ + return py_get_named_type(til, name, ntf_flags | NTF_64BIT); +} + +//------------------------------------------------------------------------- +int py_print_decls(text_sink_t &printer, til_t *til, PyObject *py_ordinals, uint32 flags) +{ + if ( !PyList_Check(py_ordinals) ) + { + PyErr_SetString(PyExc_ValueError, "'ordinals' must be a list"); + return 0; + } + + Py_ssize_t nords = PyList_Size(py_ordinals); + ordvec_t ordinals; + ordinals.reserve(size_t(nords)); + for ( Py_ssize_t i = 0; i < nords; ++i ) + { + borref_t item(PyList_GetItem(py_ordinals, i)); + if ( item == NULL + || (!PyInt_Check(item.o) && !PyLong_Check(item.o)) ) + { + qstring msg; + msg.sprnt("ordinals[%d] is not a valid value", int(i)); + PyErr_SetString(PyExc_ValueError, msg.begin()); + return 0; + } + uint32 ord = PyInt_Check(item.o) ? PyInt_AsLong(item.o) : PyLong_AsLong(item.o); + ordinals.push_back(ord); + } + return print_decls(printer, til, ordinals.empty() ? NULL : &ordinals, flags); +} + +// + +// +//------------------------------------------------------------------------- +// A set of tinfo_t & details objects that were created from IDAPython. +// This is necessary in order to clear all the "type details" that are +// associated, in the kernel, with the tinfo_t instances. +// +// Unfortunately the IDAPython plugin has to terminate _after_ the IDB is +// closed, but the "type details" must be cleared _before_ the IDB is closed. +static qvector py_tinfo_t_vec; +static qvector py_ptr_type_data_t_vec; +static qvector py_array_type_data_t_vec; +static qvector py_func_type_data_t_vec; +static qvector py_udt_type_data_t_vec; + +static void __clear(tinfo_t *inst) { inst->clear(); } +static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); } +static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); } +static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); } +static void __clear(udt_type_data_t *inst) { inst->clear(); } + +void til_clear_python_tinfo_t_instances(void) +{ + // Pre-emptive strike: clear all the python-exposed tinfo_t + // (& related types) instances: if that were not done here, + // ~tinfo_t() calls happening as part of the python shutdown + // process will try and clear() their details. ..but the kernel's + // til-related functions will already have deleted those details + // at that point. + // + // NOTE: Don't clear() the arrays of pointers. All the python-exposed + // instances will be deleted through the python shutdown/ref-decrementing + // process anyway (which will cause til_deregister_..() calls), and the + // entries will be properly pulled out of the vector when that happens. +#define BATCH_CLEAR(Type) \ + do \ + { \ + for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \ + __clear(py_##Type##_vec[i]); \ + } while ( false ) + + BATCH_CLEAR(tinfo_t); + BATCH_CLEAR(ptr_type_data_t); + BATCH_CLEAR(array_type_data_t); + BATCH_CLEAR(func_type_data_t); + BATCH_CLEAR(udt_type_data_t); +#undef BATCH_CLEAR +} + +#define DEF_REG_UNREG_REFCOUNTED(Type) \ + void til_register_python_##Type##_instance(Type *inst) \ + { \ + /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \ + /* to an object's tinfo_t property will end up trying to register it. */ \ + py_##Type##_vec.add_unique(inst); \ + } \ + \ + void til_deregister_python_##Type##_instance(Type *inst) \ + { \ + qvector::iterator found = py_##Type##_vec.find(inst); \ + if ( found != py_##Type##_vec.end() ) \ + { \ + __clear(inst); \ + /* tif->clear();*/ \ + py_##Type##_vec.erase(found); \ + } \ + } + +DEF_REG_UNREG_REFCOUNTED(tinfo_t); +DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t); +DEF_REG_UNREG_REFCOUNTED(array_type_data_t); +DEF_REG_UNREG_REFCOUNTED(func_type_data_t); +DEF_REG_UNREG_REFCOUNTED(udt_type_data_t); + +#undef DEF_REG_UNREG_REFCOUNTED + +// + + +#endif diff --git a/pywraps/py_ua.hpp b/pywraps/py_ua.hpp index c94603e4..01894357 100644 --- a/pywraps/py_ua.hpp +++ b/pywraps/py_ua.hpp @@ -1,919 +1,907 @@ -#ifndef __PY_UA__ -#define __PY_UA__ - -//------------------------------------------------------------------------- -// -//------------------------------------------------------------------------- -insn_t *insn_t_get_clink(PyObject *self) -{ - return (insn_t *)pyobj_get_clink(self); -} - -//------------------------------------------------------------------------- -op_t *op_t_get_clink(PyObject *self) -{ - return (op_t *)pyobj_get_clink(self); -} -// - -//------------------------------------------------------------------------- -// - -//------------------------------------------------------------------------- -/* -# -def init_output_buffer(size = MAXSTR): - """ - This function initialize an output buffer with the given size. - It should be called before using any out_xxxx() functions. - @return: It returns a string. This string should then be passed to MakeLine(). - This function could return None if it failed to create a buffer with the given size. - """ - pass -# -*/ -PyObject *py_init_output_buffer(size_t size = MAXSTR) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Let Python allocate a writable string buffer for us - PyObject *py_str = PyString_FromStringAndSize(NULL, size); - if ( py_str == NULL ) - Py_RETURN_NONE; - - init_output_buffer(PyString_AsString(py_str), size); - return py_str; -} - -//------------------------------------------------------------------------- -/* -# -def term_output_buffer(): - """Use this function to terminate an output buffer.""" - pass -# -*/ -void py_term_output_buffer() -{ - term_output_buffer(); -} - -//------------------------------------------------------------------------- -/* -# -def decode_preceding_insn(ea): - """ - Decodes the preceding instruction. Please check ua.hpp / decode_preceding_insn() - @param ea: current ea - @return: tuple(preceeding_ea or BADADDR, farref = Boolean) - """ - pass -# -*/ -PyObject *py_decode_preceding_insn(ea_t ea) -{ - bool farref; - ea_t r = decode_preceding_insn(ea, &farref); - PYW_GIL_CHECK_LOCKED_SCOPE(); - return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); -} - -//------------------------------------------------------------------------- -/* -# -def OutValue(op, outflags = 0): - """ - Output immediate value - @param op: operand (of type op_t) - @return: flags of the output value - -1: value is output with COLOR_ERROR - 0: value is output as a number or character or segment - """ - pass -# -*/ -flags_t py_OutValue(PyObject *x, int outflags=0) -{ - op_t *op = op_t_get_clink(x); - if ( op == NULL ) - return 0; - - return OutValue(*op, outflags); -} - -//------------------------------------------------------------------------- -/* -# -def get_stkvar(op, v): - """ - Get pointer to stack variable - @param op: reference to instruction operand - @param v: immediate value in the operand (usually op.addr) - @return: - - None on failure - - tuple(member_t, actval) - where actval: actual value used to fetch stack variable - """ - pass -# -*/ -PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - uint64 v; - if ( op == NULL || !PyW_GetNumber(py_v, &v) ) - Py_RETURN_NONE; - - sval_t actval; - member_t *member = get_stkvar(*op, sval_t(v), &actval); - if ( member == NULL ) - Py_RETURN_NONE; - - return Py_BuildValue("(O" PY_SFMT64 ")", - SWIG_NewPointerObj(SWIG_as_voidptr(member), SWIGTYPE_p_member_t, 0), - pyl_t(actval)); -} - -//------------------------------------------------------------------------- -/* -header: frame.hpp -# -def add_stkvar3(op, v, flags): - """ - Automatically add stack variable if doesn't exist - Processor modules should use ua_stkvar2() - @param op: reference to instruction operand - @param v: immediate value in the operand (usually op.addr) - @param flags: combination of STKVAR_... constants - @return: Boolean - """ - pass -# -*/ -bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - uint64 v; - return ( op == NULL || !PyW_GetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; -} - -//------------------------------------------------------------------------- -/* -header: frame.hpp -// Calculate offset of stack variable in the frame structure -// pfn - pointer to function (can't be NULL!) -// x - reference to instruction operand -// v - value of variable offset in the instruction -// returns: offset of stack variable in the frame structure (0..n) - -ea_t calc_frame_offset(func_t *pfn, const op_t *x, sval_t v); -*/ - -//------------------------------------------------------------------------- -/* -header: typeinf.hpp -# -def apply_type_to_stkarg(op, v, type, name): - """ - Apply type information to a stack variable - - @param op: reference to instruction operand - @param v: immediate value in the operand (usually op.addr) - @param type: type string. Retrieve from idc.ParseType("type string", flags)[1] - @param name: stack variable name - - @return: Boolean - """ - pass -# -*/ -bool py_apply_type_to_stkarg( - PyObject *py_op, - PyObject *py_uv, - PyObject *py_type, - const char *name) -{ - uint64 v; - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - if ( op == NULL || !PyW_GetNumber(py_uv, &v) || !PyString_Check(py_type)) - { - return false; - } - else - { - const type_t *t = (type_t *) PyString_AsString(py_type); - tinfo_t tif; - tif.deserialize(idati, &t); - borref_t br(py_op); - bool rc; - Py_BEGIN_ALLOW_THREADS; - rc = apply_tinfo_to_stkarg(*op, uval_t(v), tif, name); - Py_END_ALLOW_THREADS; - return rc; - } -} - -//------------------------------------------------------------------------- -/* -# -def OutImmChar(op, outflags = 0): - """ - Output operand value as a commented character constant - @param op: operand (of type op_t) - @return: None - """ - pass -# -*/ -static void py_OutImmChar(PyObject *x) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(x); - if ( op != NULL ) - OutImmChar(*op); -} - -//------------------------------------------------------------------------- -/* -# -def ua_stkvar2(op, outflags = 0): - """ - Create or modify a stack variable in the function frame. - Please check ua.hpp / ua_stkvar2() - @param op: operand (of type op_t) - @return: None - """ - pass -# -*/ -static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(x); - return op == NULL ? false : ua_stkvar2(*op, v, flags); -} - -//------------------------------------------------------------------------- -/* -# -def ua_add_off_drefs(op, type): - """ - Add xrefs for offset operand of the current instruction - Please check ua.hpp / ua_add_off_drefs() - @param op: operand (of type op_t) - @return: None - """ - pass -# -*/ -ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); -} - -//------------------------------------------------------------------------- -/* -# -def ua_add_off_drefs2(op, type, outf): - """ - Add xrefs for offset operand of the current instruction - Please check ua.hpp / ua_add_off_drefs2() - @return: ea_t - """ - pass -# -*/ -ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); -} - -//------------------------------------------------------------------------- -/* -# -def out_name_expr(op, ea, off): - """ - Output a name expression - @param op: operand (of type op_t) - @param ea: address of expression - @param off: the value of name expression. this parameter is used only to - check that the name expression will have the wanted value. - You may pass BADADDR for this parameter. - @return: true if the name expression has been produced - """ - pass -# -*/ -bool py_out_name_expr( - PyObject *py_op, - ea_t ea, - PyObject *py_off) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - uint64 v(0); - adiff_t off; - if ( PyW_GetNumber(py_off, &v) ) - off = adiff_t(v); - else - off = BADADDR; - - return op == NULL ? false : out_name_expr(*op, ea, off); -} - -//------------------------------------------------------------------------- -/* -# -def construct_macro(insn): - """ - See ua.hpp's construct_macro(). - """ - pass -# -*/ -bool py_construct_macro(bool enable, PyObject *build_macro) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !PyCallable_Check(build_macro) ) - return false; - - static qstack macro_builders; - - macro_builders.push(newref_t(build_macro)); - struct ida_local lambda_t - { - static bool idaapi call_build_macro(insn_t &s, bool may_go_forward) - { - PyObject *py_builder = macro_builders.top().o; - newref_t pyres = PyObject_CallFunction( - py_builder, "O", - may_go_forward ? Py_True : Py_False); - PyW_ShowCbErr("build_macro"); - if ( pyres.o == NULL || pyres.o == Py_None ) - return false; - insn_t *_s = insn_t_get_clink(pyres.o); - if ( _s == NULL ) - return false; - s = *_s; - return true; - } - }; - bool res = construct_macro(enable, lambda_t::call_build_macro); - macro_builders.pop(); - return res; -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) - Py_RETURN_NONE; - - // Extract C link - insn_t *insn = (insn_t *)PyCObject_AsVoidPtr(py_insn_lnk); - - // Return a link to the operand - return PyCObject_FromVoidPtr(&insn->Operands[i], NULL); -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_create() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new insn_t(), NULL); -} - -//------------------------------------------------------------------------- -static PyObject *op_t_create() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new op_t(), NULL); -} - -//------------------------------------------------------------------------- -static bool op_t_assign(PyObject *self, PyObject *other) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *lhs = op_t_get_clink(self); - op_t *rhs = op_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -static bool insn_t_assign(PyObject *self, PyObject *other) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *lhs = insn_t_get_clink(self); - insn_t *rhs = insn_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -static bool op_t_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - - op_t *op = (op_t *)PyCObject_AsVoidPtr(py_obj); - delete op; - - return true; -} - -//------------------------------------------------------------------------- -static bool insn_t_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - - delete (insn_t *)PyCObject_AsVoidPtr(py_obj); - return true; -} - -//------------------------------------------------------------------------- -// Returns a C link to the global 'cmd' variable -static PyObject *py_get_global_cmd_link() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(&::cmd, NULL); -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_is_canon_insn(int itype) -{ - bool ok = ph.is_canon_insn(itype); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_canon_feature(int itype) -{ - uint32 v = ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0; - PYW_GIL_CHECK_LOCKED_SCOPE(); - return Py_BuildValue("I", v); -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_canon_mnem(int itype) -{ - bool ok = ph.is_canon_insn(itype); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_cs(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->cs); -} - -static void insn_t_set_cs(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - - uint64 v(0); - PyW_GetNumber(value, &v); - link->cs = ea_t(v); -} - -static PyObject *insn_t_get_ip(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->ip); -} - -static void insn_t_set_ip(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->ip = ea_t(v); -} - -static PyObject *insn_t_get_ea(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->ea); -} - -static void insn_t_set_ea(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->ea = ea_t(v); -} - -static PyObject *insn_t_get_itype(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->itype); -} - -static void insn_t_set_itype(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->itype = (uint16)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_size(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->size); -} - -static void insn_t_set_size(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->size = (uint16)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_auxpref(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->auxpref); -} - -static void insn_t_set_auxpref(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->auxpref = (uint16)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_segpref(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->segpref); -} - -static void insn_t_set_segpref(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->segpref = (char)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_insnpref(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->insnpref); -} - -static void insn_t_set_insnpref(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->insnpref = (char)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_flags(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->flags); -} - -static void insn_t_set_flags(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->flags = (char)PyInt_AsLong(value); -} - -//------------------------------------------------------------------------- -static PyObject *op_t_get_n(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->n); -} - -static void op_t_set_n(PyObject *self, PyObject *value) -{ - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->n = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_type(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("B", link->type); -} - -static void op_t_set_type(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->type = (optype_t)PyInt_AsLong(value); -} - -static PyObject *op_t_get_offb(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->offb); -} - -static void op_t_set_offb(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->offb = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_offo(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->offo); -} - -static void op_t_set_offo(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->offo = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_flags(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("B", link->flags); -} - -static void op_t_set_flags(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->flags = (uchar)PyInt_AsLong(value); -} - -static PyObject *op_t_get_dtyp(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->dtyp); -} - -static void op_t_set_dtyp(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->dtyp = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_reg_phrase(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->reg); -} -static void op_t_set_reg_phrase(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->reg = (uint16)PyInt_AsLong(value); -} - -static PyObject *op_t_get_value(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->value); -} - -static void op_t_set_value(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->value = uval_t(v); -} - -static PyObject *op_t_get_addr(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->addr); -} - -static void op_t_set_addr(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->addr = ea_t(v); -} - -static PyObject *op_t_get_specval(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->specval); -} - -static void op_t_set_specval(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->specval = ea_t(v); -} - -static PyObject *op_t_get_specflag1(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag1); -} - -static void op_t_set_specflag1(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag1 = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_specflag2(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag2); -} - -static void op_t_set_specflag2(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag2 = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_specflag3(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag3); -} - -static void op_t_set_specflag3(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag3 = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_specflag4(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag4); -} - -static void op_t_set_specflag4(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag4 = (char)PyInt_AsLong(value); -} - -// - -#endif +#ifndef __PY_UA__ +#define __PY_UA__ + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- +insn_t *insn_t_get_clink(PyObject *self) +{ + return (insn_t *)pyobj_get_clink(self); +} + +//------------------------------------------------------------------------- +op_t *op_t_get_clink(PyObject *self) +{ + return (op_t *)pyobj_get_clink(self); +} +// + +//------------------------------------------------------------------------- +// + +//------------------------------------------------------------------------- +/* +# +def init_output_buffer(size = MAXSTR): + """ + This function initialize an output buffer with the given size. + It should be called before using any out_xxxx() functions. + @return: It returns a string. This string should then be passed to MakeLine(). + This function could return None if it failed to create a buffer with the given size. + """ + pass +# +*/ +PyObject *py_init_output_buffer(size_t size = MAXSTR) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Let Python allocate a writable string buffer for us + PyObject *py_str = PyString_FromStringAndSize(NULL, size); + if ( py_str == NULL ) + Py_RETURN_NONE; + + init_output_buffer(PyString_AsString(py_str), size); + return py_str; +} + +//------------------------------------------------------------------------- +/* +# +def term_output_buffer(): + """Use this function to terminate an output buffer.""" + pass +# +*/ +void py_term_output_buffer() +{ + term_output_buffer(); +} + +//------------------------------------------------------------------------- +/* +# +def decode_preceding_insn(ea): + """ + Decodes the preceding instruction. Please check ua.hpp / decode_preceding_insn() + @param ea: current ea + @return: tuple(preceeding_ea or BADADDR, farref = Boolean) + """ + pass +# +*/ +PyObject *py_decode_preceding_insn(ea_t ea) +{ + bool farref; + ea_t r = decode_preceding_insn(ea, &farref); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); +} + +//------------------------------------------------------------------------- +/* +# +def OutValue(op, outflags = 0): + """ + Output immediate value + @param op: operand (of type op_t) + @return: flags of the output value + -1: value is output with COLOR_ERROR + 0: value is output as a number or character or segment + """ + pass +# +*/ +flags_t py_OutValue(PyObject *x, int outflags=0) +{ + op_t *op = op_t_get_clink(x); + if ( op == NULL ) + return 0; + + return OutValue(*op, outflags); +} + +//------------------------------------------------------------------------- +/* +# +def get_stkvar(op, v): + """ + Get pointer to stack variable + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @return: + - None on failure + - tuple(member_t, actval) + where actval: actual value used to fetch stack variable + """ + pass +# +*/ +PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + uint64 v; + if ( op == NULL || !PyW_GetNumber(py_v, &v) ) + Py_RETURN_NONE; + + sval_t actval; + member_t *member = get_stkvar(*op, sval_t(v), &actval); + if ( member == NULL ) + Py_RETURN_NONE; + + return Py_BuildValue("(O" PY_SFMT64 ")", + SWIG_NewPointerObj(SWIG_as_voidptr(member), SWIGTYPE_p_member_t, 0), + pyl_t(actval)); +} + +//------------------------------------------------------------------------- +/* +header: frame.hpp +# +def add_stkvar3(op, v, flags): + """ + Automatically add stack variable if doesn't exist + Processor modules should use ua_stkvar2() + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @param flags: combination of STKVAR_... constants + @return: Boolean + """ + pass +# +*/ +bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + uint64 v; + return ( op == NULL || !PyW_GetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; +} + +//------------------------------------------------------------------------- +/* +header: typeinf.hpp +# +def apply_type_to_stkarg(op, v, type, name): + """ + Apply type information to a stack variable + + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @param type: type string. Retrieve from idc.ParseType("type string", flags)[1] + @param name: stack variable name + + @return: Boolean + """ + pass +# +*/ +bool py_apply_type_to_stkarg( + PyObject *py_op, + PyObject *py_uv, + PyObject *py_type, + const char *name) +{ + uint64 v; + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + if ( op == NULL || !PyW_GetNumber(py_uv, &v) || !PyString_Check(py_type)) + { + return false; + } + else + { + const type_t *t = (type_t *) PyString_AsString(py_type); + tinfo_t tif; + tif.deserialize(idati, &t); + borref_t br(py_op); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = apply_tinfo_to_stkarg(*op, uval_t(v), tif, name); + Py_END_ALLOW_THREADS; + return rc; + } +} + +//------------------------------------------------------------------------- +/* +# +def OutImmChar(op, outflags = 0): + """ + Output operand value as a commented character constant + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +static void py_OutImmChar(PyObject *x) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(x); + if ( op != NULL ) + OutImmChar(*op); +} + +//------------------------------------------------------------------------- +/* +# +def ua_stkvar2(op, outflags = 0): + """ + Create or modify a stack variable in the function frame. + Please check ua.hpp / ua_stkvar2() + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(x); + return op == NULL ? false : ua_stkvar2(*op, v, flags); +} + +//------------------------------------------------------------------------- +/* +# +def ua_add_off_drefs(op, type): + """ + Add xrefs for offset operand of the current instruction + Please check ua.hpp / ua_add_off_drefs() + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); +} + +//------------------------------------------------------------------------- +/* +# +def ua_add_off_drefs2(op, type, outf): + """ + Add xrefs for offset operand of the current instruction + Please check ua.hpp / ua_add_off_drefs2() + @return: ea_t + """ + pass +# +*/ +ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); +} + +//------------------------------------------------------------------------- +/* +# +def out_name_expr(op, ea, off): + """ + Output a name expression + @param op: operand (of type op_t) + @param ea: address of expression + @param off: the value of name expression. this parameter is used only to + check that the name expression will have the wanted value. + You may pass BADADDR for this parameter. + @return: true if the name expression has been produced + """ + pass +# +*/ +bool py_out_name_expr( + PyObject *py_op, + ea_t ea, + PyObject *py_off) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + uint64 v(0); + adiff_t off; + if ( PyW_GetNumber(py_off, &v) ) + off = adiff_t(v); + else + off = BADADDR; + + return op == NULL ? false : out_name_expr(*op, ea, off); +} + +//------------------------------------------------------------------------- +/* +# +def construct_macro(insn): + """ + See ua.hpp's construct_macro(). + """ + pass +# +*/ +bool py_construct_macro(bool enable, PyObject *build_macro) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !PyCallable_Check(build_macro) ) + return false; + + static qstack macro_builders; + + macro_builders.push(newref_t(build_macro)); + struct ida_local lambda_t + { + static bool idaapi call_build_macro(insn_t &s, bool may_go_forward) + { + PyObject *py_builder = macro_builders.top().o; + newref_t pyres = PyObject_CallFunction( + py_builder, "O", + may_go_forward ? Py_True : Py_False); + PyW_ShowCbErr("build_macro"); + if ( pyres.o == NULL || pyres.o == Py_None ) + return false; + insn_t *_s = insn_t_get_clink(pyres.o); + if ( _s == NULL ) + return false; + s = *_s; + return true; + } + }; + bool res = construct_macro(enable, lambda_t::call_build_macro); + macro_builders.pop(); + return res; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) + Py_RETURN_NONE; + + // Extract C link + insn_t *insn = (insn_t *)PyCObject_AsVoidPtr(py_insn_lnk); + + // Return a link to the operand + return PyCObject_FromVoidPtr(&insn->Operands[i], NULL); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_create() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new insn_t(), NULL); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_create() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new op_t(), NULL); +} + +//------------------------------------------------------------------------- +static bool op_t_assign(PyObject *self, PyObject *other) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *lhs = op_t_get_clink(self); + op_t *rhs = op_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool insn_t_assign(PyObject *self, PyObject *other) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *lhs = insn_t_get_clink(self); + insn_t *rhs = insn_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool op_t_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + + op_t *op = (op_t *)PyCObject_AsVoidPtr(py_obj); + delete op; + + return true; +} + +//------------------------------------------------------------------------- +static bool insn_t_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + + delete (insn_t *)PyCObject_AsVoidPtr(py_obj); + return true; +} + +//------------------------------------------------------------------------- +// Returns a C link to the global 'cmd' variable +static PyObject *py_get_global_cmd_link() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(&::cmd, NULL); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_is_canon_insn(int itype) +{ + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_canon_feature(int itype) +{ + uint32 v = ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("I", v); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_canon_mnem(int itype) +{ + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_cs(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->cs); +} + +static void insn_t_set_cs(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + + uint64 v(0); + PyW_GetNumber(value, &v); + link->cs = ea_t(v); +} + +static PyObject *insn_t_get_ip(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ip); +} + +static void insn_t_set_ip(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->ip = ea_t(v); +} + +static PyObject *insn_t_get_ea(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ea); +} + +static void insn_t_set_ea(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->ea = ea_t(v); +} + +static PyObject *insn_t_get_itype(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->itype); +} + +static void insn_t_set_itype(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->itype = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_size(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->size); +} + +static void insn_t_set_size(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->size = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_auxpref(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->auxpref); +} + +static void insn_t_set_auxpref(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->auxpref = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_segpref(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->segpref); +} + +static void insn_t_set_segpref(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->segpref = (char)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_insnpref(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->insnpref); +} + +static void insn_t_set_insnpref(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->insnpref = (char)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_flags(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->flags); +} + +static void insn_t_set_flags(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (char)PyInt_AsLong(value); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_get_n(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->n); +} + +static void op_t_set_n(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->n = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_type(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("B", link->type); +} + +static void op_t_set_type(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->type = (optype_t)PyInt_AsLong(value); +} + +static PyObject *op_t_get_offb(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->offb); +} + +static void op_t_set_offb(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->offb = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_offo(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->offo); +} + +static void op_t_set_offo(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->offo = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_flags(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("B", link->flags); +} + +static void op_t_set_flags(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (uchar)PyInt_AsLong(value); +} + +static PyObject *op_t_get_dtyp(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->dtyp); +} + +static void op_t_set_dtyp(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->dtyp = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_reg_phrase(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->reg); +} +static void op_t_set_reg_phrase(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->reg = (uint16)PyInt_AsLong(value); +} + +static PyObject *op_t_get_value(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->value); +} + +static void op_t_set_value(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->value = uval_t(v); +} + +static PyObject *op_t_get_addr(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->addr); +} + +static void op_t_set_addr(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->addr = ea_t(v); +} + +static PyObject *op_t_get_specval(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->specval); +} + +static void op_t_set_specval(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->specval = ea_t(v); +} + +static PyObject *op_t_get_specflag1(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag1); +} + +static void op_t_set_specflag1(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag1 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag2(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag2); +} + +static void op_t_set_specflag2(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag2 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag3(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag3); +} + +static void op_t_set_specflag3(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag3 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag4(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag4); +} + +static void op_t_set_specflag4(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag4 = (char)PyInt_AsLong(value); +} + +// + +#endif diff --git a/pywraps/py_ua.py b/pywraps/py_ua.py index 3daaee3a..ccee1f34 100644 --- a/pywraps/py_ua.py +++ b/pywraps/py_ua.py @@ -485,7 +485,7 @@ def get_idpdesc(self): short processor names similar to the one in ph.psnames. This method can be overridden to return to the kernel a different IDP description. """ - return self.plnames[0] + ':' + ':'.join(self.psnames) + return '\x01'.join(map(lambda t: '\x01'.join(t), zip(self.plnames, self.psnames))) def get_uFlag(self): """Use this utility function to retrieve the 'uFlag' global variable""" diff --git a/pywraps/py_view_base.hpp b/pywraps/py_view_base.hpp index 6c211a8d..858a12b4 100644 --- a/pywraps/py_view_base.hpp +++ b/pywraps/py_view_base.hpp @@ -3,13 +3,6 @@ // -//#define PYGDBG_ENABLED -#ifdef PYGDBG_ENABLED -#define PYGLOG(...) msg(__VA_ARGS__) -#else -#define PYGLOG(...) -#endif - //------------------------------------------------------------------------- class py_customidamemo_t; class lookup_info_t @@ -185,7 +178,7 @@ class py_customidamemo_t // Bi-directionally bind/unbind the Python object and this controller. bool bind(PyObject *_self, TCustomControl *view); - void unbind(); + void unbind(bool clear_view); static lookup_info_t lookup_info; friend TForm *pycim_get_tform(PyObject *self); @@ -248,7 +241,7 @@ py_customidamemo_t::py_customidamemo_t() py_customidamemo_t::~py_customidamemo_t() { PYGLOG("%p: ~py_customidamemo_t()\n", this); - unbind(); + unbind(true); lookup_info.del_by_py_view(this); } @@ -521,7 +514,7 @@ bool py_customidamemo_t::bind(PyObject *_self, TCustomControl *view) } //------------------------------------------------------------------------- -void py_customidamemo_t::unbind() +void py_customidamemo_t::unbind(bool clear_view) { if ( self == NULL ) return; @@ -530,7 +523,8 @@ void py_customidamemo_t::unbind() newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); PyObject_SetAttrString(self.o, S_M_THIS, py_cobj.o); self = newref_t(NULL); - view = NULL; + if ( clear_view ) + view = NULL; } //------------------------------------------------------------------------- @@ -622,71 +616,49 @@ void py_customidamemo_t::install_custom_viewer_handlers() return; \ PYW_GIL_CHECK_LOCKED_SCOPE() - -#ifdef PYGDBG_ENABLED -#define CHK_RES() \ - do \ - { \ - PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o); \ - if (PyErr_Occurred()) \ - PyErr_Print(); \ - } while ( false ) -#else -#define CHK_RES() \ - do \ - { \ - if (PyErr_Occurred()) \ - PyErr_Print(); \ - } while ( false ) -#endif - //------------------------------------------------------------------------- void py_customidamemo_t::on_view_activated() { CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_ACTIVATED, NULL)); - CHK_RES(); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_deactivated() { CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_DEACTIVATED, NULL)); - CHK_RES(); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_keydown(int key, int state) { CHK_EVT(GRBASE_HAVE_KEYDOWN); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_KEYDOWN, "ii", key, state)); - CHK_RES(); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_popup() { CHK_EVT(GRBASE_HAVE_POPUP); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_POPUP, NULL)); - CHK_RES(); } //------------------------------------------------------------------------- @@ -707,33 +679,30 @@ void py_customidamemo_t::on_view_click(const view_mouse_event_t *event) if ( ovc_num_args == 6 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CLICK, "iiiiO", event->x, event->y, event->state, event->button, rpos)); - CHK_RES(); } else if ( ovc_num_args == 5 ) { - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CLICK, "iiii", event->x, event->y, event->state, event->button)); - CHK_RES(); } else { - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CLICK, "iii", event->x, event->y, event->state)); - CHK_RES(); } } @@ -746,23 +715,21 @@ void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) if ( ovdc_num_args == 5 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_DBLCLICK, "iiiO", event->x, event->y, event->state, rpos)); - CHK_RES(); } else { - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_DBLCLICK, "iii", event->x, event->y, event->state)); - CHK_RES(); } } @@ -770,28 +737,25 @@ void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) void py_customidamemo_t::on_view_curpos() { CHK_EVT(GRBASE_HAVE_VIEW_CURPOS); - newref_t result( + pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CURPOS, NULL)); - CHK_RES(); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_close() { CHK_EVT(GRBASE_HAVE_CLOSE); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); - CHK_RES(); + pycall_res_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt) { CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); - CHK_RES(); + pycall_res_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); } //------------------------------------------------------------------------- @@ -835,21 +799,19 @@ void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event) if ( ovmo_num_args == 7 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result(PyObject_CallMethod( + pycall_res_t result(PyObject_CallMethod( self.o, (char *)S_ON_VIEW_MOUSE_OVER, "iiiiOO", event->x, event->y, event->state, icode, tuple.o, rpos)); - CHK_RES(); } else { - newref_t result(PyObject_CallMethod( + pycall_res_t result(PyObject_CallMethod( self.o, (char *)S_ON_VIEW_MOUSE_OVER, "iiiiO", event->x, event->y, event->state, icode, tuple.o)); - CHK_RES(); } } @@ -865,17 +827,15 @@ void py_customidamemo_t::on_view_mouse_moved(const view_mouse_event_t *event) if ( ovmm_num_args == 7 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result(PyObject_CallMethod( + pycall_res_t result(PyObject_CallMethod( self.o, (char *)S_ON_VIEW_MOUSE_MOVED, "iiiiOO", event->x, event->y, event->state, icode, tuple.o, rpos)); - CHK_RES(); } } -#undef CHK_RES #undef CHK_EVT //------------------------------------------------------------------------- diff --git a/pywraps/pywraps.hpp b/pywraps/pywraps.hpp index 987a84bf..be3cf327 100644 --- a/pywraps/pywraps.hpp +++ b/pywraps/pywraps.hpp @@ -1,2 +1,2 @@ -// Just a proxy header +// Just a proxy header #include "../pywraps.hpp" \ No newline at end of file diff --git a/pywraps/pywraps.sln b/pywraps/pywraps.sln index a92f6b49..fce00728 100644 --- a/pywraps/pywraps.sln +++ b/pywraps/pywraps.sln @@ -1,32 +1,32 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pywraps", "pywraps.vcxproj", "{F43D6BB8-B7D6-486A-82E5-BABBA9848525}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Rel64|Win32 = Rel64|Win32 - Release|Win32 = Release|Win32 - Release64|Win32 = Release64|Win32 - SemiDebug|Win32 = SemiDebug|Win32 - SemiDebugx64|Win32 = SemiDebugx64|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.ActiveCfg = Debug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.Build.0 = Debug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Rel64|Win32.ActiveCfg = Rel64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Rel64|Win32.Build.0 = Rel64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.ActiveCfg = Release|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.Build.0 = Release|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release64|Win32.ActiveCfg = Release64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release64|Win32.Build.0 = Release64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.ActiveCfg = SemiDebug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.Build.0 = SemiDebug|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebugx64|Win32.ActiveCfg = SemiDebugx64|Win32 - {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebugx64|Win32.Build.0 = SemiDebugx64|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pywraps", "pywraps.vcxproj", "{F43D6BB8-B7D6-486A-82E5-BABBA9848525}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Rel64|Win32 = Rel64|Win32 + Release|Win32 = Release|Win32 + Release64|Win32 = Release64|Win32 + SemiDebug|Win32 = SemiDebug|Win32 + SemiDebugx64|Win32 = SemiDebugx64|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.ActiveCfg = Debug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.Build.0 = Debug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Rel64|Win32.ActiveCfg = Rel64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Rel64|Win32.Build.0 = Rel64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.ActiveCfg = Release|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.Build.0 = Release|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release64|Win32.ActiveCfg = Release64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release64|Win32.Build.0 = Release64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.ActiveCfg = SemiDebug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.Build.0 = SemiDebug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebugx64|Win32.ActiveCfg = SemiDebugx64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebugx64|Win32.Build.0 = SemiDebugx64|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pywraps/pywraps.vcproj b/pywraps/pywraps.vcproj index f0dad192..50335a95 100644 --- a/pywraps/pywraps.vcproj +++ b/pywraps/pywraps.vcproj @@ -1,1844 +1,1844 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pywraps/readme.txt b/pywraps/readme.txt index 5ff73f87..dfc562d7 100644 --- a/pywraps/readme.txt +++ b/pywraps/readme.txt @@ -1,116 +1,116 @@ -============================ -deploy.py - usage -============================ - -The deploy script is used to deploy python and c++ code into SWIG interface files appropriately. -The reason it was created was because working with .i files to put a mixture of C++ and Python code is not practical for testing and development process. - -In SWIG, there are three sections: - -Inline ---------- - -C++ code will be wrapped by SWIG. - -In SWIG .i files the inline code is marked with: - %inline %{ - C++ code - %} - -In deploy.py supporting files the code to be pasted into .i files is marked with: - // - C++ code - // - - -Code -------- -C++ code will be pasted and compiled into the wrapped module but will not be wrapped by SWIG. - -In SWIG .i files the code is marked with: - %{ - C++ code - %} - -Similarly, for deploy.py supporting files should be marked with: - // - C++ code - // - -Pythoncode --------------- - -Python code allows you to insert Python code into the final Python module. - -In SWIG .i files, the extra python code is marked with: - %pythoncode %{ - Py code - %} - -In deploy.py supporting python files, it is marked with: - # - Py code - # - -Using deploy.py ------------------- -Make sure that all of the 3 code markers exist in the interface files and deploy.py support files (C++ or Python). - -As an example, let us interpret the meaning of: - deploy.py py_idaapi py_idaapi.hpp,py_idaapi.py ..\swig\idaapi.i -It means: - NAME = py_idaapi - ...take code snips from py_idaapi.hpp and py_idaapi.py - ...and paste the code there into idaapi.i SWIG interface file - -Now remember that both the input files have the special markers (discussed above) and so does idaapi.i file - - -============================ -linkgen.py - usage -============================ -TODO - - -============================ -swigdocs.py - usage -============================ - -The swigdocs script will extract python comments from SWIG interface files (*.i). - -There are two places where Python code documentation can be found: - 1. In the "%pythoncode %{" section, we extract all the python code because it could contain docstrings. - Inside the pythoncode section, one can find embedded commented that are commented out. - Because they are commented out, the documentation generator will miss them. The swigdocs script will remove the comment character: -# -# def OnClose(self): -# """ -# Called when the window is being closed. -# This callback is mandatory. -# @return: nothing -# """ -# pass -# - After swigdocs finishes, the output will contain all the python code and all the commented code (now uncommented). - - 2. In the "%inline %{" section (in C++ code), one can find functions comments like this: -/* -# -def dbg_read_memory(ea, sz): - """ - Reads from the debugee's memory at the specified ea - @return: - - The read buffer (as a string) - - Or None on failure - """ - pass -# -*/ -static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) -{ - ...... -} - In this case, the code inside tag will be extracted as well. - - +============================ +deploy.py - usage +============================ + +The deploy script is used to deploy python and c++ code into SWIG interface files appropriately. +The reason it was created was because working with .i files to put a mixture of C++ and Python code is not practical for testing and development process. + +In SWIG, there are three sections: + +Inline +--------- + +C++ code will be wrapped by SWIG. + +In SWIG .i files the inline code is marked with: + %inline %{ + C++ code + %} + +In deploy.py supporting files the code to be pasted into .i files is marked with: + // + C++ code + // + + +Code +------- +C++ code will be pasted and compiled into the wrapped module but will not be wrapped by SWIG. + +In SWIG .i files the code is marked with: + %{ + C++ code + %} + +Similarly, for deploy.py supporting files should be marked with: + // + C++ code + // + +Pythoncode +-------------- + +Python code allows you to insert Python code into the final Python module. + +In SWIG .i files, the extra python code is marked with: + %pythoncode %{ + Py code + %} + +In deploy.py supporting python files, it is marked with: + # + Py code + # + +Using deploy.py +------------------ +Make sure that all of the 3 code markers exist in the interface files and deploy.py support files (C++ or Python). + +As an example, let us interpret the meaning of: + deploy.py py_idaapi py_idaapi.hpp,py_idaapi.py ..\swig\idaapi.i +It means: + NAME = py_idaapi + ...take code snips from py_idaapi.hpp and py_idaapi.py + ...and paste the code there into idaapi.i SWIG interface file + +Now remember that both the input files have the special markers (discussed above) and so does idaapi.i file + + +============================ +linkgen.py - usage +============================ +TODO + + +============================ +swigdocs.py - usage +============================ + +The swigdocs script will extract python comments from SWIG interface files (*.i). + +There are two places where Python code documentation can be found: + 1. In the "%pythoncode %{" section, we extract all the python code because it could contain docstrings. + Inside the pythoncode section, one can find embedded commented that are commented out. + Because they are commented out, the documentation generator will miss them. The swigdocs script will remove the comment character: +# +# def OnClose(self): +# """ +# Called when the window is being closed. +# This callback is mandatory. +# @return: nothing +# """ +# pass +# + After swigdocs finishes, the output will contain all the python code and all the commented code (now uncommented). + + 2. In the "%inline %{" section (in C++ code), one can find functions comments like this: +/* +# +def dbg_read_memory(ea, sz): + """ + Reads from the debugee's memory at the specified ea + @return: + - The read buffer (as a string) + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) +{ + ...... +} + In this case, the code inside tag will be extracted as well. + + After swigdocs finishes, the output is a Python file containing all code and comments extracted from the *.i file(s). \ No newline at end of file diff --git a/pywraps/sidaapi.py b/pywraps/sidaapi.py index 80309e6d..f3dbf472 100644 --- a/pywraps/sidaapi.py +++ b/pywraps/sidaapi.py @@ -1,228 +1,228 @@ -import sys -import sidc - -BUF = None - -# ---------------------------------------------------------------------- -def ua_next_byte(): - p = cmd.ea - if p >= len(BUF): - return None - ch = ord(BUF[p]) - ua_seek(p+1) - cmd.size += 1 - return ch - -def ua_next_word(): - b1 = ua_next_byte() - b2 = ua_next_byte() - return (b2 << 8) | b1 - -def ua_next_long(): - w1 = ua_next_word() - w2 = ua_next_word() - return (w2 << 16) | w1 - -def ua_next_qword(): - d1 = ua_next_long() - d2 = ua_next_long() - return (d2 << 32) | d1 - -# ---------------------------------------------------------------------- -def ua_set_data(data): - global BUF - BUF = data - ua_seek(0) - -# ---------------------------------------------------------------------- -def ua_seek(v): - cmd.ea = v - -# ---------------------------------------------------------------------- -def ua_get_seek(): - return cmd.ea - -# ---------------------------------------------------------------------- -def init_file(fn): - global BUF, PTR - try: - f = open(fn, "rb") - BUF = f.read() - ua_seek(0) - f.close() - except Exception, e: - print "init_file()", e - return False - return True - -# ---------------------------------------------------------------------- -class cvar_t: - def __init__(self): - self.uFlag = 0 - self.gl_comm = 1 -# ---------------------------------------------------------------------- -cvar = cvar_t() -cmd = sidc.insn_t() - -# ---------------------------------------------------------------------- -class processor_t(object): - def __init__(self): - # This is an opaque object - self.__idc_cvt_id__ = 2 - - # Take a reference to 'cmd' - self.cmd = cmd - - def get_idpdesc(self): - """ - This function must be present and should return the list of - short processor names similar to the one in ph.psnames - """ - return idpdef['plnames'][0] + ':' + ':'.join(idpdef['psnames']) - - def get_uFlag(self): - """Use this utility function to retrieve the 'uFlag' global variable""" - return _idaapi.cvar.uFlag - - def get_auxpref(self): - return self.cmd.auxpref - -# ---------------------------------------------------------------------- -BADADDR = 0xFFFFFFFFFFFFFFFFL - -# ---------------------------------------------------------------------- -UA_MAXOP = 6 -o_void = 0 -# ---------------------------------------------------------------------- - -""" -# Colors dump - -attrs = [x for x in dir(idaapi) if x.startswith('SCOLOR')] -for x in attrs: - print "%s =%r;" % (x, getattr(idaapi, x)) - -attrs = [x for x in dir(idaapi) if x.startswith('COLOR')] -for x in attrs: - v = getattr(idaapi, x); - if isinstance(v, str): - v = ord(x[0]) - print "%s =%r;" % (x, v) -""" - -SCOLOR_ADDR ='('; -SCOLOR_ALTOP ='\x16'; -SCOLOR_ASMDIR ='\x1b'; -SCOLOR_AUTOCMT ='\x04'; -SCOLOR_BINPREF ='\x14'; -SCOLOR_CHAR ='\n'; -SCOLOR_CNAME ='%'; -SCOLOR_CODNAME ='\x1a'; -SCOLOR_COLLAPSED ="'"; -SCOLOR_CREF ='\x0e'; -SCOLOR_CREFTAIL ='\x10'; -SCOLOR_DATNAME ='\x06'; -SCOLOR_DCHAR ='\x1e'; -SCOLOR_DEFAULT ='\x01'; -SCOLOR_DEMNAME ='\x08'; -SCOLOR_DNAME ='\x07'; -SCOLOR_DNUM ='\x1f'; -SCOLOR_DREF ='\x0f'; -SCOLOR_DREFTAIL ='\x11'; -SCOLOR_DSTR ='\x1d'; -SCOLOR_ERROR ='\x12'; -SCOLOR_ESC ='\x03'; -SCOLOR_EXTRA ='\x15'; -SCOLOR_FG_MAX ='('; -SCOLOR_HIDNAME ='\x17'; -SCOLOR_IMPNAME ='"'; -SCOLOR_INSN ='\x05'; -SCOLOR_INV ='\x04'; -SCOLOR_KEYWORD =' '; -SCOLOR_LIBNAME ='\x18'; -SCOLOR_LOCNAME ='\x19'; -SCOLOR_MACRO ='\x1c'; -SCOLOR_NUMBER ='\x0c'; -SCOLOR_OFF ='\x02'; -SCOLOR_ON ='\x01'; -SCOLOR_OPND1 =')'; -SCOLOR_OPND2 ='*'; -SCOLOR_OPND3 ='+'; -SCOLOR_OPND4 =','; -SCOLOR_OPND5 ='-'; -SCOLOR_OPND6 ='.'; -SCOLOR_PREFIX ='\x13'; -SCOLOR_REG ='!'; -SCOLOR_REGCMT ='\x02'; -SCOLOR_RPTCMT ='\x03'; -SCOLOR_SEGNAME ='#'; -SCOLOR_STRING ='\x0b'; -SCOLOR_SYMBOL ='\t'; -SCOLOR_UNAME ='&'; -SCOLOR_UNKNAME ='$'; -SCOLOR_UTF8 ='2'; -SCOLOR_VOIDOP ='\r'; -COLOR_ADDR =40; -COLOR_ADDR_SIZE =8; -COLOR_ALTOP =22; -COLOR_ASMDIR =27; -COLOR_AUTOCMT =4; -COLOR_BG_MAX =12; -COLOR_BINPREF =20; -COLOR_CHAR =10; -COLOR_CNAME =37; -COLOR_CODE =5; -COLOR_CODNAME =26; -COLOR_COLLAPSED =39; -COLOR_CREF =14; -COLOR_CREFTAIL =16; -COLOR_CURITEM =9; -COLOR_CURLINE =10; -COLOR_DATA =6; -COLOR_DATNAME =6; -COLOR_DCHAR =30; -COLOR_DEFAULT =1; -COLOR_DEMNAME =8; -COLOR_DNAME =7; -COLOR_DNUM =31; -COLOR_DREF =15; -COLOR_DREFTAIL =17; -COLOR_DSTR =29; -COLOR_ERROR =18; -COLOR_ESC =3; -COLOR_EXTERN =8; -COLOR_EXTRA =21; -COLOR_FG_MAX =40; -COLOR_HIDLINE =11; -COLOR_HIDNAME =23; -COLOR_IMPNAME =34; -COLOR_INSN =5; -COLOR_INV =4; -COLOR_KEYWORD =32; -COLOR_LIBFUNC =3; -COLOR_LIBNAME =24; -COLOR_LOCNAME =25; -COLOR_MACRO =28; -COLOR_NUMBER =12; -COLOR_OFF = 2; -COLOR_ON = 1; -COLOR_OPND1 =41; -COLOR_OPND2 =42; -COLOR_OPND3 =43; -COLOR_OPND4 =44; -COLOR_OPND5 =45; -COLOR_OPND6 =46; -COLOR_PREFIX =19; -COLOR_REG =33; -COLOR_REGCMT =2; -COLOR_REGFUNC =4; -COLOR_RPTCMT =3; -COLOR_SEGNAME =35; -COLOR_SELECTED =2; -COLOR_STRING =11; -COLOR_SYMBOL =9; -COLOR_UNAME =38; -COLOR_UNKNAME =36; -COLOR_UNKNOWN =7; -COLOR_UTF8 =50; +import sys +import sidc + +BUF = None + +# ---------------------------------------------------------------------- +def ua_next_byte(): + p = cmd.ea + if p >= len(BUF): + return None + ch = ord(BUF[p]) + ua_seek(p+1) + cmd.size += 1 + return ch + +def ua_next_word(): + b1 = ua_next_byte() + b2 = ua_next_byte() + return (b2 << 8) | b1 + +def ua_next_long(): + w1 = ua_next_word() + w2 = ua_next_word() + return (w2 << 16) | w1 + +def ua_next_qword(): + d1 = ua_next_long() + d2 = ua_next_long() + return (d2 << 32) | d1 + +# ---------------------------------------------------------------------- +def ua_set_data(data): + global BUF + BUF = data + ua_seek(0) + +# ---------------------------------------------------------------------- +def ua_seek(v): + cmd.ea = v + +# ---------------------------------------------------------------------- +def ua_get_seek(): + return cmd.ea + +# ---------------------------------------------------------------------- +def init_file(fn): + global BUF, PTR + try: + f = open(fn, "rb") + BUF = f.read() + ua_seek(0) + f.close() + except Exception, e: + print "init_file()", e + return False + return True + +# ---------------------------------------------------------------------- +class cvar_t: + def __init__(self): + self.uFlag = 0 + self.gl_comm = 1 +# ---------------------------------------------------------------------- +cvar = cvar_t() +cmd = sidc.insn_t() + +# ---------------------------------------------------------------------- +class processor_t(object): + def __init__(self): + # This is an opaque object + self.__idc_cvt_id__ = 2 + + # Take a reference to 'cmd' + self.cmd = cmd + + def get_idpdesc(self): + """ + This function must be present and should return the list of + short processor names similar to the one in ph.psnames + """ + return idpdef['plnames'][0] + ':' + ':'.join(idpdef['psnames']) + + def get_uFlag(self): + """Use this utility function to retrieve the 'uFlag' global variable""" + return _idaapi.cvar.uFlag + + def get_auxpref(self): + return self.cmd.auxpref + +# ---------------------------------------------------------------------- +BADADDR = 0xFFFFFFFFFFFFFFFFL + +# ---------------------------------------------------------------------- +UA_MAXOP = 6 +o_void = 0 +# ---------------------------------------------------------------------- + +""" +# Colors dump + +attrs = [x for x in dir(idaapi) if x.startswith('SCOLOR')] +for x in attrs: + print "%s =%r;" % (x, getattr(idaapi, x)) + +attrs = [x for x in dir(idaapi) if x.startswith('COLOR')] +for x in attrs: + v = getattr(idaapi, x); + if isinstance(v, str): + v = ord(x[0]) + print "%s =%r;" % (x, v) +""" + +SCOLOR_ADDR ='('; +SCOLOR_ALTOP ='\x16'; +SCOLOR_ASMDIR ='\x1b'; +SCOLOR_AUTOCMT ='\x04'; +SCOLOR_BINPREF ='\x14'; +SCOLOR_CHAR ='\n'; +SCOLOR_CNAME ='%'; +SCOLOR_CODNAME ='\x1a'; +SCOLOR_COLLAPSED ="'"; +SCOLOR_CREF ='\x0e'; +SCOLOR_CREFTAIL ='\x10'; +SCOLOR_DATNAME ='\x06'; +SCOLOR_DCHAR ='\x1e'; +SCOLOR_DEFAULT ='\x01'; +SCOLOR_DEMNAME ='\x08'; +SCOLOR_DNAME ='\x07'; +SCOLOR_DNUM ='\x1f'; +SCOLOR_DREF ='\x0f'; +SCOLOR_DREFTAIL ='\x11'; +SCOLOR_DSTR ='\x1d'; +SCOLOR_ERROR ='\x12'; +SCOLOR_ESC ='\x03'; +SCOLOR_EXTRA ='\x15'; +SCOLOR_FG_MAX ='('; +SCOLOR_HIDNAME ='\x17'; +SCOLOR_IMPNAME ='"'; +SCOLOR_INSN ='\x05'; +SCOLOR_INV ='\x04'; +SCOLOR_KEYWORD =' '; +SCOLOR_LIBNAME ='\x18'; +SCOLOR_LOCNAME ='\x19'; +SCOLOR_MACRO ='\x1c'; +SCOLOR_NUMBER ='\x0c'; +SCOLOR_OFF ='\x02'; +SCOLOR_ON ='\x01'; +SCOLOR_OPND1 =')'; +SCOLOR_OPND2 ='*'; +SCOLOR_OPND3 ='+'; +SCOLOR_OPND4 =','; +SCOLOR_OPND5 ='-'; +SCOLOR_OPND6 ='.'; +SCOLOR_PREFIX ='\x13'; +SCOLOR_REG ='!'; +SCOLOR_REGCMT ='\x02'; +SCOLOR_RPTCMT ='\x03'; +SCOLOR_SEGNAME ='#'; +SCOLOR_STRING ='\x0b'; +SCOLOR_SYMBOL ='\t'; +SCOLOR_UNAME ='&'; +SCOLOR_UNKNAME ='$'; +SCOLOR_UTF8 ='2'; +SCOLOR_VOIDOP ='\r'; +COLOR_ADDR =40; +COLOR_ADDR_SIZE =8; +COLOR_ALTOP =22; +COLOR_ASMDIR =27; +COLOR_AUTOCMT =4; +COLOR_BG_MAX =12; +COLOR_BINPREF =20; +COLOR_CHAR =10; +COLOR_CNAME =37; +COLOR_CODE =5; +COLOR_CODNAME =26; +COLOR_COLLAPSED =39; +COLOR_CREF =14; +COLOR_CREFTAIL =16; +COLOR_CURITEM =9; +COLOR_CURLINE =10; +COLOR_DATA =6; +COLOR_DATNAME =6; +COLOR_DCHAR =30; +COLOR_DEFAULT =1; +COLOR_DEMNAME =8; +COLOR_DNAME =7; +COLOR_DNUM =31; +COLOR_DREF =15; +COLOR_DREFTAIL =17; +COLOR_DSTR =29; +COLOR_ERROR =18; +COLOR_ESC =3; +COLOR_EXTERN =8; +COLOR_EXTRA =21; +COLOR_FG_MAX =40; +COLOR_HIDLINE =11; +COLOR_HIDNAME =23; +COLOR_IMPNAME =34; +COLOR_INSN =5; +COLOR_INV =4; +COLOR_KEYWORD =32; +COLOR_LIBFUNC =3; +COLOR_LIBNAME =24; +COLOR_LOCNAME =25; +COLOR_MACRO =28; +COLOR_NUMBER =12; +COLOR_OFF = 2; +COLOR_ON = 1; +COLOR_OPND1 =41; +COLOR_OPND2 =42; +COLOR_OPND3 =43; +COLOR_OPND4 =44; +COLOR_OPND5 =45; +COLOR_OPND6 =46; +COLOR_PREFIX =19; +COLOR_REG =33; +COLOR_REGCMT =2; +COLOR_REGFUNC =4; +COLOR_RPTCMT =3; +COLOR_SEGNAME =35; +COLOR_SELECTED =2; +COLOR_STRING =11; +COLOR_SYMBOL =9; +COLOR_UNAME =38; +COLOR_UNKNAME =36; +COLOR_UNKNOWN =7; +COLOR_UTF8 =50; COLOR_VOIDOP =13; \ No newline at end of file diff --git a/pywraps/sidc.py b/pywraps/sidc.py index e2315850..dc7dcc7a 100644 --- a/pywraps/sidc.py +++ b/pywraps/sidc.py @@ -1,313 +1,313 @@ -# ---------------------------------------------------------------------- -# -# Misc constants -# -UA_MAXOP = 6 - -# ---------------------------------------------------------------------- -# instruc_t related constants - -# -# instruc_t.feature -# -CF_STOP = 0x00001 # Instruction doesn't pass execution to the next instruction -CF_CALL = 0x00002 # CALL instruction (should make a procedure here) -CF_CHG1 = 0x00004 # The instruction modifies the first operand -CF_CHG2 = 0x00008 # The instruction modifies the second operand -CF_CHG3 = 0x00010 # The instruction modifies the third operand -CF_CHG4 = 0x00020 # The instruction modifies 4 operand -CF_CHG5 = 0x00040 # The instruction modifies 5 operand -CF_CHG6 = 0x00080 # The instruction modifies 6 operand -CF_USE1 = 0x00100 # The instruction uses value of the first operand -CF_USE2 = 0x00200 # The instruction uses value of the second operand -CF_USE3 = 0x00400 # The instruction uses value of the third operand -CF_USE4 = 0x00800 # The instruction uses value of the 4 operand -CF_USE5 = 0x01000 # The instruction uses value of the 5 operand -CF_USE6 = 0x02000 # The instruction uses value of the 6 operand -CF_JUMP = 0x04000 # The instruction passes execution using indirect jump or call (thus needs additional analysis) -CF_SHFT = 0x08000 # Bit-shift instruction (shl,shr...) -CF_HLL = 0x10000 # Instruction may be present in a high level language function. - -# ---------------------------------------------------------------------- -# op_t related constants - -# -# op_t.type -# Description Data field -o_void = 0 # No Operand ---------- -o_reg = 1 # General Register (al,ax,es,ds...) reg -o_mem = 2 # Direct Memory Reference (DATA) addr -o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase -o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr -o_imm = 5 # Immediate Value value -o_far = 6 # Immediate Far Address (CODE) addr -o_near = 7 # Immediate Near Address (CODE) addr -o_idpspec0 = 8 # Processor specific type -o_idpspec1 = 9 # Processor specific type -o_idpspec2 = 10 # Processor specific type -o_idpspec3 = 11 # Processor specific type -o_idpspec4 = 12 # Processor specific type -o_idpspec5 = 13 # Processor specific type - # There can be more processor specific types - -# -# op_t.dtyp -# -dt_byte = 0 # 8 bit -dt_word = 1 # 16 bit -dt_dword = 2 # 32 bit -dt_float = 3 # 4 byte -dt_double = 4 # 8 byte -dt_tbyte = 5 # variable size (ph.tbyte_size) -dt_packreal = 6 # packed real format for mc68040 -dt_qword = 7 # 64 bit -dt_byte16 = 8 # 128 bit -dt_code = 9 # ptr to code (not used?) -dt_void = 10 # none -dt_fword = 11 # 48 bit -dt_bitfild = 12 # bit field (mc680x0) -dt_string = 13 # pointer to asciiz string -dt_unicode = 14 # pointer to unicode string -dt_3byte = 15 # 3-byte data -dt_ldbl = 16 # long double (which may be different from tbyte) -dt_byte32 = 17 # 256 bit -dt_byte64 = 18 # 512 bit - -# -# op_t.flags -# -OF_NO_BASE_DISP = 0x80 # o_displ: base displacement doesn't exist meaningful only for o_displ type if set, base displacement (x.addr) doesn't exist. -OF_OUTER_DISP = 0x40 # o_displ: outer displacement exists meaningful only for o_displ type if set, outer displacement (x.value) exists. -PACK_FORM_DEF = 0x20 # !o_reg + dt_packreal: packed factor defined -OF_NUMBER = 0x10 # can be output as number only if set, the operand can be converted to a number only -OF_SHOW = 0x08 # should the operand be displayed? if clear, the operand is hidden and should not be displayed - -# -# insn_t.flags -# -INSN_MACRO = 0x01 # macro instruction -INSN_MODMAC = 0x02 # macros: may modify the database to make room for the macro insn - -# ---------------------------------------------------------------------- -# asm_t related constants - -# -# asm_t.flag -# -AS_OFFST = 0x00000001 # offsets are 'offset xxx' ? -AS_COLON = 0x00000002 # create colons after data names ? -AS_UDATA = 0x00000004 # can use '?' in data directives - -AS_2CHRE = 0x00000008 # double char constants are: "xy -AS_NCHRE = 0x00000010 # char constants are: 'x -AS_N2CHR = 0x00000020 # can't have 2 byte char consts - - # ASCII directives: -AS_1TEXT = 0x00000040 # 1 text per line, no bytes -AS_NHIAS = 0x00000080 # no characters with high bit -AS_NCMAS = 0x00000100 # no commas in ascii directives - -AS_HEXFM = 0x00000E00 # format of hex numbers: -ASH_HEXF0 = 0x00000000 # 34h -ASH_HEXF1 = 0x00000200 # h'34 -ASH_HEXF2 = 0x00000400 # 34 -ASH_HEXF3 = 0x00000600 # 0x34 -ASH_HEXF4 = 0x00000800 # $34 -ASH_HEXF5 = 0x00000A00 # <^R > (radix) -AS_DECFM = 0x00003000 # format of dec numbers: -ASD_DECF0 = 0x00000000 # 34 -ASD_DECF1 = 0x00001000 # #34 -ASD_DECF2 = 0x00002000 # 34. -ASD_DECF3 = 0x00003000 # .34 -AS_OCTFM = 0x0001C000 # format of octal numbers: -ASO_OCTF0 = 0x00000000 # 123o -ASO_OCTF1 = 0x00004000 # 0123 -ASO_OCTF2 = 0x00008000 # 123 -ASO_OCTF3 = 0x0000C000 # @123 -ASO_OCTF4 = 0x00010000 # o'123 -ASO_OCTF5 = 0x00014000 # 123q -ASO_OCTF6 = 0x00018000 # ~123 -AS_BINFM = 0x000E0000 # format of binary numbers: -ASB_BINF0 = 0x00000000 # 010101b -ASB_BINF1 = 0x00020000 # ^B010101 -ASB_BINF2 = 0x00040000 # %010101 -ASB_BINF3 = 0x00060000 # 0b1010101 -ASB_BINF4 = 0x00080000 # b'1010101 -ASB_BINF5 = 0x000A0000 # b'1010101' - -AS_UNEQU = 0x00100000 # replace undefined data items - # with EQU (for ANTA's A80) -AS_ONEDUP = 0x00200000 # One array definition per line -AS_NOXRF = 0x00400000 # Disable xrefs during the output file generation -AS_XTRNTYPE = 0x00800000 # Assembler understands type of extrn - # symbols as ":type" suffix -AS_RELSUP = 0x01000000 # Checkarg: 'and','or','xor' operations - # with addresses are possible -AS_LALIGN = 0x02000000 # Labels at "align" keyword - # are supported. -AS_NOCODECLN = 0x04000000 # don't create colons after code names -AS_NOTAB = 0x08000000 # Disable tabulation symbols during the output file generation -AS_NOSPACE = 0x10000000 # No spaces in expressions -AS_ALIGN2 = 0x20000000 # .align directive expects an exponent rather than a power of 2 - # (.align 5 means to align at 32byte boundary) -AS_ASCIIC = 0x40000000 # ascii directive accepts C-like - # escape sequences (\n,\x01 and similar) -AS_ASCIIZ = 0x80000000 # ascii directive inserts implicit - # zero byte at the end - -# ---------------------------------------------------------------------- -# processor_t related constants - -IDP_INTERFACE_VERSION = 76 -CUSTOM_CMD_ITYPE = 0x8000 -REG_SPOIL = 0x80000000 - -REAL_ERROR_FORMAT = -1 # not supported format for current .idp -REAL_ERROR_RANGE = -2 # number too big (small) for store (mem NOT modifyed) -REAL_ERROR_BADDATA = -3 # illegal real data for load (IEEE data not filled) - -# -# Check whether the operand is relative to stack pointer or frame pointer. -# This function is used to determine how to output a stack variable -# This function may be absent. If it is absent, then all operands -# are sp based by default. -# Define this function only if some stack references use frame pointer -# instead of stack pointer. -# returns flags: -OP_FP_BASED = 0x00000000 # operand is FP based -OP_SP_BASED = 0x00000001 # operand is SP based -OP_SP_ADD = 0x00000000 # operand value is added to the pointer -OP_SP_SUB = 0x00000002 # operand value is substracted from the pointer - -# -# processor_t.flag -# -PR_SEGS = 0x000001 # has segment registers? -PR_USE32 = 0x000002 # supports 32-bit addressing? -PR_DEFSEG32 = 0x000004 # segments are 32-bit by default -PR_RNAMESOK = 0x000008 # allow to user register names for location names -PR_ADJSEGS = 0x000020 # IDA may adjust segments moving their starting/ending addresses. -PR_DEFNUM = 0x0000C0 # default number representation: -PRN_HEX = 0x000000 # hex -PRN_OCT = 0x000040 # octal -PRN_DEC = 0x000080 # decimal -PRN_BIN = 0x0000C0 # binary -PR_WORD_INS = 0x000100 # instruction codes are grouped 2bytes in binrary line prefix -PR_NOCHANGE = 0x000200 # The user can't change segments and code/data attributes (display only) -PR_ASSEMBLE = 0x000400 # Module has a built-in assembler and understands IDP_ASSEMBLE -PR_ALIGN = 0x000800 # All data items should be aligned properly -PR_TYPEINFO = 0x001000 # the processor module supports - # type information callbacks - # ALL OF THEM SHOULD BE IMPLEMENTED! - # (the ones >= decorate_name) -PR_USE64 = 0x002000 # supports 64-bit addressing? -PR_SGROTHER = 0x004000 # the segment registers don't contain - # the segment selectors, something else -PR_STACK_UP = 0x008000 # the stack grows up -PR_BINMEM = 0x010000 # the processor module provides correct - # segmentation for binary files - # (i.e. it creates additional segments) - # The kernel will not ask the user - # to specify the RAM/ROM sizes -PR_SEGTRANS = 0x020000 # the processor module supports - # the segment translation feature - # (it means it calculates the code - # addresses using the codeSeg() function) -PR_CHK_XREF = 0x040000 # don't allow near xrefs between segments - # with different bases -PR_NO_SEGMOVE = 0x080000 # the processor module doesn't support move_segm() - # (i.e. the user can't move segments) -PR_FULL_HIFXP = 0x100000 # REF_VHIGH operand value contains full operand - # not only the high bits. Meaningful if ph.high_fixup_bits -PR_USE_ARG_TYPES = 0x200000 # use ph.use_arg_types callback -PR_SCALE_STKVARS = 0x400000 # use ph.get_stkvar_scale callback -PR_DELAYED = 0x800000 # has delayed jumps and calls -PR_ALIGN_INSN = 0x1000000 # allow ida to create alignment instructions - # arbirtrarily. Since these instructions - # might lead to other wrong instructions - # and spoil the listing, IDA does not create - # them by default anymore -PR_PURGING = 0x2000000 # there are calling conventions which may - # purge bytes from the stack -PR_CNDINSNS = 0x4000000 # has conditional instructions -PR_USE_TBYTE = 0x8000000 # BTMT_SPECFLT means _TBYTE type -PR_DEFSEG64 = 0x10000000 # segments are 64-bit by default - -# ---------------------------------------------------------------------- -OOF_SIGNMASK = 0x0003 # sign symbol (+/-) output: -OOFS_IFSIGN = 0x0000 # output sign if needed -OOFS_NOSIGN = 0x0001 # don't output sign, forbid the user to change the sign -OOFS_NEEDSIGN = 0x0002 # always out sign (+-) -OOF_SIGNED = 0x0004 # output as signed if < 0 -OOF_NUMBER = 0x0008 # always as a number -OOF_WIDTHMASK = 0x0070 # width of value in bits: -OOFW_IMM = 0x0000 # take from x.dtyp -OOFW_8 = 0x0010 # 8 bit width -OOFW_16 = 0x0020 # 16 bit width -OOFW_24 = 0x0030 # 24 bit width -OOFW_32 = 0x0040 # 32 bit width -OOFW_64 = 0x0050 # 32 bit width -OOF_ADDR = 0x0080 # output x.addr, otherwise x.value -OOF_OUTER = 0x0100 # output outer operand -OOF_ZSTROFF = 0x0200 # meaningful only if isStroff(uFlag) - # append a struct field name if - # the field offset is zero? - # if AFL_ZSTROFF is set, then this flag - # is ignored. -OOF_NOBNOT = 0x0400 # prohibit use of binary not -OOF_SPACES = 0x0800 # do not suppress leading spaces - # currently works only for floating point numbers - - -# ---------------------------------------------------------------------- -class insn_t(object): - def __init__(self, noperands = UA_MAXOP): - self.auxpref = 0 - self.cs = 0 - self.ea = 0 - self.flags = 0 - self.insnpref = 0 - self.ip = 0 - self.itype = 0 - self.n = 0 - self.segpref = 0 - self.size = 0 - self.Operands = [] - - # store the number of operands - self.n = noperands - - # create operands - for i in xrange(0, noperands): - op = op_t() - op.n = i - self.Operands.append(op) - setattr(self, 'Op%d' % (i+1), op) - def __getitem__(self, i): - return self.Operands[i] - -# ---------------------------------------------------------------------- -class op_t(object): - def __init__(self): - self.addr = 0 - self.dtyp = 0 - self.flags = 0 - self.n = 0 - self.offb = 0 - self.offo = 0 - self.reg = 0 - self.specval = 0 - self.specflag1 = 0 - self.specflag2 = 0 - self.specflag3 = 0 - self.specflag4 = 0 - self.type = 0 - self.value = 0 - - # make sure reg and phrase have the same value - def __setattr__(self, name, value): - if name == 'reg' or name == 'phrase': - object.__setattr__(self, 'reg', value) - object.__setattr__(self, 'phrase', value) - else: - object.__setattr__(self, name, value) +# ---------------------------------------------------------------------- +# +# Misc constants +# +UA_MAXOP = 6 + +# ---------------------------------------------------------------------- +# instruc_t related constants + +# +# instruc_t.feature +# +CF_STOP = 0x00001 # Instruction doesn't pass execution to the next instruction +CF_CALL = 0x00002 # CALL instruction (should make a procedure here) +CF_CHG1 = 0x00004 # The instruction modifies the first operand +CF_CHG2 = 0x00008 # The instruction modifies the second operand +CF_CHG3 = 0x00010 # The instruction modifies the third operand +CF_CHG4 = 0x00020 # The instruction modifies 4 operand +CF_CHG5 = 0x00040 # The instruction modifies 5 operand +CF_CHG6 = 0x00080 # The instruction modifies 6 operand +CF_USE1 = 0x00100 # The instruction uses value of the first operand +CF_USE2 = 0x00200 # The instruction uses value of the second operand +CF_USE3 = 0x00400 # The instruction uses value of the third operand +CF_USE4 = 0x00800 # The instruction uses value of the 4 operand +CF_USE5 = 0x01000 # The instruction uses value of the 5 operand +CF_USE6 = 0x02000 # The instruction uses value of the 6 operand +CF_JUMP = 0x04000 # The instruction passes execution using indirect jump or call (thus needs additional analysis) +CF_SHFT = 0x08000 # Bit-shift instruction (shl,shr...) +CF_HLL = 0x10000 # Instruction may be present in a high level language function. + +# ---------------------------------------------------------------------- +# op_t related constants + +# +# op_t.type +# Description Data field +o_void = 0 # No Operand ---------- +o_reg = 1 # General Register (al,ax,es,ds...) reg +o_mem = 2 # Direct Memory Reference (DATA) addr +o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase +o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr +o_imm = 5 # Immediate Value value +o_far = 6 # Immediate Far Address (CODE) addr +o_near = 7 # Immediate Near Address (CODE) addr +o_idpspec0 = 8 # Processor specific type +o_idpspec1 = 9 # Processor specific type +o_idpspec2 = 10 # Processor specific type +o_idpspec3 = 11 # Processor specific type +o_idpspec4 = 12 # Processor specific type +o_idpspec5 = 13 # Processor specific type + # There can be more processor specific types + +# +# op_t.dtyp +# +dt_byte = 0 # 8 bit +dt_word = 1 # 16 bit +dt_dword = 2 # 32 bit +dt_float = 3 # 4 byte +dt_double = 4 # 8 byte +dt_tbyte = 5 # variable size (ph.tbyte_size) +dt_packreal = 6 # packed real format for mc68040 +dt_qword = 7 # 64 bit +dt_byte16 = 8 # 128 bit +dt_code = 9 # ptr to code (not used?) +dt_void = 10 # none +dt_fword = 11 # 48 bit +dt_bitfild = 12 # bit field (mc680x0) +dt_string = 13 # pointer to asciiz string +dt_unicode = 14 # pointer to unicode string +dt_3byte = 15 # 3-byte data +dt_ldbl = 16 # long double (which may be different from tbyte) +dt_byte32 = 17 # 256 bit +dt_byte64 = 18 # 512 bit + +# +# op_t.flags +# +OF_NO_BASE_DISP = 0x80 # o_displ: base displacement doesn't exist meaningful only for o_displ type if set, base displacement (x.addr) doesn't exist. +OF_OUTER_DISP = 0x40 # o_displ: outer displacement exists meaningful only for o_displ type if set, outer displacement (x.value) exists. +PACK_FORM_DEF = 0x20 # !o_reg + dt_packreal: packed factor defined +OF_NUMBER = 0x10 # can be output as number only if set, the operand can be converted to a number only +OF_SHOW = 0x08 # should the operand be displayed? if clear, the operand is hidden and should not be displayed + +# +# insn_t.flags +# +INSN_MACRO = 0x01 # macro instruction +INSN_MODMAC = 0x02 # macros: may modify the database to make room for the macro insn + +# ---------------------------------------------------------------------- +# asm_t related constants + +# +# asm_t.flag +# +AS_OFFST = 0x00000001 # offsets are 'offset xxx' ? +AS_COLON = 0x00000002 # create colons after data names ? +AS_UDATA = 0x00000004 # can use '?' in data directives + +AS_2CHRE = 0x00000008 # double char constants are: "xy +AS_NCHRE = 0x00000010 # char constants are: 'x +AS_N2CHR = 0x00000020 # can't have 2 byte char consts + + # ASCII directives: +AS_1TEXT = 0x00000040 # 1 text per line, no bytes +AS_NHIAS = 0x00000080 # no characters with high bit +AS_NCMAS = 0x00000100 # no commas in ascii directives + +AS_HEXFM = 0x00000E00 # format of hex numbers: +ASH_HEXF0 = 0x00000000 # 34h +ASH_HEXF1 = 0x00000200 # h'34 +ASH_HEXF2 = 0x00000400 # 34 +ASH_HEXF3 = 0x00000600 # 0x34 +ASH_HEXF4 = 0x00000800 # $34 +ASH_HEXF5 = 0x00000A00 # <^R > (radix) +AS_DECFM = 0x00003000 # format of dec numbers: +ASD_DECF0 = 0x00000000 # 34 +ASD_DECF1 = 0x00001000 # #34 +ASD_DECF2 = 0x00002000 # 34. +ASD_DECF3 = 0x00003000 # .34 +AS_OCTFM = 0x0001C000 # format of octal numbers: +ASO_OCTF0 = 0x00000000 # 123o +ASO_OCTF1 = 0x00004000 # 0123 +ASO_OCTF2 = 0x00008000 # 123 +ASO_OCTF3 = 0x0000C000 # @123 +ASO_OCTF4 = 0x00010000 # o'123 +ASO_OCTF5 = 0x00014000 # 123q +ASO_OCTF6 = 0x00018000 # ~123 +AS_BINFM = 0x000E0000 # format of binary numbers: +ASB_BINF0 = 0x00000000 # 010101b +ASB_BINF1 = 0x00020000 # ^B010101 +ASB_BINF2 = 0x00040000 # %010101 +ASB_BINF3 = 0x00060000 # 0b1010101 +ASB_BINF4 = 0x00080000 # b'1010101 +ASB_BINF5 = 0x000A0000 # b'1010101' + +AS_UNEQU = 0x00100000 # replace undefined data items + # with EQU (for ANTA's A80) +AS_ONEDUP = 0x00200000 # One array definition per line +AS_NOXRF = 0x00400000 # Disable xrefs during the output file generation +AS_XTRNTYPE = 0x00800000 # Assembler understands type of extrn + # symbols as ":type" suffix +AS_RELSUP = 0x01000000 # Checkarg: 'and','or','xor' operations + # with addresses are possible +AS_LALIGN = 0x02000000 # Labels at "align" keyword + # are supported. +AS_NOCODECLN = 0x04000000 # don't create colons after code names +AS_NOTAB = 0x08000000 # Disable tabulation symbols during the output file generation +AS_NOSPACE = 0x10000000 # No spaces in expressions +AS_ALIGN2 = 0x20000000 # .align directive expects an exponent rather than a power of 2 + # (.align 5 means to align at 32byte boundary) +AS_ASCIIC = 0x40000000 # ascii directive accepts C-like + # escape sequences (\n,\x01 and similar) +AS_ASCIIZ = 0x80000000 # ascii directive inserts implicit + # zero byte at the end + +# ---------------------------------------------------------------------- +# processor_t related constants + +IDP_INTERFACE_VERSION = 76 +CUSTOM_CMD_ITYPE = 0x8000 +REG_SPOIL = 0x80000000 + +REAL_ERROR_FORMAT = -1 # not supported format for current .idp +REAL_ERROR_RANGE = -2 # number too big (small) for store (mem NOT modifyed) +REAL_ERROR_BADDATA = -3 # illegal real data for load (IEEE data not filled) + +# +# Check whether the operand is relative to stack pointer or frame pointer. +# This function is used to determine how to output a stack variable +# This function may be absent. If it is absent, then all operands +# are sp based by default. +# Define this function only if some stack references use frame pointer +# instead of stack pointer. +# returns flags: +OP_FP_BASED = 0x00000000 # operand is FP based +OP_SP_BASED = 0x00000001 # operand is SP based +OP_SP_ADD = 0x00000000 # operand value is added to the pointer +OP_SP_SUB = 0x00000002 # operand value is substracted from the pointer + +# +# processor_t.flag +# +PR_SEGS = 0x000001 # has segment registers? +PR_USE32 = 0x000002 # supports 32-bit addressing? +PR_DEFSEG32 = 0x000004 # segments are 32-bit by default +PR_RNAMESOK = 0x000008 # allow to user register names for location names +PR_ADJSEGS = 0x000020 # IDA may adjust segments moving their starting/ending addresses. +PR_DEFNUM = 0x0000C0 # default number representation: +PRN_HEX = 0x000000 # hex +PRN_OCT = 0x000040 # octal +PRN_DEC = 0x000080 # decimal +PRN_BIN = 0x0000C0 # binary +PR_WORD_INS = 0x000100 # instruction codes are grouped 2bytes in binrary line prefix +PR_NOCHANGE = 0x000200 # The user can't change segments and code/data attributes (display only) +PR_ASSEMBLE = 0x000400 # Module has a built-in assembler and understands IDP_ASSEMBLE +PR_ALIGN = 0x000800 # All data items should be aligned properly +PR_TYPEINFO = 0x001000 # the processor module supports + # type information callbacks + # ALL OF THEM SHOULD BE IMPLEMENTED! + # (the ones >= decorate_name) +PR_USE64 = 0x002000 # supports 64-bit addressing? +PR_SGROTHER = 0x004000 # the segment registers don't contain + # the segment selectors, something else +PR_STACK_UP = 0x008000 # the stack grows up +PR_BINMEM = 0x010000 # the processor module provides correct + # segmentation for binary files + # (i.e. it creates additional segments) + # The kernel will not ask the user + # to specify the RAM/ROM sizes +PR_SEGTRANS = 0x020000 # the processor module supports + # the segment translation feature + # (it means it calculates the code + # addresses using the codeSeg() function) +PR_CHK_XREF = 0x040000 # don't allow near xrefs between segments + # with different bases +PR_NO_SEGMOVE = 0x080000 # the processor module doesn't support move_segm() + # (i.e. the user can't move segments) +PR_FULL_HIFXP = 0x100000 # REF_VHIGH operand value contains full operand + # not only the high bits. Meaningful if ph.high_fixup_bits +PR_USE_ARG_TYPES = 0x200000 # use ph.use_arg_types callback +PR_SCALE_STKVARS = 0x400000 # use ph.get_stkvar_scale callback +PR_DELAYED = 0x800000 # has delayed jumps and calls +PR_ALIGN_INSN = 0x1000000 # allow ida to create alignment instructions + # arbirtrarily. Since these instructions + # might lead to other wrong instructions + # and spoil the listing, IDA does not create + # them by default anymore +PR_PURGING = 0x2000000 # there are calling conventions which may + # purge bytes from the stack +PR_CNDINSNS = 0x4000000 # has conditional instructions +PR_USE_TBYTE = 0x8000000 # BTMT_SPECFLT means _TBYTE type +PR_DEFSEG64 = 0x10000000 # segments are 64-bit by default + +# ---------------------------------------------------------------------- +OOF_SIGNMASK = 0x0003 # sign symbol (+/-) output: +OOFS_IFSIGN = 0x0000 # output sign if needed +OOFS_NOSIGN = 0x0001 # don't output sign, forbid the user to change the sign +OOFS_NEEDSIGN = 0x0002 # always out sign (+-) +OOF_SIGNED = 0x0004 # output as signed if < 0 +OOF_NUMBER = 0x0008 # always as a number +OOF_WIDTHMASK = 0x0070 # width of value in bits: +OOFW_IMM = 0x0000 # take from x.dtyp +OOFW_8 = 0x0010 # 8 bit width +OOFW_16 = 0x0020 # 16 bit width +OOFW_24 = 0x0030 # 24 bit width +OOFW_32 = 0x0040 # 32 bit width +OOFW_64 = 0x0050 # 32 bit width +OOF_ADDR = 0x0080 # output x.addr, otherwise x.value +OOF_OUTER = 0x0100 # output outer operand +OOF_ZSTROFF = 0x0200 # meaningful only if isStroff(uFlag) + # append a struct field name if + # the field offset is zero? + # if AFL_ZSTROFF is set, then this flag + # is ignored. +OOF_NOBNOT = 0x0400 # prohibit use of binary not +OOF_SPACES = 0x0800 # do not suppress leading spaces + # currently works only for floating point numbers + + +# ---------------------------------------------------------------------- +class insn_t(object): + def __init__(self, noperands = UA_MAXOP): + self.auxpref = 0 + self.cs = 0 + self.ea = 0 + self.flags = 0 + self.insnpref = 0 + self.ip = 0 + self.itype = 0 + self.n = 0 + self.segpref = 0 + self.size = 0 + self.Operands = [] + + # store the number of operands + self.n = noperands + + # create operands + for i in xrange(0, noperands): + op = op_t() + op.n = i + self.Operands.append(op) + setattr(self, 'Op%d' % (i+1), op) + def __getitem__(self, i): + return self.Operands[i] + +# ---------------------------------------------------------------------- +class op_t(object): + def __init__(self): + self.addr = 0 + self.dtyp = 0 + self.flags = 0 + self.n = 0 + self.offb = 0 + self.offo = 0 + self.reg = 0 + self.specval = 0 + self.specflag1 = 0 + self.specflag2 = 0 + self.specflag3 = 0 + self.specflag4 = 0 + self.type = 0 + self.value = 0 + + # make sure reg and phrase have the same value + def __setattr__(self, name, value): + if name == 'reg' or name == 'phrase': + object.__setattr__(self, 'reg', value) + object.__setattr__(self, 'phrase', value) + else: + object.__setattr__(self, name, value) diff --git a/pywraps/swig_stub.cpp b/pywraps/swig_stub.cpp index 28ee2dcf..343a24e0 100644 --- a/pywraps/swig_stub.cpp +++ b/pywraps/swig_stub.cpp @@ -1,6 +1,6 @@ -#include "swig_stub.h" - -PyObject *SWIG_NewPointerObj(void *ptr, void *type, int flags) -{ - return PyCObject_FromVoidPtr(ptr, NULL); -} +#include "swig_stub.h" + +PyObject *SWIG_NewPointerObj(void *ptr, void *type, int flags) +{ + return PyCObject_FromVoidPtr(ptr, NULL); +} diff --git a/pywraps/swig_stub.h b/pywraps/swig_stub.h index 307b8bb6..0880d150 100644 --- a/pywraps/swig_stub.h +++ b/pywraps/swig_stub.h @@ -1,27 +1,27 @@ -#ifndef __SWIG_STUB__ -#define __SWIG_STUB__ - -#include - -#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) - -PyObject *SWIG_NewPointerObj(void *ptr, void *type, int flags); - -namespace Swig -{ - class DirectorException - { - public: - const char *getMessage() const - { - return "NULL"; - } - }; -} - -#define SWIG_RUNTIME_VERSION "4" - -// Some fake SWIG types -#define SWIGTYPE_p_member_t NULL - +#ifndef __SWIG_STUB__ +#define __SWIG_STUB__ + +#include + +#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) + +PyObject *SWIG_NewPointerObj(void *ptr, void *type, int flags); + +namespace Swig +{ + class DirectorException + { + public: + const char *getMessage() const + { + return "NULL"; + } + }; +} + +#define SWIG_RUNTIME_VERSION "4" + +// Some fake SWIG types +#define SWIGTYPE_p_member_t NULL + #endif \ No newline at end of file diff --git a/swig/bytes.i b/swig/bytes.i index 8ed03f2b..522d552a 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -1,1225 +1,1225 @@ -// Make get_any_cmt() work -%apply unsigned char *OUTPUT { color_t *cmttype }; - -// For get_enum_id() -%apply unsigned char *OUTPUT { uchar *serial }; - -// get_[first|last]_serial_enum_member() won't take serials as input; it'll be present as output -%apply unsigned char *OUTPUT { uchar *out_serial }; -// get_[next|prev]_serial_enum_member() take serials as input, and have the result present as output -%apply unsigned char *INOUT { uchar *in_out_serial }; - -// Unexported and kernel-only declarations -%ignore FlagsEnable; -%ignore FlagsDisable; -%ignore testf_t; -%ignore nextthat; -%ignore prevthat; -%ignore adjust_visea; -%ignore prev_visea; -%ignore next_visea; -%ignore visit_patched_bytes; -%ignore is_first_visea; -%ignore is_last_visea; -%ignore is_visible_finally; -%ignore invalidate_visea_cache; -%ignore fluFlags; -%ignore setFlbits; -%ignore clrFlbits; -%ignore get_8bit; -%ignore get_ascii_char; -%ignore del_opinfo; -%ignore del_one_opinfo; -%ignore doCode; -%ignore get_repeatable_cmt; -%ignore get_any_indented_cmt; -%ignore del_code_comments; -%ignore doFlow; -%ignore noFlow; -%ignore doRef; -%ignore noRef; -%ignore coagulate; -%ignore coagulate_dref; -%ignore init_hidden_areas; -%ignore save_hidden_areas; -%ignore term_hidden_areas; -%ignore check_move_args; -%ignore movechunk; -%ignore lock_dbgmem_config; -%ignore unlock_dbgmem_config; -%ignore set_op_type_no_event; -%ignore shuffle_tribytes; -%ignore set_enum_id; -%ignore validate_tofs; -%ignore set_flags_nomark; -%ignore set_flbits_nomark; -%ignore clr_flbits_nomark; -%ignore ida_vpagesize; -%ignore ida_vpages; -%ignore ida_npagesize; -%ignore ida_npages; -%ignore fpnum_digits; -%ignore fpnum_length; -%ignore FlagsInit; -%ignore FlagsTerm; -%ignore FlagsReset; -%ignore init_flags; -%ignore term_flags; -%ignore reset_flags; -%ignore flush_flags; -%ignore get_flags_linput; -%ignore data_type_t; -%ignore data_format_t; -%ignore get_custom_data_type; -%ignore get_custom_data_format; -%ignore unregister_custom_data_format; -%ignore register_custom_data_format; -%ignore unregister_custom_data_type; -%ignore register_custom_data_type; -%ignore get_many_bytes; -%ignore get_ascii_contents; -%ignore get_ascii_contents2; - -// TODO: This could be fixed (if needed) -%ignore set_dbgmem_source; - -%include "bytes.hpp" - -%clear(void *buf, ssize_t size); - -%clear(const void *buf, size_t size); -%clear(void *buf, ssize_t size); -%clear(opinfo_t *); - -%rename (visit_patched_bytes) py_visit_patched_bytes; -%rename (nextthat) py_nextthat; -%rename (prevthat) py_prevthat; -%rename (get_custom_data_type) py_get_custom_data_type; -%rename (get_custom_data_format) py_get_custom_data_format; -%rename (unregister_custom_data_format) py_unregister_custom_data_format; -%rename (register_custom_data_format) py_register_custom_data_format; -%rename (unregister_custom_data_type) py_unregister_custom_data_type; -%rename (register_custom_data_type) py_register_custom_data_type; -%rename (get_many_bytes) py_get_many_bytes; -%rename (get_ascii_contents) py_get_ascii_contents; -%rename (get_ascii_contents2) py_get_ascii_contents2; -%{ -// -//------------------------------------------------------------------------ -static bool idaapi py_testf_cb(flags_t flags, void *ud) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_flags(PyLong_FromUnsignedLong(flags)); - newref_t result(PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags.o, NULL)); - return result != NULL && PyObject_IsTrue(result.o); -} - -//------------------------------------------------------------------------ -// Wraps the (next|prev)that() -static ea_t py_npthat(ea_t ea, ea_t bound, PyObject *py_callable, bool next) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCallable_Check(py_callable) ) - return BADADDR; - else - return (next ? nextthat : prevthat)(ea, bound, py_testf_cb, py_callable); -} - -//--------------------------------------------------------------------------- -static int idaapi py_visit_patched_bytes_cb( - ea_t ea, - int32 fpos, - uint32 o, - uint32 v, - void *ud) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallFunction( - (PyObject *)ud, - PY_FMT64 "iII", - pyul_t(ea), - fpos, - o, - v)); - PyW_ShowCbErr("visit_patched_bytes"); - return (py_result != NULL && PyInt_Check(py_result.o)) ? PyInt_AsLong(py_result.o) : 0; -} - - - -//------------------------------------------------------------------------ -class py_custom_data_type_t -{ - data_type_t dt; - qstring dt_name, dt_menu_name, dt_hotkey, dt_asm_keyword; - int dtid; // The data format id - PyObject *py_self; // Associated Python object - - // may create data? NULL means always may - static bool idaapi s_may_create_at( - void *ud, // user-defined data - ea_t ea, // address of the future item - size_t nbytes) // size of the future item - { - py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - - PYW_GIL_GET; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_MAY_CREATE_AT, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(nbytes))); - - PyW_ShowCbErr(S_MAY_CREATE_AT); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - // !=NULL means variable size datatype - static asize_t idaapi s_calc_item_size( - // This function is used to determine - // size of the (possible) item at 'ea' - void *ud, // user-defined data - ea_t ea, // address of the item - asize_t maxsize) // maximal size of the item - { - PYW_GIL_GET; - // Returns: 0-no such item can be created/displayed - // this callback is required only for varsize datatypes - py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_CALC_ITEM_SIZE, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(maxsize))); - - if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) - return 0; - - uint64 num = 0; - PyW_GetNumber(py_result.o, &num); - return asize_t(num); - } - -public: - const char *get_name() const - { - return dt_name.c_str(); - } - - py_custom_data_type_t() - { - dtid = -1; - py_self = NULL; - } - - int register_dt(PyObject *py_obj) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Already registered? - if ( dtid >= 0 ) - return dtid; - - memset(&dt, 0, sizeof(dt)); - dt.cbsize = sizeof(dt); - dt.ud = this; - - do - { - ref_t py_attr; - - // name - if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) - break; - - dt.name = dt_name.c_str(); - - // menu_name (optional) - if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &dt_menu_name) ) - dt.menu_name = dt_menu_name.c_str(); - - // asm_keyword (optional) - if ( PyW_GetStringAttr(py_obj, S_ASM_KEYWORD, &dt_asm_keyword) ) - dt.asm_keyword = dt_asm_keyword.c_str(); - - // hotkey (optional) - if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &dt_hotkey) ) - dt.hotkey = dt_hotkey.c_str(); - - // value_size - py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - dt.value_size = PyInt_AsLong(py_attr.o); - py_attr = ref_t(); - - // props - py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - dt.props = PyInt_AsLong(py_attr.o); - py_attr = ref_t(); - - // may_create_at - py_attr = PyW_TryGetAttrString(py_obj, S_MAY_CREATE_AT); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - dt.may_create_at = s_may_create_at; - py_attr = ref_t(); - - // calc_item_size - py_attr = PyW_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - dt.calc_item_size = s_calc_item_size; - py_attr = ref_t(); - - // Now try to register - dtid = register_custom_data_type(&dt); - if ( dtid < 0 ) - break; - - // Hold reference to the PyObject - Py_INCREF(py_obj); - py_self = py_obj; - - py_attr = newref_t(PyInt_FromLong(dtid)); - PyObject_SetAttrString(py_obj, S_ID, py_attr.o); - } while ( false ); - return dtid; - } - - bool unregister_dt() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( dtid < 0 ) - return true; - - if ( !unregister_custom_data_type(dtid) ) - return false; - - // Release reference of Python object - Py_XDECREF(py_self); - py_self = NULL; - dtid = -1; - return true; - } - - ~py_custom_data_type_t() - { - unregister_dt(); - } -}; -typedef std::map py_custom_data_type_map_t; -static py_custom_data_type_map_t py_dt_map; - -//------------------------------------------------------------------------ -class py_custom_data_format_t -{ -private: - data_format_t df; - int dfid; - PyObject *py_self; - qstring df_name, df_menu_name, df_hotkey; - - static bool idaapi s_print( // convert to colored string - void *ud, // user-defined data - qstring *out, // output buffer. may be NULL - const void *value, // value to print. may not be NULL - asize_t size, // size of value in bytes - ea_t current_ea, // current address (BADADDR if unknown) - int operand_num, // current operand number - int dtid) // custom data type id - { - PYW_GIL_GET; - - // Build a string from the buffer - newref_t py_value(PyString_FromStringAndSize( - (const char *)value, - Py_ssize_t(size))); - if ( py_value == NULL ) - return false; - - py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - newref_t py_result(PyObject_CallMethod( - _this->py_self, - (char *)S_PRINTF, - "O" PY_FMT64 "ii", - py_value.o, - pyul_t(current_ea), - operand_num, - dtid)); - - // Error while calling the function? - if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL ) - return false; - - bool ok = false; - if ( PyString_Check(py_result.o) ) - { - Py_ssize_t len; - char *buf; - if ( out != NULL && PyString_AsStringAndSize(py_result.o, &buf, &len) != -1 ) - { - out->qclear(); - out->append(buf, len); - } - ok = true; - } - return ok; - } - - static bool idaapi s_scan( // convert from uncolored string - void *ud, // user-defined data - bytevec_t *value, // output buffer. may be NULL - const char *input, // input string. may not be NULL - ea_t current_ea, // current address (BADADDR if unknown) - int operand_num, // current operand number (-1 if unknown) - qstring *errstr) // buffer for error message - { - PYW_GIL_GET; - - py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_SCAN, - "s" PY_FMT64, - input, - pyul_t(current_ea), - operand_num)); - - // Error while calling the function? - if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL) - return false; - - bool ok = false; - do - { - // We expect a tuple(bool, string|None) - if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) - break; - - borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); - borref_t py_val(PyTuple_GetItem(py_result.o, 1)); - - // Get return code from Python - ok = PyObject_IsTrue(py_bool.o); - - // We expect None or the value (depending on probe) - if ( ok ) - { - // Probe-only? Then okay, no need to extract the 'value' - if ( value == NULL ) - break; - - Py_ssize_t len; - char *buf; - if ( PyString_AsStringAndSize(py_val.o, &buf, &len) != -1 ) - { - value->qclear(); - value->append(buf, len); - } - } - // An error occured? - else - { - // Make sure the user returned (False, String) - if ( py_bool.o != Py_False || !PyString_Check(py_val.o) ) - { - *errstr = "Invalid return value returned from the Python callback!"; - break; - } - // Get the error message - *errstr = PyString_AsString(py_val.o); - } - } while ( false ); - return ok; - } - - static void idaapi s_analyze( // analyze custom data format occurrence - void *ud, // user-defined data - ea_t current_ea, // current address (BADADDR if unknown) - int operand_num) // current operand number - // this callback can be used to create - // xrefs from the current item. - // this callback may be missing. - { - PYW_GIL_GET; - - py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - newref_t py_result( - PyObject_CallMethod( - _this->py_self, - (char *)S_ANALYZE, - PY_FMT64 "i", - pyul_t(current_ea), - operand_num)); - - PyW_ShowCbErr(S_ANALYZE); - } -public: - py_custom_data_format_t() - { - dfid = -1; - py_self = NULL; - } - - const char *get_name() const - { - return df_name.c_str(); - } - - int register_df(int dtid, PyObject *py_obj) - { - // Already registered? - if ( dfid >= 0 ) - return dfid; - - memset(&df, 0, sizeof(df)); - df.cbsize = sizeof(df); - df.ud = this; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - ref_t py_attr; - - // name - if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) - break; - df.name = df_name.c_str(); - - // menu_name (optional) - if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &df_menu_name) ) - df.menu_name = df_menu_name.c_str(); - - // props - py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - df.props = PyInt_AsLong(py_attr.o); - - // hotkey - if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) - df.hotkey = df_hotkey.c_str(); - - // value_size - py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - df.value_size = PyInt_AsLong(py_attr.o); - - // text_width - py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); - if ( py_attr != NULL && PyInt_Check(py_attr.o) ) - df.text_width = PyInt_AsLong(py_attr.o); - - // print cb - py_attr = PyW_TryGetAttrString(py_obj, S_PRINTF); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - df.print = s_print; - - // scan cb - py_attr = PyW_TryGetAttrString(py_obj, S_SCAN); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - df.scan = s_scan; - - // analyze cb - py_attr = PyW_TryGetAttrString(py_obj, S_ANALYZE); - if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) - df.analyze = s_analyze; - - // Now try to register - dfid = register_custom_data_format(dtid, &df); - if ( dfid < 0 ) - break; - - // Hold reference to the PyObject - Py_INCREF(py_obj); - py_self = py_obj; - - // Update the format ID - py_attr = newref_t(PyInt_FromLong(dfid)); - PyObject_SetAttrString(py_obj, S_ID, py_attr.o); - } while ( false ); - return dfid; - } - - bool unregister_df(int dtid) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Never registered? - if ( dfid < 0 ) - return true; - - if ( !unregister_custom_data_format(dtid, dfid) ) - return false; - - // Release reference of Python object - Py_XDECREF(py_self); - py_self = NULL; - dfid = -1; - return true; - } - - ~py_custom_data_format_t() - { - } -}; - -//------------------------------------------------------------------------ -// Helper class to bind pairs to py_custom_data_format_t -class py_custom_data_format_list_t -{ - struct py_custom_data_format_entry_t - { - int dtid; - int dfid; - py_custom_data_format_t *df; - }; - typedef qvector ENTRY; - ENTRY entries; -public: - typedef ENTRY::iterator POS; - void add(int dtid, int dfid, py_custom_data_format_t *df) - { - py_custom_data_format_entry_t &e = entries.push_back(); - e.dtid = dtid; - e.dfid = dfid; - e.df = df; - } - py_custom_data_format_t *find(int dtid, int dfid, POS *loc = NULL) - { - for ( POS it=entries.begin(), it_end = entries.end(); it!=it_end; ++it ) - { - if ( it->dfid == dfid && it->dtid == dtid ) - { - if ( loc != NULL ) - *loc = it; - return it->df; - } - } - return NULL; - } - void erase(POS &pos) - { - entries.erase(pos); - } -}; -static py_custom_data_format_list_t py_df_list; - -//------------------------------------------------------------------------ -static PyObject *py_data_type_to_py_dict(const data_type_t *dt) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", - S_VALUE_SIZE, pyul_t(dt->value_size), - S_PROPS, dt->props, - S_CBSIZE, dt->cbsize, - S_NAME, dt->name == NULL ? "" : dt->name, - S_MENU_NAME, dt->menu_name == NULL ? "" : dt->menu_name, - S_HOTKEY, dt->hotkey == NULL ? "" : dt->hotkey, - S_ASM_KEYWORD, dt->asm_keyword == NULL ? "" : dt->asm_keyword); -} - -//------------------------------------------------------------------------ -static PyObject *py_data_format_to_py_dict(const data_format_t *df) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", - S_PROPS, df->props, - S_CBSIZE, df->cbsize, - S_TEXT_WIDTH, df->text_width, - S_VALUE_SIZE, pyul_t(df->value_size), - S_NAME, df->name == NULL ? "" : df->name, - S_MENU_NAME, df->menu_name == NULL ? "" : df->menu_name, - S_HOTKEY, df->hotkey == NULL ? "" : df->hotkey); -} -// -%} - -%inline %{ -// - -//------------------------------------------------------------------------ -/* -# -def visit_patched_bytes(ea1, ea2, callable): - """ - Enumerates patched bytes in the given range and invokes a callable - @param ea1: start address - @param ea2: end address - @param callable: a Python callable with the following prototype: - callable(ea, fpos, org_val, patch_val). - If the callable returns non-zero then that value will be - returned to the caller and the enumeration will be - interrupted. - @return: Zero if the enumeration was successful or the return - value of the callback if enumeration was interrupted. - """ - pass -# -*/ -static int py_visit_patched_bytes(ea_t ea1, ea_t ea2, PyObject *py_callable) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCallable_Check(py_callable) ) - return 0; - else - return visit_patched_bytes(ea1, ea2, py_visit_patched_bytes_cb, py_callable); -} - -//------------------------------------------------------------------------ -/* -# -def nextthat(ea, maxea, callable): - """ - Find next address with a flag satisfying the function 'testf'. - Start searching from address 'ea'+1 and inspect bytes up to 'maxea'. - maxea is not included in the search range. - - @param callable: a Python callable with the following prototype: - callable(flags). Return True to stop enumeration. - @return: the found address or BADADDR. - """ - pass -# -*/ -static ea_t py_nextthat(ea_t ea, ea_t maxea, PyObject *callable) -{ - return py_npthat(ea, maxea, callable, true); -} - -//--------------------------------------------------------------------------- -static ea_t py_prevthat(ea_t ea, ea_t minea, PyObject *callable) -{ - return py_npthat(ea, minea, callable, false); -} - -//------------------------------------------------------------------------ -/* -# -def get_many_bytes(ea, size): - """ - Get the specified number of bytes of the program into the buffer. - @param ea: program address - @param size: number of bytes to return - @return: None or the string buffer - """ - pass -# -*/ -static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - if ( size <= 0 ) - break; - - // Allocate memory via Python - newref_t py_buf(PyString_FromStringAndSize(NULL, Py_ssize_t(size))); - if ( py_buf == NULL ) - break; - - // Read bytes - if ( !get_many_bytes(ea, PyString_AsString(py_buf.o), size) ) - Py_RETURN_NONE; - - py_buf.incref(); - return py_buf.o; - } while ( false ); - Py_RETURN_NONE; -} - -//--------------------------------------------------------------------------- -/* -# -# Conversion options for get_ascii_contents2(): -ACFOPT_ASCII = 0x00000000 # convert Unicode strings to ASCII -ACFOPT_UTF16 = 0x00000001 # return UTF-16 (aka wide-char) array for Unicode strings - # ignored for non-Unicode strings -ACFOPT_UTF8 = 0x00000002 # convert Unicode strings to UTF-8 - # ignored for non-Unicode strings -ACFOPT_CONVMASK = 0x0000000F -ACFOPT_ESCAPE = 0x00000010 # for ACFOPT_ASCII, convert non-printable - # characters to C escapes (\n, \xNN, \uNNNN) - -def get_ascii_contents2(ea, len, type, flags = ACFOPT_ASCII): - """ - Get bytes contents at location, possibly converted. - It works even if the string has not been created in the database yet. - - Note that this will always return a simple string of bytes - (i.e., a 'str' instance), and not a string of unicode characters. - - If you want auto-conversion to unicode strings (that is: real strings), - you should probably be using the idautils.Strings class. - - @param ea: linear address of the string - @param len: length of the string in bytes (including terminating 0) - @param type: type of the string. Represents both the character encoding, - and the 'type' of string at the given location. - @param flags: combination of ACFOPT_..., to perform output conversion. - @return: a bytes-filled str object. - """ - pass -# -*/ -static PyObject *py_get_ascii_contents2( - ea_t ea, - size_t len, - int32 type, - int flags = ACFOPT_ASCII) -{ - char *buf = (char *)qalloc(len+1); - if ( buf == NULL ) - return NULL; - - size_t used_size; - if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size, flags) ) - { - qfree(buf); - Py_RETURN_NONE; - } - if ( type == ASCSTR_C && used_size > 0 && buf[used_size-1] == '\0' ) - used_size--; - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_buf(PyString_FromStringAndSize((const char *)buf, used_size)); - qfree(buf); - py_buf.incref(); - return py_buf.o; -} -//--------------------------------------------------------------------------- -/* -# -def get_ascii_contents(ea, len, type): - """ - Get contents of ascii string - This function returns the displayed part of the string - It works even if the string has not been created in the database yet. - - @param ea: linear address of the string - @param len: length of the string in bytes (including terminating 0) - @param type: type of the string - @return: string contents (not including terminating 0) or None - """ - pass -# -*/ -static PyObject *py_get_ascii_contents( - ea_t ea, - size_t len, - int32 type) -{ - return py_get_ascii_contents2(ea, len, type); -} - - - -//------------------------------------------------------------------------ -/* -# -def register_custom_data_type(dt): - """ - Registers a custom data type. - @param dt: an instance of the data_type_t class - @return: - < 0 if failed to register - > 0 data type id - """ - pass -# -*/ -// Given a py.data_format_t object, this function will register a datatype -static int py_register_custom_data_type(PyObject *py_dt) -{ - py_custom_data_type_t *inst = new py_custom_data_type_t(); - int r = inst->register_dt(py_dt); - if ( r < 0 ) - { - delete inst; - return r; - } - // Insert the instance to the map - py_dt_map[r] = inst; - return r; -} - -//------------------------------------------------------------------------ -/* -# -def unregister_custom_data_type(dtid): - """ - Unregisters a custom data type. - @param dtid: the data type id - @return: Boolean - """ - pass -# -*/ -static bool py_unregister_custom_data_type(int dtid) -{ - py_custom_data_type_map_t::iterator it = py_dt_map.find(dtid); - - // Maybe the user is trying to unregister a C api dt? - if ( it == py_dt_map.end() ) - return unregister_custom_data_type(dtid); - - py_custom_data_type_t *inst = it->second; - bool ok = inst->unregister_dt(); - - // Perhaps it was automatically unregistered because the idb was close? - if ( !ok ) - { - // Is this type still registered with IDA? - // If not found then mark the context for deletion - ok = find_custom_data_type(inst->get_name()) < 0; - } - - if ( ok ) - { - py_dt_map.erase(it); - delete inst; - } - return ok; -} - -//------------------------------------------------------------------------ -/* -# -def register_custom_data_format(dtid, df): - """ - Registers a custom data format with a given data type. - @param dtid: data type id - @param df: an instance of data_format_t - @return: - < 0 if failed to register - > 0 data format id - """ - pass -# -*/ -static int py_register_custom_data_format(int dtid, PyObject *py_df) -{ - py_custom_data_format_t *inst = new py_custom_data_format_t(); - int r = inst->register_df(dtid, py_df); - if ( r < 0 ) - { - delete inst; - return r; - } - // Insert the instance - py_df_list.add(dtid, r, inst); - return r; -} - -//------------------------------------------------------------------------ -/* -# -def unregister_custom_data_format(dtid, dfid): - """ - Unregisters a custom data format - @param dtid: data type id - @param dfid: data format id - @return: Boolean - """ - pass -# -*/ -static bool py_unregister_custom_data_format(int dtid, int dfid) -{ - py_custom_data_format_list_t::POS pos; - py_custom_data_format_t *inst = py_df_list.find(dtid, dfid, &pos); - // Maybe the user is trying to unregister a C api data format? - if ( inst == NULL ) - return unregister_custom_data_format(dtid, dfid); - - bool ok = inst->unregister_df(dtid); - - // Perhaps it was automatically unregistered because the type was unregistered? - if ( !ok ) - { - // Is this format still registered with IDA? - // If not, mark the context for deletion - ok = find_custom_data_format(inst->get_name()) < 0; - } - - if ( ok ) - { - py_df_list.erase(pos); - delete inst; - } - return ok; -} - -//------------------------------------------------------------------------ -/* -# -def get_custom_data_format(dtid, dfid): - """ - Returns a dictionary populated with the data format values or None on failure. - @param dtid: data type id - @param dfid: data format id - """ - pass -# -*/ -// Get definition of a registered custom data format and returns a dictionary -static PyObject *py_get_custom_data_format(int dtid, int fid) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - const data_format_t *df = get_custom_data_format(dtid, fid); - if ( df == NULL ) - Py_RETURN_NONE; - return py_data_format_to_py_dict(df); -} - -//------------------------------------------------------------------------ -/* -# -def get_custom_data_type(dtid): - """ - Returns a dictionary populated with the data type values or None on failure. - @param dtid: data type id - """ - pass -# -*/ -// Get definition of a registered custom data format and returns a dictionary -static PyObject *py_get_custom_data_type(int dtid) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - const data_type_t *dt = get_custom_data_type(dtid); - if ( dt == NULL ) - Py_RETURN_NONE; - return py_data_type_to_py_dict(dt); -} - -// -%} - -%pythoncode %{ -# -DTP_NODUP = 0x0001 - -class data_type_t(object): - """ - Custom data type definition. All data types should inherit from this class. - """ - - def __init__(self, name, value_size = 0, menu_name = None, hotkey = None, asm_keyword = None, props = 0): - """Please refer to bytes.hpp / data_type_t in the SDK""" - self.name = name - self.props = props - self.menu_name = menu_name - self.hotkey = hotkey - self.asm_keyword = asm_keyword - self.value_size = value_size - - self.id = -1 # Will be initialized after registration - """Contains the data type id after the data type is registered""" - - def register(self): - """Registers the data type and returns the type id or < 0 on failure""" - return _idaapi.register_custom_data_type(self) - - def unregister(self): - """Unregisters the data type and returns True on success""" - # Not registered? - if self.id < 0: - return True - - # Try to unregister - r = _idaapi.unregister_custom_data_type(self.id) - - # Clear the ID - if r: - self.id = -1 - return r -# -# def may_create_at(self, ea, nbytes): -# """ -# (optional) If this callback is not defined then this means always may create data type at the given ea. -# @param ea: address of the future item -# @param nbytes: size of the future item -# @return: Boolean -# """ -# -# return False -# -# def calc_item_size(self, ea, maxsize): -# """ -# (optional) If this callback is defined it means variable size datatype -# This function is used to determine size of the (possible) item at 'ea' -# @param ea: address of the item -# @param maxsize: maximal size of the item -# @return: integer -# Returns: 0-no such item can be created/displayed -# this callback is required only for varsize datatypes -# """ -# return 0 -# -# ----------------------------------------------------------------------- -# Uncomment the corresponding callbacks in the inherited class -class data_format_t(object): - """Information about a data format""" - def __init__(self, name, value_size = 0, menu_name = None, props = 0, hotkey = None, text_width = 0): - """Custom data format definition. - @param name: Format name, must be unique - @param menu_name: Visible format name to use in menus - @param props: properties (currently 0) - @param hotkey: Hotkey for the corresponding menu item - @param value_size: size of the value in bytes. 0 means any size is ok - @text_width: Usual width of the text representation - """ - self.name = name - self.menu_name = menu_name - self.props = props - self.hotkey = hotkey - self.value_size = value_size - self.text_width = text_width - - self.id = -1 # Will be initialized after registration - """contains the format id after the format gets registered""" - - def register(self, dtid): - """Registers the data format with the given data type id and returns the type id or < 0 on failure""" - return _idaapi.register_custom_data_format(dtid, self) - - def unregister(self, dtid): - """Unregisters the data format with the given data type id""" - - # Not registered? - if self.id < 0: - return True - - # Unregister - r = _idaapi.unregister_custom_data_format(dtid, self.id) - - # Clear the ID - if r: - self.id = -1 - return r -# -# def printf(self, value, current_ea, operand_num, dtid): -# """ -# Convert a value buffer to colored string. -# -# @param value: The value to be printed -# @param current_ea: The ea of the value -# @param operand_num: The affected operand -# @param dtid: custom data type id (0-standard built-in data type) -# @return: a colored string representing the passed 'value' or None on failure -# """ -# return None -# -# def scan(self, input, current_ea, operand_num): -# """ -# Convert from uncolored string 'input' to byte value -# -# @param input: input string -# @param current_ea: current address (BADADDR if unknown) -# @param operand_num: current operand number (-1 if unknown) -# -# @return: tuple (Boolean, string) -# - (False, ErrorMessage) if conversion fails -# - (True, Value buffer) if conversion succeeds -# """ -# return (False, "Not implemented") -# -# def analyze(self, current_ea, operand_num): -# """ -# (optional) Analyze custom data format occurrence. -# It can be used to create xrefs from the current item. -# -# @param current_ea: current address (BADADDR if unknown) -# @param operand_num: current operand number -# @return: None -# """ -# -# pass -# -# ----------------------------------------------------------------------- -def __walk_types_and_formats(formats, type_action, format_action, installing): - broken = False - for f in formats: - if len(f) == 1: - if not format_action(f[0], 0): - broken = True - break - else: - dt = f[0] - dfs = f[1:] - # install data type before installing formats - if installing and not type_action(dt): - broken = True - break - # process formats using the correct dt.id - for df in dfs: - if not format_action(df, dt.id): - broken = True - break - # uninstall data type after uninstalling formats - if not installing and not type_action(dt): - broken = True - break - return not broken - -# ----------------------------------------------------------------------- -def register_data_types_and_formats(formats): - """ - Registers multiple data types and formats at once. - To register one type/format at a time use register_custom_data_type/register_custom_data_format - - It employs a special table of types and formats described below: - - The 'formats' is a list of tuples. 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, then tuple[0] is the data type and tuple[1:] are the data formats. For example: - many_formats = [ - (pascal_data_type(), pascal_data_format()), - (simplevm_data_type(), simplevm_data_format()), - (makedword_data_format(),), - (simplevm_data_format(),) - ] - The first two tuples describe data types and their associated formats. - The last two tuples describe two data formats to be used with built-in data types. - """ - def __reg_format(df, dtid): - df.register(dtid) - if dtid == 0: - print "Registered format '%s' with built-in types, ID=%d" % (df.name, df.id) - else: - print " Registered format '%s', ID=%d (dtid=%d)" % (df.name, df.id, dtid) - return df.id != -1 - - def __reg_type(dt): - dt.register() - print "Registered type '%s', ID=%d" % (dt.name, dt.id) - return dt.id != -1 - ok = __walk_types_and_formats(formats, __reg_type, __reg_format, True) - return 1 if ok else -1 - -# ----------------------------------------------------------------------- -def unregister_data_types_and_formats(formats): - """As opposed to register_data_types_and_formats(), this function - unregisters multiple data types and formats at once. - """ - def __unreg_format(df, dtid): - print "%snregistering format '%s'" % ("U" if dtid == 0 else " u", df.name) - df.unregister(dtid) - return True - - def __unreg_type(dt): - print "Unregistering type '%s', ID=%d" % (dt.name, dt.id) - dt.unregister() - return True - ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format, False) - return 1 if ok else -1 - -# -%} \ No newline at end of file +// Make get_any_cmt() work +%apply unsigned char *OUTPUT { color_t *cmttype }; + +// For get_enum_id() +%apply unsigned char *OUTPUT { uchar *serial }; + +// get_[first|last]_serial_enum_member() won't take serials as input; it'll be present as output +%apply unsigned char *OUTPUT { uchar *out_serial }; +// get_[next|prev]_serial_enum_member() take serials as input, and have the result present as output +%apply unsigned char *INOUT { uchar *in_out_serial }; + +// Unexported and kernel-only declarations +%ignore FlagsEnable; +%ignore FlagsDisable; +%ignore testf_t; +%ignore nextthat; +%ignore prevthat; +%ignore adjust_visea; +%ignore prev_visea; +%ignore next_visea; +%ignore visit_patched_bytes; +%ignore is_first_visea; +%ignore is_last_visea; +%ignore is_visible_finally; +%ignore invalidate_visea_cache; +%ignore fluFlags; +%ignore setFlbits; +%ignore clrFlbits; +%ignore get_8bit; +%ignore get_ascii_char; +%ignore del_opinfo; +%ignore del_one_opinfo; +%ignore doCode; +%ignore get_repeatable_cmt; +%ignore get_any_indented_cmt; +%ignore del_code_comments; +%ignore doFlow; +%ignore noFlow; +%ignore doRef; +%ignore noRef; +%ignore coagulate; +%ignore coagulate_dref; +%ignore init_hidden_areas; +%ignore save_hidden_areas; +%ignore term_hidden_areas; +%ignore check_move_args; +%ignore movechunk; +%ignore lock_dbgmem_config; +%ignore unlock_dbgmem_config; +%ignore set_op_type_no_event; +%ignore shuffle_tribytes; +%ignore set_enum_id; +%ignore validate_tofs; +%ignore set_flags_nomark; +%ignore set_flbits_nomark; +%ignore clr_flbits_nomark; +%ignore ida_vpagesize; +%ignore ida_vpages; +%ignore ida_npagesize; +%ignore ida_npages; +%ignore fpnum_digits; +%ignore fpnum_length; +%ignore FlagsInit; +%ignore FlagsTerm; +%ignore FlagsReset; +%ignore init_flags; +%ignore term_flags; +%ignore reset_flags; +%ignore flush_flags; +%ignore get_flags_linput; +%ignore data_type_t; +%ignore data_format_t; +%ignore get_custom_data_type; +%ignore get_custom_data_format; +%ignore unregister_custom_data_format; +%ignore register_custom_data_format; +%ignore unregister_custom_data_type; +%ignore register_custom_data_type; +%ignore get_many_bytes; +%ignore get_ascii_contents; +%ignore get_ascii_contents2; + +// TODO: This could be fixed (if needed) +%ignore set_dbgmem_source; + +%include "bytes.hpp" + +%clear(void *buf, ssize_t size); + +%clear(const void *buf, size_t size); +%clear(void *buf, ssize_t size); +%clear(opinfo_t *); + +%rename (visit_patched_bytes) py_visit_patched_bytes; +%rename (nextthat) py_nextthat; +%rename (prevthat) py_prevthat; +%rename (get_custom_data_type) py_get_custom_data_type; +%rename (get_custom_data_format) py_get_custom_data_format; +%rename (unregister_custom_data_format) py_unregister_custom_data_format; +%rename (register_custom_data_format) py_register_custom_data_format; +%rename (unregister_custom_data_type) py_unregister_custom_data_type; +%rename (register_custom_data_type) py_register_custom_data_type; +%rename (get_many_bytes) py_get_many_bytes; +%rename (get_ascii_contents) py_get_ascii_contents; +%rename (get_ascii_contents2) py_get_ascii_contents2; +%{ +// +//------------------------------------------------------------------------ +static bool idaapi py_testf_cb(flags_t flags, void *ud) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_flags(PyLong_FromUnsignedLong(flags)); + newref_t result(PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags.o, NULL)); + return result != NULL && PyObject_IsTrue(result.o); +} + +//------------------------------------------------------------------------ +// Wraps the (next|prev)that() +static ea_t py_npthat(ea_t ea, ea_t bound, PyObject *py_callable, bool next) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCallable_Check(py_callable) ) + return BADADDR; + else + return (next ? nextthat : prevthat)(ea, bound, py_testf_cb, py_callable); +} + +//--------------------------------------------------------------------------- +static int idaapi py_visit_patched_bytes_cb( + ea_t ea, + int32 fpos, + uint32 o, + uint32 v, + void *ud) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallFunction( + (PyObject *)ud, + PY_FMT64 "iII", + pyul_t(ea), + fpos, + o, + v)); + PyW_ShowCbErr("visit_patched_bytes"); + return (py_result != NULL && PyInt_Check(py_result.o)) ? PyInt_AsLong(py_result.o) : 0; +} + + + +//------------------------------------------------------------------------ +class py_custom_data_type_t +{ + data_type_t dt; + qstring dt_name, dt_menu_name, dt_hotkey, dt_asm_keyword; + int dtid; // The data format id + PyObject *py_self; // Associated Python object + + // may create data? NULL means always may + static bool idaapi s_may_create_at( + void *ud, // user-defined data + ea_t ea, // address of the future item + size_t nbytes) // size of the future item + { + py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; + + PYW_GIL_GET; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_MAY_CREATE_AT, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(nbytes))); + + PyW_ShowCbErr(S_MAY_CREATE_AT); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + // !=NULL means variable size datatype + static asize_t idaapi s_calc_item_size( + // This function is used to determine + // size of the (possible) item at 'ea' + void *ud, // user-defined data + ea_t ea, // address of the item + asize_t maxsize) // maximal size of the item + { + PYW_GIL_GET; + // Returns: 0-no such item can be created/displayed + // this callback is required only for varsize datatypes + py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_CALC_ITEM_SIZE, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(maxsize))); + + if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) + return 0; + + uint64 num = 0; + PyW_GetNumber(py_result.o, &num); + return asize_t(num); + } + +public: + const char *get_name() const + { + return dt_name.c_str(); + } + + py_custom_data_type_t() + { + dtid = -1; + py_self = NULL; + } + + int register_dt(PyObject *py_obj) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Already registered? + if ( dtid >= 0 ) + return dtid; + + memset(&dt, 0, sizeof(dt)); + dt.cbsize = sizeof(dt); + dt.ud = this; + + do + { + ref_t py_attr; + + // name + if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) + break; + + dt.name = dt_name.c_str(); + + // menu_name (optional) + if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &dt_menu_name) ) + dt.menu_name = dt_menu_name.c_str(); + + // asm_keyword (optional) + if ( PyW_GetStringAttr(py_obj, S_ASM_KEYWORD, &dt_asm_keyword) ) + dt.asm_keyword = dt_asm_keyword.c_str(); + + // hotkey (optional) + if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &dt_hotkey) ) + dt.hotkey = dt_hotkey.c_str(); + + // value_size + py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.value_size = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); + + // props + py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.props = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); + + // may_create_at + py_attr = PyW_TryGetAttrString(py_obj, S_MAY_CREATE_AT); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + dt.may_create_at = s_may_create_at; + py_attr = ref_t(); + + // calc_item_size + py_attr = PyW_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + dt.calc_item_size = s_calc_item_size; + py_attr = ref_t(); + + // Now try to register + dtid = register_custom_data_type(&dt); + if ( dtid < 0 ) + break; + + // Hold reference to the PyObject + Py_INCREF(py_obj); + py_self = py_obj; + + py_attr = newref_t(PyInt_FromLong(dtid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); + } while ( false ); + return dtid; + } + + bool unregister_dt() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( dtid < 0 ) + return true; + + if ( !unregister_custom_data_type(dtid) ) + return false; + + // Release reference of Python object + Py_XDECREF(py_self); + py_self = NULL; + dtid = -1; + return true; + } + + ~py_custom_data_type_t() + { + unregister_dt(); + } +}; +typedef std::map py_custom_data_type_map_t; +static py_custom_data_type_map_t py_dt_map; + +//------------------------------------------------------------------------ +class py_custom_data_format_t +{ +private: + data_format_t df; + int dfid; + PyObject *py_self; + qstring df_name, df_menu_name, df_hotkey; + + static bool idaapi s_print( // convert to colored string + void *ud, // user-defined data + qstring *out, // output buffer. may be NULL + const void *value, // value to print. may not be NULL + asize_t size, // size of value in bytes + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num, // current operand number + int dtid) // custom data type id + { + PYW_GIL_GET; + + // Build a string from the buffer + newref_t py_value(PyString_FromStringAndSize( + (const char *)value, + Py_ssize_t(size))); + if ( py_value == NULL ) + return false; + + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + newref_t py_result(PyObject_CallMethod( + _this->py_self, + (char *)S_PRINTF, + "O" PY_FMT64 "ii", + py_value.o, + pyul_t(current_ea), + operand_num, + dtid)); + + // Error while calling the function? + if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL ) + return false; + + bool ok = false; + if ( PyString_Check(py_result.o) ) + { + Py_ssize_t len; + char *buf; + if ( out != NULL && PyString_AsStringAndSize(py_result.o, &buf, &len) != -1 ) + { + out->qclear(); + out->append(buf, len); + } + ok = true; + } + return ok; + } + + static bool idaapi s_scan( // convert from uncolored string + void *ud, // user-defined data + bytevec_t *value, // output buffer. may be NULL + const char *input, // input string. may not be NULL + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num, // current operand number (-1 if unknown) + qstring *errstr) // buffer for error message + { + PYW_GIL_GET; + + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_SCAN, + "s" PY_FMT64, + input, + pyul_t(current_ea), + operand_num)); + + // Error while calling the function? + if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL) + return false; + + bool ok = false; + do + { + // We expect a tuple(bool, string|None) + if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) + break; + + borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); + borref_t py_val(PyTuple_GetItem(py_result.o, 1)); + + // Get return code from Python + ok = PyObject_IsTrue(py_bool.o); + + // We expect None or the value (depending on probe) + if ( ok ) + { + // Probe-only? Then okay, no need to extract the 'value' + if ( value == NULL ) + break; + + Py_ssize_t len; + char *buf; + if ( PyString_AsStringAndSize(py_val.o, &buf, &len) != -1 ) + { + value->qclear(); + value->append(buf, len); + } + } + // An error occured? + else + { + // Make sure the user returned (False, String) + if ( py_bool.o != Py_False || !PyString_Check(py_val.o) ) + { + *errstr = "Invalid return value returned from the Python callback!"; + break; + } + // Get the error message + *errstr = PyString_AsString(py_val.o); + } + } while ( false ); + return ok; + } + + static void idaapi s_analyze( // analyze custom data format occurrence + void *ud, // user-defined data + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num) // current operand number + // this callback can be used to create + // xrefs from the current item. + // this callback may be missing. + { + PYW_GIL_GET; + + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_ANALYZE, + PY_FMT64 "i", + pyul_t(current_ea), + operand_num)); + + PyW_ShowCbErr(S_ANALYZE); + } +public: + py_custom_data_format_t() + { + dfid = -1; + py_self = NULL; + } + + const char *get_name() const + { + return df_name.c_str(); + } + + int register_df(int dtid, PyObject *py_obj) + { + // Already registered? + if ( dfid >= 0 ) + return dfid; + + memset(&df, 0, sizeof(df)); + df.cbsize = sizeof(df); + df.ud = this; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + ref_t py_attr; + + // name + if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) + break; + df.name = df_name.c_str(); + + // menu_name (optional) + if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &df_menu_name) ) + df.menu_name = df_menu_name.c_str(); + + // props + py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.props = PyInt_AsLong(py_attr.o); + + // hotkey + if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) + df.hotkey = df_hotkey.c_str(); + + // value_size + py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.value_size = PyInt_AsLong(py_attr.o); + + // text_width + py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.text_width = PyInt_AsLong(py_attr.o); + + // print cb + py_attr = PyW_TryGetAttrString(py_obj, S_PRINTF); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + df.print = s_print; + + // scan cb + py_attr = PyW_TryGetAttrString(py_obj, S_SCAN); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + df.scan = s_scan; + + // analyze cb + py_attr = PyW_TryGetAttrString(py_obj, S_ANALYZE); + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) + df.analyze = s_analyze; + + // Now try to register + dfid = register_custom_data_format(dtid, &df); + if ( dfid < 0 ) + break; + + // Hold reference to the PyObject + Py_INCREF(py_obj); + py_self = py_obj; + + // Update the format ID + py_attr = newref_t(PyInt_FromLong(dfid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); + } while ( false ); + return dfid; + } + + bool unregister_df(int dtid) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Never registered? + if ( dfid < 0 ) + return true; + + if ( !unregister_custom_data_format(dtid, dfid) ) + return false; + + // Release reference of Python object + Py_XDECREF(py_self); + py_self = NULL; + dfid = -1; + return true; + } + + ~py_custom_data_format_t() + { + } +}; + +//------------------------------------------------------------------------ +// Helper class to bind pairs to py_custom_data_format_t +class py_custom_data_format_list_t +{ + struct py_custom_data_format_entry_t + { + int dtid; + int dfid; + py_custom_data_format_t *df; + }; + typedef qvector ENTRY; + ENTRY entries; +public: + typedef ENTRY::iterator POS; + void add(int dtid, int dfid, py_custom_data_format_t *df) + { + py_custom_data_format_entry_t &e = entries.push_back(); + e.dtid = dtid; + e.dfid = dfid; + e.df = df; + } + py_custom_data_format_t *find(int dtid, int dfid, POS *loc = NULL) + { + for ( POS it=entries.begin(), it_end = entries.end(); it!=it_end; ++it ) + { + if ( it->dfid == dfid && it->dtid == dtid ) + { + if ( loc != NULL ) + *loc = it; + return it->df; + } + } + return NULL; + } + void erase(POS &pos) + { + entries.erase(pos); + } +}; +static py_custom_data_format_list_t py_df_list; + +//------------------------------------------------------------------------ +static PyObject *py_data_type_to_py_dict(const data_type_t *dt) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", + S_VALUE_SIZE, pyul_t(dt->value_size), + S_PROPS, dt->props, + S_CBSIZE, dt->cbsize, + S_NAME, dt->name == NULL ? "" : dt->name, + S_MENU_NAME, dt->menu_name == NULL ? "" : dt->menu_name, + S_HOTKEY, dt->hotkey == NULL ? "" : dt->hotkey, + S_ASM_KEYWORD, dt->asm_keyword == NULL ? "" : dt->asm_keyword); +} + +//------------------------------------------------------------------------ +static PyObject *py_data_format_to_py_dict(const data_format_t *df) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", + S_PROPS, df->props, + S_CBSIZE, df->cbsize, + S_TEXT_WIDTH, df->text_width, + S_VALUE_SIZE, pyul_t(df->value_size), + S_NAME, df->name == NULL ? "" : df->name, + S_MENU_NAME, df->menu_name == NULL ? "" : df->menu_name, + S_HOTKEY, df->hotkey == NULL ? "" : df->hotkey); +} +// +%} + +%inline %{ +// + +//------------------------------------------------------------------------ +/* +# +def visit_patched_bytes(ea1, ea2, callable): + """ + Enumerates patched bytes in the given range and invokes a callable + @param ea1: start address + @param ea2: end address + @param callable: a Python callable with the following prototype: + callable(ea, fpos, org_val, patch_val). + If the callable returns non-zero then that value will be + returned to the caller and the enumeration will be + interrupted. + @return: Zero if the enumeration was successful or the return + value of the callback if enumeration was interrupted. + """ + pass +# +*/ +static int py_visit_patched_bytes(ea_t ea1, ea_t ea2, PyObject *py_callable) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCallable_Check(py_callable) ) + return 0; + else + return visit_patched_bytes(ea1, ea2, py_visit_patched_bytes_cb, py_callable); +} + +//------------------------------------------------------------------------ +/* +# +def nextthat(ea, maxea, callable): + """ + Find next address with a flag satisfying the function 'testf'. + Start searching from address 'ea'+1 and inspect bytes up to 'maxea'. + maxea is not included in the search range. + + @param callable: a Python callable with the following prototype: + callable(flags). Return True to stop enumeration. + @return: the found address or BADADDR. + """ + pass +# +*/ +static ea_t py_nextthat(ea_t ea, ea_t maxea, PyObject *callable) +{ + return py_npthat(ea, maxea, callable, true); +} + +//--------------------------------------------------------------------------- +static ea_t py_prevthat(ea_t ea, ea_t minea, PyObject *callable) +{ + return py_npthat(ea, minea, callable, false); +} + +//------------------------------------------------------------------------ +/* +# +def get_many_bytes(ea, size): + """ + Get the specified number of bytes of the program into the buffer. + @param ea: program address + @param size: number of bytes to return + @return: None or the string buffer + """ + pass +# +*/ +static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + if ( size <= 0 ) + break; + + // Allocate memory via Python + newref_t py_buf(PyString_FromStringAndSize(NULL, Py_ssize_t(size))); + if ( py_buf == NULL ) + break; + + // Read bytes + if ( !get_many_bytes(ea, PyString_AsString(py_buf.o), size) ) + Py_RETURN_NONE; + + py_buf.incref(); + return py_buf.o; + } while ( false ); + Py_RETURN_NONE; +} + +//--------------------------------------------------------------------------- +/* +# +# Conversion options for get_ascii_contents2(): +ACFOPT_ASCII = 0x00000000 # convert Unicode strings to ASCII +ACFOPT_UTF16 = 0x00000001 # return UTF-16 (aka wide-char) array for Unicode strings + # ignored for non-Unicode strings +ACFOPT_UTF8 = 0x00000002 # convert Unicode strings to UTF-8 + # ignored for non-Unicode strings +ACFOPT_CONVMASK = 0x0000000F +ACFOPT_ESCAPE = 0x00000010 # for ACFOPT_ASCII, convert non-printable + # characters to C escapes (\n, \xNN, \uNNNN) + +def get_ascii_contents2(ea, len, type, flags = ACFOPT_ASCII): + """ + Get bytes contents at location, possibly converted. + It works even if the string has not been created in the database yet. + + Note that this will always return a simple string of bytes + (i.e., a 'str' instance), and not a string of unicode characters. + + If you want auto-conversion to unicode strings (that is: real strings), + you should probably be using the idautils.Strings class. + + @param ea: linear address of the string + @param len: length of the string in bytes (including terminating 0) + @param type: type of the string. Represents both the character encoding, + and the 'type' of string at the given location. + @param flags: combination of ACFOPT_..., to perform output conversion. + @return: a bytes-filled str object. + """ + pass +# +*/ +static PyObject *py_get_ascii_contents2( + ea_t ea, + size_t len, + int32 type, + int flags = ACFOPT_ASCII) +{ + char *buf = (char *)qalloc(len+1); + if ( buf == NULL ) + return NULL; + + size_t used_size; + if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size, flags) ) + { + qfree(buf); + Py_RETURN_NONE; + } + if ( type == ASCSTR_C && used_size > 0 && buf[used_size-1] == '\0' ) + used_size--; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_buf(PyString_FromStringAndSize((const char *)buf, used_size)); + qfree(buf); + py_buf.incref(); + return py_buf.o; +} +//--------------------------------------------------------------------------- +/* +# +def get_ascii_contents(ea, len, type): + """ + Get contents of ascii string + This function returns the displayed part of the string + It works even if the string has not been created in the database yet. + + @param ea: linear address of the string + @param len: length of the string in bytes (including terminating 0) + @param type: type of the string + @return: string contents (not including terminating 0) or None + """ + pass +# +*/ +static PyObject *py_get_ascii_contents( + ea_t ea, + size_t len, + int32 type) +{ + return py_get_ascii_contents2(ea, len, type); +} + + + +//------------------------------------------------------------------------ +/* +# +def register_custom_data_type(dt): + """ + Registers a custom data type. + @param dt: an instance of the data_type_t class + @return: + < 0 if failed to register + > 0 data type id + """ + pass +# +*/ +// Given a py.data_format_t object, this function will register a datatype +static int py_register_custom_data_type(PyObject *py_dt) +{ + py_custom_data_type_t *inst = new py_custom_data_type_t(); + int r = inst->register_dt(py_dt); + if ( r < 0 ) + { + delete inst; + return r; + } + // Insert the instance to the map + py_dt_map[r] = inst; + return r; +} + +//------------------------------------------------------------------------ +/* +# +def unregister_custom_data_type(dtid): + """ + Unregisters a custom data type. + @param dtid: the data type id + @return: Boolean + """ + pass +# +*/ +static bool py_unregister_custom_data_type(int dtid) +{ + py_custom_data_type_map_t::iterator it = py_dt_map.find(dtid); + + // Maybe the user is trying to unregister a C api dt? + if ( it == py_dt_map.end() ) + return unregister_custom_data_type(dtid); + + py_custom_data_type_t *inst = it->second; + bool ok = inst->unregister_dt(); + + // Perhaps it was automatically unregistered because the idb was close? + if ( !ok ) + { + // Is this type still registered with IDA? + // If not found then mark the context for deletion + ok = find_custom_data_type(inst->get_name()) < 0; + } + + if ( ok ) + { + py_dt_map.erase(it); + delete inst; + } + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def register_custom_data_format(dtid, df): + """ + Registers a custom data format with a given data type. + @param dtid: data type id + @param df: an instance of data_format_t + @return: + < 0 if failed to register + > 0 data format id + """ + pass +# +*/ +static int py_register_custom_data_format(int dtid, PyObject *py_df) +{ + py_custom_data_format_t *inst = new py_custom_data_format_t(); + int r = inst->register_df(dtid, py_df); + if ( r < 0 ) + { + delete inst; + return r; + } + // Insert the instance + py_df_list.add(dtid, r, inst); + return r; +} + +//------------------------------------------------------------------------ +/* +# +def unregister_custom_data_format(dtid, dfid): + """ + Unregisters a custom data format + @param dtid: data type id + @param dfid: data format id + @return: Boolean + """ + pass +# +*/ +static bool py_unregister_custom_data_format(int dtid, int dfid) +{ + py_custom_data_format_list_t::POS pos; + py_custom_data_format_t *inst = py_df_list.find(dtid, dfid, &pos); + // Maybe the user is trying to unregister a C api data format? + if ( inst == NULL ) + return unregister_custom_data_format(dtid, dfid); + + bool ok = inst->unregister_df(dtid); + + // Perhaps it was automatically unregistered because the type was unregistered? + if ( !ok ) + { + // Is this format still registered with IDA? + // If not, mark the context for deletion + ok = find_custom_data_format(inst->get_name()) < 0; + } + + if ( ok ) + { + py_df_list.erase(pos); + delete inst; + } + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def get_custom_data_format(dtid, dfid): + """ + Returns a dictionary populated with the data format values or None on failure. + @param dtid: data type id + @param dfid: data format id + """ + pass +# +*/ +// Get definition of a registered custom data format and returns a dictionary +static PyObject *py_get_custom_data_format(int dtid, int fid) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + const data_format_t *df = get_custom_data_format(dtid, fid); + if ( df == NULL ) + Py_RETURN_NONE; + return py_data_format_to_py_dict(df); +} + +//------------------------------------------------------------------------ +/* +# +def get_custom_data_type(dtid): + """ + Returns a dictionary populated with the data type values or None on failure. + @param dtid: data type id + """ + pass +# +*/ +// Get definition of a registered custom data format and returns a dictionary +static PyObject *py_get_custom_data_type(int dtid) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + const data_type_t *dt = get_custom_data_type(dtid); + if ( dt == NULL ) + Py_RETURN_NONE; + return py_data_type_to_py_dict(dt); +} + +// +%} + +%pythoncode %{ +# +DTP_NODUP = 0x0001 + +class data_type_t(object): + """ + Custom data type definition. All data types should inherit from this class. + """ + + def __init__(self, name, value_size = 0, menu_name = None, hotkey = None, asm_keyword = None, props = 0): + """Please refer to bytes.hpp / data_type_t in the SDK""" + self.name = name + self.props = props + self.menu_name = menu_name + self.hotkey = hotkey + self.asm_keyword = asm_keyword + self.value_size = value_size + + self.id = -1 # Will be initialized after registration + """Contains the data type id after the data type is registered""" + + def register(self): + """Registers the data type and returns the type id or < 0 on failure""" + return _idaapi.register_custom_data_type(self) + + def unregister(self): + """Unregisters the data type and returns True on success""" + # Not registered? + if self.id < 0: + return True + + # Try to unregister + r = _idaapi.unregister_custom_data_type(self.id) + + # Clear the ID + if r: + self.id = -1 + return r +# +# def may_create_at(self, ea, nbytes): +# """ +# (optional) If this callback is not defined then this means always may create data type at the given ea. +# @param ea: address of the future item +# @param nbytes: size of the future item +# @return: Boolean +# """ +# +# return False +# +# def calc_item_size(self, ea, maxsize): +# """ +# (optional) If this callback is defined it means variable size datatype +# This function is used to determine size of the (possible) item at 'ea' +# @param ea: address of the item +# @param maxsize: maximal size of the item +# @return: integer +# Returns: 0-no such item can be created/displayed +# this callback is required only for varsize datatypes +# """ +# return 0 +# +# ----------------------------------------------------------------------- +# Uncomment the corresponding callbacks in the inherited class +class data_format_t(object): + """Information about a data format""" + def __init__(self, name, value_size = 0, menu_name = None, props = 0, hotkey = None, text_width = 0): + """Custom data format definition. + @param name: Format name, must be unique + @param menu_name: Visible format name to use in menus + @param props: properties (currently 0) + @param hotkey: Hotkey for the corresponding menu item + @param value_size: size of the value in bytes. 0 means any size is ok + @text_width: Usual width of the text representation + """ + self.name = name + self.menu_name = menu_name + self.props = props + self.hotkey = hotkey + self.value_size = value_size + self.text_width = text_width + + self.id = -1 # Will be initialized after registration + """contains the format id after the format gets registered""" + + def register(self, dtid): + """Registers the data format with the given data type id and returns the type id or < 0 on failure""" + return _idaapi.register_custom_data_format(dtid, self) + + def unregister(self, dtid): + """Unregisters the data format with the given data type id""" + + # Not registered? + if self.id < 0: + return True + + # Unregister + r = _idaapi.unregister_custom_data_format(dtid, self.id) + + # Clear the ID + if r: + self.id = -1 + return r +# +# def printf(self, value, current_ea, operand_num, dtid): +# """ +# Convert a value buffer to colored string. +# +# @param value: The value to be printed +# @param current_ea: The ea of the value +# @param operand_num: The affected operand +# @param dtid: custom data type id (0-standard built-in data type) +# @return: a colored string representing the passed 'value' or None on failure +# """ +# return None +# +# def scan(self, input, current_ea, operand_num): +# """ +# Convert from uncolored string 'input' to byte value +# +# @param input: input string +# @param current_ea: current address (BADADDR if unknown) +# @param operand_num: current operand number (-1 if unknown) +# +# @return: tuple (Boolean, string) +# - (False, ErrorMessage) if conversion fails +# - (True, Value buffer) if conversion succeeds +# """ +# return (False, "Not implemented") +# +# def analyze(self, current_ea, operand_num): +# """ +# (optional) Analyze custom data format occurrence. +# It can be used to create xrefs from the current item. +# +# @param current_ea: current address (BADADDR if unknown) +# @param operand_num: current operand number +# @return: None +# """ +# +# pass +# +# ----------------------------------------------------------------------- +def __walk_types_and_formats(formats, type_action, format_action, installing): + broken = False + for f in formats: + if len(f) == 1: + if not format_action(f[0], 0): + broken = True + break + else: + dt = f[0] + dfs = f[1:] + # install data type before installing formats + if installing and not type_action(dt): + broken = True + break + # process formats using the correct dt.id + for df in dfs: + if not format_action(df, dt.id): + broken = True + break + # uninstall data type after uninstalling formats + if not installing and not type_action(dt): + broken = True + break + return not broken + +# ----------------------------------------------------------------------- +def register_data_types_and_formats(formats): + """ + Registers multiple data types and formats at once. + To register one type/format at a time use register_custom_data_type/register_custom_data_format + + It employs a special table of types and formats described below: + + The 'formats' is a list of tuples. 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, then tuple[0] is the data type and tuple[1:] are the data formats. For example: + many_formats = [ + (pascal_data_type(), pascal_data_format()), + (simplevm_data_type(), simplevm_data_format()), + (makedword_data_format(),), + (simplevm_data_format(),) + ] + The first two tuples describe data types and their associated formats. + The last two tuples describe two data formats to be used with built-in data types. + """ + def __reg_format(df, dtid): + df.register(dtid) + if dtid == 0: + print "Registered format '%s' with built-in types, ID=%d" % (df.name, df.id) + else: + print " Registered format '%s', ID=%d (dtid=%d)" % (df.name, df.id, dtid) + return df.id != -1 + + def __reg_type(dt): + dt.register() + print "Registered type '%s', ID=%d" % (dt.name, dt.id) + return dt.id != -1 + ok = __walk_types_and_formats(formats, __reg_type, __reg_format, True) + return 1 if ok else -1 + +# ----------------------------------------------------------------------- +def unregister_data_types_and_formats(formats): + """As opposed to register_data_types_and_formats(), this function + unregisters multiple data types and formats at once. + """ + def __unreg_format(df, dtid): + print "%snregistering format '%s'" % ("U" if dtid == 0 else " u", df.name) + df.unregister(dtid) + return True + + def __unreg_type(dt): + print "Unregistering type '%s', ID=%d" % (dt.name, dt.id) + dt.unregister() + return True + ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format, False) + return 1 if ok else -1 + +# +%} diff --git a/swig/dbg.i b/swig/dbg.i index 18c3b775..f8844627 100644 --- a/swig/dbg.i +++ b/swig/dbg.i @@ -1,461 +1,463 @@ -// SWIG chokes on the original declaration so it is replicated here -typedef struct -{ - ulonglong ival; // 8: integer value - ushort fval[6]; // 12: floating point value in the internal representation (see ieee.h) -} regval_t; - -%ignore dbg; -%ignore register_srcinfo_provider; -%ignore unregister_srcinfo_provider; -%ignore appcall_info_t; -%ignore get_manual_regions; -%ignore internal_appcall; -%ignore internal_cleanup_appcall; - -%ignore source_file_t; -%ignore source_item_t; -%ignore srcinfo_provider_t; -%ignore bpt_location_t::print; -%ignore bpt_t::set_cond; -%ignore bpt_t::eval_cond; -%ignore bpt_t::write; -%ignore bpt_t::erase; -%ignore bpt_t::cndbody; -%ignore bpt_t::get_cnd_elang; -%ignore bpt_t::set_cnd_elang; -%rename (get_manual_regions) py_get_manual_regions; -%ignore set_manual_regions; -%ignore inform_idc_about_debthread; -%ignore is_dbgmem_valid; - -%rename (list_bptgrps) py_list_bptgrps; -%apply qstring *result { qstring *grp_name }; -%ignore qvector::operator==; -%ignore qvector::operator!=; -%ignore qvector::find; -%ignore qvector::has; -%ignore qvector::del; -%ignore qvector::add_unique; -%template(bpt_vec_t) qvector; - -%ignore internal_get_sreg_base; -%rename (internal_get_sreg_base) py_internal_get_sreg_base; - -// We want ALL wrappers around what is declared in dbg.hpp -// to release the GIL when calling into the IDA api: those -// might be very long operations, that even require some -// network traffic. -%thread; -%include "dbg.hpp" -%nothread; -%ignore DBG_Callback; -%feature("director") DBG_Hooks; - -%{ -// -static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); -// -%} - -%extend bpt_t -{ - PyObject *condition; - PyObject *elang; -} -%{ -PyObject *bpt_t_condition_get(bpt_t *bpt) -{ - return PyString_FromString(bpt->cndbody.c_str()); -} - -void bpt_t_condition_set(bpt_t *bpt, PyObject *val) -{ - if ( PyString_Check(val) ) - bpt->cndbody = PyString_AsString(val); - else - PyErr_SetString(PyExc_ValueError, "expected a string"); -} - -PyObject *bpt_t_elang_get(bpt_t *bpt) -{ - return PyString_FromString(bpt->get_cnd_elang()); -} - -void bpt_t_elang_set(bpt_t *bpt, PyObject *val) -{ - if ( PyString_Check(val) ) - { - char *cval = PyString_AsString(val); - if ( !bpt->set_cnd_elang(cval) ) - PyErr_SetString(PyExc_ValueError, "too many extlangs"); - } - else - { - PyErr_SetString(PyExc_ValueError, "expected a string"); - } -} -%} -%inline %{ - -// - -//------------------------------------------------------------------------- -/* -# -def get_manual_regions(): - """ - Returns the manual memory regions - @return: list(startEA, endEA, name, sclass, sbase, bitness, perm) - """ - pass -# -*/ -static PyObject *py_get_manual_regions() -{ - meminfo_vec_t areas; - get_manual_regions(&areas); - return meminfo_vec_t_to_py(areas); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_is_loaded(): - """ - Checks if a debugger is loaded - @return: Boolean - """ - pass -# -*/ -static bool dbg_is_loaded() -{ - return dbg != NULL; -} - -//------------------------------------------------------------------------- -/* -# -def refresh_debugger_memory(): - """ - Refreshes the debugger memory - @return: Nothing - """ - pass -# -*/ -static PyObject *refresh_debugger_memory() -{ - invalidate_dbgmem_config(); - invalidate_dbgmem_contents(BADADDR, 0); - - // Ask the debugger to populate debug names - if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) - dbg->stopped_at_debug_event(true); - - // Invalidate the cache - isEnabled(0); - - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; -} - -int idaapi DBG_Callback(void *ud, int notification_code, va_list va); -class DBG_Hooks -{ -public: - virtual ~DBG_Hooks() { unhook(); } - - bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); } - bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); } - /* Hook functions to be overridden in Python */ - virtual void dbg_process_start(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) {} - virtual void dbg_process_exit(pid_t pid, - thid_t tid, - ea_t ea, - int exit_code) {} - virtual void dbg_process_attach(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) {} - virtual void dbg_process_detach(pid_t pid, - thid_t tid, - ea_t ea) {} - virtual void dbg_thread_start(pid_t pid, - thid_t tid, - ea_t ea) {} - virtual void dbg_thread_exit(pid_t pid, - thid_t tid, - ea_t ea, - int exit_code) {} - virtual void dbg_library_load(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) {} - virtual void dbg_library_unload(pid_t pid, - thid_t tid, - ea_t ea, - char *libname) {} - virtual void dbg_information(pid_t pid, - thid_t tid, - ea_t ea, - char *info) {} - virtual int dbg_exception(pid_t pid, - thid_t tid, - ea_t ea, - int code, - bool can_cont, - ea_t exc_ea, - char *info) { return 0; } - virtual void dbg_suspend_process(void) {} - virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; } - virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; } - virtual void dbg_request_error(int failed_command, - int failed_dbg_notification) {} - virtual void dbg_step_into(void) {} - virtual void dbg_step_over(void) {} - virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {} - virtual void dbg_step_until_ret(void) {} -}; - -int idaapi DBG_Callback(void *ud, int notification_code, va_list va) -{ - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - class DBG_Hooks *proxy = (class DBG_Hooks *)ud; - debug_event_t *event; - int code = 0; - - try - { - switch (notification_code) - { - case dbg_process_start: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_start(event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - break; - - case dbg_process_exit: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_exit( - event->pid, - event->tid, - event->ea, - event->exit_code); - break; - - case dbg_process_attach: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_attach( - event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - break; - - case dbg_process_detach: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_detach( - event->pid, - event->tid, - event->ea); - break; - - case dbg_thread_start: - event = va_arg(va, debug_event_t *); - proxy->dbg_thread_start( - event->pid, - event->tid, - event->ea); - break; - - case dbg_thread_exit: - event = va_arg(va, debug_event_t *); - proxy->dbg_thread_exit( - event->pid, - event->tid, - event->ea, - event->exit_code); - break; - - case dbg_library_load: - event = va_arg(va, debug_event_t *); - proxy->dbg_library_load( - event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - break; - - case dbg_library_unload: - event = va_arg(va, debug_event_t *); - proxy->dbg_library_unload( - event->pid, - event->tid, - event->ea, - event->info); - break; - - case dbg_information: - event = va_arg(va, debug_event_t *); - proxy->dbg_information( - event->pid, - event->tid, - event->ea, - event->info); - break; - - case dbg_exception: - { - event = va_arg(va, debug_event_t *); - int *warn = va_arg(va, int *); - *warn = proxy->dbg_exception( - event->pid, - event->tid, - event->ea, - event->exc.code, - event->exc.can_cont, - event->exc.ea, - event->exc.info); - break; - } - - case dbg_suspend_process: - proxy->dbg_suspend_process(); - break; - - case dbg_bpt: - { - thid_t tid = va_arg(va, thid_t); - ea_t breakpoint_ea = va_arg(va, ea_t); - int *warn = va_arg(va, int *); - *warn = proxy->dbg_bpt(tid, breakpoint_ea); - break; - } - - case dbg_trace: - { - thid_t tid = va_arg(va, thid_t); - ea_t ip = va_arg(va, ea_t); - code = proxy->dbg_trace(tid, ip); - break; - } - - case dbg_request_error: - { - int failed_command = (int)va_argi(va, ui_notification_t); - int failed_dbg_notification = (int)va_argi(va, dbg_notification_t); - proxy->dbg_request_error(failed_command, failed_dbg_notification); - break; - } - - case dbg_step_into: - proxy->dbg_step_into(); - break; - - case dbg_step_over: - proxy->dbg_step_over(); - break; - - case dbg_run_to: - event = va_arg(va, debug_event_t *); - proxy->dbg_run_to( - event->pid, - event->tid, - event->ea); - break; - - case dbg_step_until_ret: - proxy->dbg_step_until_ret(); - break; - } - } - catch (Swig::DirectorException &e) - { - msg("Exception in DBG Hook function: %s\n", e.getMessage()); - if (PyErr_Occurred()) - PyErr_Print(); - } - return code; -} - -//------------------------------------------------------------------------ -/* -# -def py_list_bptgrps(): - """ - Returns list of breakpoint group names - @return: A list of strings or None on failure - """ - pass -# -*/ -static PyObject *py_list_bptgrps() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstrvec_t args; - if ( list_bptgrps(&args) == 0 ) - Py_RETURN_NONE; - return qstrvec2pylist(args); -} - -//------------------------------------------------------------------------ -/* -# -def move_bpt_to_grp(): - """ - Sets new group for the breakpoint - """ - pass -# -*/ -static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - set_bpt_group(*bpt, grp_name); -} - -/* -# -def internal_get_sreg_base(): - """ - Get the sreg base, for the given thread. - - @return: The sreg base, or BADADDR on failure. - """ - pass -# -*/ -static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ea_t answer; - return internal_get_sreg_base(tid, sreg_value, &answer) < 1 - ? BADADDR - : answer; -} - -// - -%} +// SWIG chokes on the original declaration so it is replicated here +typedef struct +{ + ulonglong ival; // 8: integer value + ushort fval[6]; // 12: floating point value in the internal representation (see ieee.h) +} regval_t; + +%ignore dbg; +%ignore register_srcinfo_provider; +%ignore unregister_srcinfo_provider; +%ignore appcall_info_t; +%ignore get_manual_regions; +%ignore internal_appcall; +%ignore internal_cleanup_appcall; +%ignore change_bptlocs; +%ignore movbpt_info_t; + +%ignore source_file_t; +%ignore source_item_t; +%ignore srcinfo_provider_t; +%ignore bpt_location_t::print; +%ignore bpt_t::set_cond; +%ignore bpt_t::eval_cond; +%ignore bpt_t::write; +%ignore bpt_t::erase; +%ignore bpt_t::cndbody; +%ignore bpt_t::get_cnd_elang; +%ignore bpt_t::set_cnd_elang; +%rename (get_manual_regions) py_get_manual_regions; +%ignore set_manual_regions; +%ignore inform_idc_about_debthread; +%ignore is_dbgmem_valid; + +%rename (list_bptgrps) py_list_bptgrps; +%apply qstring *result { qstring *grp_name }; +%ignore qvector::operator==; +%ignore qvector::operator!=; +%ignore qvector::find; +%ignore qvector::has; +%ignore qvector::del; +%ignore qvector::add_unique; +%template(bpt_vec_t) qvector; + +%ignore internal_get_sreg_base; +%rename (internal_get_sreg_base) py_internal_get_sreg_base; + +// We want ALL wrappers around what is declared in dbg.hpp +// to release the GIL when calling into the IDA api: those +// might be very long operations, that even require some +// network traffic. +%thread; +%include "dbg.hpp" +%nothread; +%ignore DBG_Callback; +%feature("director") DBG_Hooks; + +%{ +// +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); +// +%} + +%extend bpt_t +{ + PyObject *condition; + PyObject *elang; +} +%{ +PyObject *bpt_t_condition_get(bpt_t *bpt) +{ + return PyString_FromString(bpt->cndbody.c_str()); +} + +void bpt_t_condition_set(bpt_t *bpt, PyObject *val) +{ + if ( PyString_Check(val) ) + bpt->cndbody = PyString_AsString(val); + else + PyErr_SetString(PyExc_ValueError, "expected a string"); +} + +PyObject *bpt_t_elang_get(bpt_t *bpt) +{ + return PyString_FromString(bpt->get_cnd_elang()); +} + +void bpt_t_elang_set(bpt_t *bpt, PyObject *val) +{ + if ( PyString_Check(val) ) + { + char *cval = PyString_AsString(val); + if ( !bpt->set_cnd_elang(cval) ) + PyErr_SetString(PyExc_ValueError, "too many extlangs"); + } + else + { + PyErr_SetString(PyExc_ValueError, "expected a string"); + } +} +%} +%inline %{ + +// + +//------------------------------------------------------------------------- +/* +# +def get_manual_regions(): + """ + Returns the manual memory regions + @return: list(startEA, endEA, name, sclass, sbase, bitness, perm) + """ + pass +# +*/ +static PyObject *py_get_manual_regions() +{ + meminfo_vec_t areas; + get_manual_regions(&areas); + return meminfo_vec_t_to_py(areas); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_is_loaded(): + """ + Checks if a debugger is loaded + @return: Boolean + """ + pass +# +*/ +static bool dbg_is_loaded() +{ + return dbg != NULL; +} + +//------------------------------------------------------------------------- +/* +# +def refresh_debugger_memory(): + """ + Refreshes the debugger memory + @return: Nothing + """ + pass +# +*/ +static PyObject *refresh_debugger_memory() +{ + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, 0); + + // Ask the debugger to populate debug names + if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) + dbg->stopped_at_debug_event(true); + + // Invalidate the cache + isEnabled(0); + + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; +} + +int idaapi DBG_Callback(void *ud, int notification_code, va_list va); +class DBG_Hooks +{ +public: + virtual ~DBG_Hooks() { unhook(); } + + bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); } + bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); } + /* Hook functions to be overridden in Python */ + virtual void dbg_process_start(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) {} + virtual void dbg_process_exit(pid_t pid, + thid_t tid, + ea_t ea, + int exit_code) {} + virtual void dbg_process_attach(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) {} + virtual void dbg_process_detach(pid_t pid, + thid_t tid, + ea_t ea) {} + virtual void dbg_thread_start(pid_t pid, + thid_t tid, + ea_t ea) {} + virtual void dbg_thread_exit(pid_t pid, + thid_t tid, + ea_t ea, + int exit_code) {} + virtual void dbg_library_load(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) {} + virtual void dbg_library_unload(pid_t pid, + thid_t tid, + ea_t ea, + char *libname) {} + virtual void dbg_information(pid_t pid, + thid_t tid, + ea_t ea, + char *info) {} + virtual int dbg_exception(pid_t pid, + thid_t tid, + ea_t ea, + int code, + bool can_cont, + ea_t exc_ea, + char *info) { return 0; } + virtual void dbg_suspend_process(void) {} + virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; } + virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; } + virtual void dbg_request_error(int failed_command, + int failed_dbg_notification) {} + virtual void dbg_step_into(void) {} + virtual void dbg_step_over(void) {} + virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {} + virtual void dbg_step_until_ret(void) {} +}; + +int idaapi DBG_Callback(void *ud, int notification_code, va_list va) +{ + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + class DBG_Hooks *proxy = (class DBG_Hooks *)ud; + debug_event_t *event; + int code = 0; + + try + { + switch (notification_code) + { + case dbg_process_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_start(event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + break; + + case dbg_process_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_exit( + event->pid, + event->tid, + event->ea, + event->exit_code); + break; + + case dbg_process_attach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_attach( + event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + break; + + case dbg_process_detach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_detach( + event->pid, + event->tid, + event->ea); + break; + + case dbg_thread_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_start( + event->pid, + event->tid, + event->ea); + break; + + case dbg_thread_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_exit( + event->pid, + event->tid, + event->ea, + event->exit_code); + break; + + case dbg_library_load: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_load( + event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + break; + + case dbg_library_unload: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_unload( + event->pid, + event->tid, + event->ea, + event->info); + break; + + case dbg_information: + event = va_arg(va, debug_event_t *); + proxy->dbg_information( + event->pid, + event->tid, + event->ea, + event->info); + break; + + case dbg_exception: + { + event = va_arg(va, debug_event_t *); + int *warn = va_arg(va, int *); + *warn = proxy->dbg_exception( + event->pid, + event->tid, + event->ea, + event->exc.code, + event->exc.can_cont, + event->exc.ea, + event->exc.info); + break; + } + + case dbg_suspend_process: + proxy->dbg_suspend_process(); + break; + + case dbg_bpt: + { + thid_t tid = va_arg(va, thid_t); + ea_t breakpoint_ea = va_arg(va, ea_t); + int *warn = va_arg(va, int *); + *warn = proxy->dbg_bpt(tid, breakpoint_ea); + break; + } + + case dbg_trace: + { + thid_t tid = va_arg(va, thid_t); + ea_t ip = va_arg(va, ea_t); + code = proxy->dbg_trace(tid, ip); + break; + } + + case dbg_request_error: + { + int failed_command = (int)va_argi(va, ui_notification_t); + int failed_dbg_notification = (int)va_argi(va, dbg_notification_t); + proxy->dbg_request_error(failed_command, failed_dbg_notification); + break; + } + + case dbg_step_into: + proxy->dbg_step_into(); + break; + + case dbg_step_over: + proxy->dbg_step_over(); + break; + + case dbg_run_to: + event = va_arg(va, debug_event_t *); + proxy->dbg_run_to( + event->pid, + event->tid, + event->ea); + break; + + case dbg_step_until_ret: + proxy->dbg_step_until_ret(); + break; + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in DBG Hook function: %s\n", e.getMessage()); + if (PyErr_Occurred()) + PyErr_Print(); + } + return code; +} + +//------------------------------------------------------------------------ +/* +# +def py_list_bptgrps(): + """ + Returns list of breakpoint group names + @return: A list of strings or None on failure + """ + pass +# +*/ +static PyObject *py_list_bptgrps() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstrvec_t args; + if ( list_bptgrps(&args) == 0 ) + Py_RETURN_NONE; + return qstrvec2pylist(args); +} + +//------------------------------------------------------------------------ +/* +# +def move_bpt_to_grp(): + """ + Sets new group for the breakpoint + """ + pass +# +*/ +static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + set_bpt_group(*bpt, grp_name); +} + +/* +# +def internal_get_sreg_base(): + """ + Get the sreg base, for the given thread. + + @return: The sreg base, or BADADDR on failure. + """ + pass +# +*/ +static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ea_t answer; + return internal_get_sreg_base(tid, sreg_value, &answer) < 1 + ? BADADDR + : answer; +} + +// + +%} diff --git a/swig/diskio.i b/swig/diskio.i index 0a505e02..6f77fbfb 100644 --- a/swig/diskio.i +++ b/swig/diskio.i @@ -1,530 +1,530 @@ -// TODO: These could be wrapped -%ignore enumerate_files; -%rename (enumerate_files) py_enumerate_files; -%ignore enumerate_system_files; -%ignore enumerate_sorted_files; -%ignore ioport_bit_t; -%ignore ioport_bits_t; -%ignore ioport_t; -%ignore read_ioports; -%ignore choose_ioport_device; -%ignore find_ioport; -%ignore find_ioport_bit; -%ignore free_ioports; -%ignore lread; -%ignore qlread; -%ignore efilelength; -%ignore qlgets; -%ignore qlgetc; -%ignore lreadbytes; -%ignore lread2bytes; -%ignore lread2bytes; -%ignore lread4bytes; -%ignore lread4bytes; -%ignore lread8bytes; -%ignore lread8bytes; -%ignore qlsize; -%ignore qlseek; -%ignore qltell; -%ignore qlfile; -%ignore make_linput; -%ignore unmake_linput; -%ignore create_remote_linput; -%ignore make_filehandle_linput; - -// FIXME: These should be wrapped for completeness -%ignore eread; -%ignore ewrite; - -// Ignore kernel-only & unexported symbols -%ignore get_thread_priority; -%ignore set_thread_priority; -%ignore checkdspace; -%ignore lowdiskgo; -%ignore ida_argv; -%ignore exename; - -%include "diskio.hpp" - -%{ -// -//-------------------------------------------------------------------------- -int idaapi py_enumerate_files_cb(const char *file, void *ud) -{ - // No need to 'PYW_GIL_GET' here, as this is called synchronously - // and from the same thread as the one that executes - // 'py_enumerate_files'. - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_file(PyString_FromString(file)); - newref_t py_ret( - PyObject_CallFunctionObjArgs( - (PyObject *)ud, - py_file.o, - NULL)); - return (py_ret == NULL || !PyNumber_Check(py_ret.o)) ? 1 /* stop enum on failure */ : PyInt_AsLong(py_ret.o); -} -// -%} - -%inline %{ -// -/* -# -class loader_input_t(pyidc_opaque_object_t): - """A helper class to work with linput_t related functions. - This class is also used by file loaders scripts. - """ - def __init__(self): - pass - - def close(self): - """Closes the file""" - pass - - def open(self, filename, remote = False): - """Opens a file (or a remote file) - @return: Boolean - """ - pass - - def set_linput(self, linput): - """Links the current loader_input_t instance to a linput_t instance""" - pass - - @staticmethod - def from_fp(fp): - """A static method to construct an instance from a FILE*""" - pass - - def open_memory(self, start, size): - """Create a linput for process memory (By internally calling idaapi.create_memory_linput()) - This linput will use dbg->read_memory() to read data - @param start: starting address of the input - @param size: size of the memory area to represent as linput - if unknown, may be passed as 0 - """ - pass - - def seek(self, pos, whence = SEEK_SET): - """Set input source position - @return: the new position (not 0 as fseek!) - """ - pass - - def tell(self): - """Returns the current position""" - pass - - def getz(self, sz, fpos = -1): - """Returns a zero terminated string at the given position - @param sz: maximum size of the string - @param fpos: if != -1 then seek will be performed before reading - @return: The string or None on failure. - """ - pass - - def gets(self, len): - """Reads a line from the input file. Returns the read line or None""" - pass - - def read(self, size): - """Reads from the file. Returns the buffer or None""" - pass - - def readbytes(self, size, big_endian): - """Similar to read() but it respect the endianness""" - pass - - def file2base(self, pos, ea1, ea2, patchable): - """ - Load portion of file into the database - This function will include (ea1..ea2) into the addressing space of the - program (make it enabled) - @param li: pointer ot input source - @param pos: position in the file - @param (ea1..ea2): range of destination linear addresses - @param patchable: should the kernel remember correspondance of - file offsets to linear addresses. - @return: 1-ok,0-read error, a warning is displayed - """ - pass - - def get_char(self): - """Reads a single character from the file. Returns None if EOF or the read character""" - pass - - def opened(self): - """Checks if the file is opened or not""" - pass -# -*/ -class loader_input_t -{ -private: - linput_t *li; - int own; - qstring fn; - enum - { - OWN_NONE = 0, // li not created yet - OWN_CREATE = 1, // Owns li because we created it - OWN_FROM_LI = 2, // No ownership we borrowed the li from another class - OWN_FROM_FP = 3, // We got an li instance from an fp instance, we have to unmake_linput() on Close - }; - - //-------------------------------------------------------------------------- - void _from_cobject(PyObject *pycobject) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); - } - - //-------------------------------------------------------------------------- - void assign(const loader_input_t &rhs) - { - fn = rhs.fn; - li = rhs.li; - own = OWN_FROM_LI; - } - - //-------------------------------------------------------------------------- - loader_input_t(const loader_input_t &rhs) - { - assign(rhs); - } -public: - // Special attribute that tells the pyvar_to_idcvar how to convert this - // class from and to IDC. The value of this variable must be set to two - int __idc_cvt_id__; - //-------------------------------------------------------------------------- - loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( pycobject != NULL && PyCObject_Check(pycobject) ) - _from_cobject(pycobject); - } - - //-------------------------------------------------------------------------- - void close() - { - if ( li == NULL ) - return; - - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - if ( own == OWN_CREATE ) - close_linput(li); - else if ( own == OWN_FROM_FP ) - unmake_linput(li); - Py_END_ALLOW_THREADS; - li = NULL; - own = OWN_NONE; - } - - //-------------------------------------------------------------------------- - ~loader_input_t() - { - close(); - } - - //-------------------------------------------------------------------------- - bool open(const char *filename, bool remote = false) - { - close(); - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - li = open_linput(filename, remote); - if ( li != NULL ) - { - // Save file name - fn = filename; - own = OWN_CREATE; - } - Py_END_ALLOW_THREADS; - return li != NULL; - } - - //-------------------------------------------------------------------------- - void set_linput(linput_t *linput) - { - close(); - own = OWN_FROM_LI; - li = linput; - fn.sprnt("", linput); - } - - //-------------------------------------------------------------------------- - static loader_input_t *from_linput(linput_t *linput) - { - loader_input_t *l = new loader_input_t(); - l->set_linput(linput); - return l; - } - - //-------------------------------------------------------------------------- - // This method can be used to pass a linput_t* from C code - static loader_input_t *from_cobject(PyObject *pycobject) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(pycobject) ) - return NULL; - loader_input_t *l = new loader_input_t(); - l->_from_cobject(pycobject); - return l; - } - - //-------------------------------------------------------------------------- - static loader_input_t *from_fp(FILE *fp) - { - PYW_GIL_GET; - loader_input_t *l = NULL; - Py_BEGIN_ALLOW_THREADS; - linput_t *fp_li = make_linput(fp); - if ( fp_li != NULL ) - { - l = new loader_input_t(); - l->own = OWN_FROM_FP; - l->fn.sprnt("", fp); - l->li = fp_li; - } - Py_END_ALLOW_THREADS; - return l; - } - - //-------------------------------------------------------------------------- - linput_t *get_linput() - { - return li; - } - - //-------------------------------------------------------------------------- - bool open_memory(ea_t start, asize_t size = 0) - { - PYW_GIL_GET; - linput_t *l; - Py_BEGIN_ALLOW_THREADS; - l = create_memory_linput(start, size); - if ( l != NULL ) - { - close(); - li = l; - fn = ""; - own = OWN_CREATE; - } - Py_END_ALLOW_THREADS; - return l != NULL; - } - - //-------------------------------------------------------------------------- - int32 seek(int32 pos, int whence = SEEK_SET) - { - int32 r; - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - r = qlseek(li, pos, whence); - Py_END_ALLOW_THREADS; - return r; - } - - //-------------------------------------------------------------------------- - int32 tell() - { - int32 r; - PYW_GIL_GET; - Py_BEGIN_ALLOW_THREADS; - r = qltell(li); - Py_END_ALLOW_THREADS; - return r; - } - - //-------------------------------------------------------------------------- - PyObject *getz(size_t sz, int32 fpos = -1) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(sz + 5); - if ( buf == NULL ) - break; - Py_BEGIN_ALLOW_THREADS; - qlgetz(li, fpos, buf, sz); - Py_END_ALLOW_THREADS; - PyObject *ret = PyString_FromString(buf); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *gets(size_t len) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(len + 5); - if ( buf == NULL ) - break; - bool ok; - Py_BEGIN_ALLOW_THREADS; - ok = qlgets(buf, len, li) != NULL; - Py_END_ALLOW_THREADS; - if ( !ok ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromString(buf); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *read(size_t size) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - ssize_t r; - Py_BEGIN_ALLOW_THREADS; - r = qlread(li, buf, size); - Py_END_ALLOW_THREADS; - if ( r == -1 ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - bool opened() - { - return li != NULL; - } - - //-------------------------------------------------------------------------- - PyObject *readbytes(size_t size, bool big_endian) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - int r; - Py_BEGIN_ALLOW_THREADS; - r = lreadbytes(li, buf, size, big_endian); - Py_END_ALLOW_THREADS; - if ( r == -1 ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) - { - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = ::file2base(li, pos, ea1, ea2, patchable); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int32 size() - { - int32 rc; - Py_BEGIN_ALLOW_THREADS; - rc = qlsize(li); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - PyObject *filename() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyString_FromString(fn.c_str()); - } - - //-------------------------------------------------------------------------- - PyObject *get_char() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int ch; - Py_BEGIN_ALLOW_THREADS; - ch = qlgetc(li); - Py_END_ALLOW_THREADS; - if ( ch == EOF ) - Py_RETURN_NONE; - return Py_BuildValue("c", ch); - } -}; - - -//-------------------------------------------------------------------------- -/* -# -def enumerate_files(path, fname, callback): - """ - Enumerate files in the specified directory while the callback returns 0. - @param path: directory to enumerate files in - @param fname: mask of file names to enumerate - @param callback: a callable object that takes the filename as - its first argument and it returns 0 to continue - enumeration or non-zero to stop enumeration. - @return: - None in case of script errors - tuple(code, fname) : If the callback returns non-zero - """ - pass -# -*/ -PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - do - { - if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) - break; - - const char *_path = PyString_AsString(path); - const char *_fname = PyString_AsString(fname); - if ( _path == NULL || _fname == NULL ) - break; - - char answer[MAXSTR]; - answer[0] = '\0'; - int r = enumerate_files(answer, sizeof(answer), _path, _fname, py_enumerate_files_cb, callback); - return Py_BuildValue("(is)", r, answer); - } while ( false ); - Py_RETURN_NONE; -} -// -%} - -%pythoncode %{ -# -def enumerate_system_files(subdir, fname, callback): - """Similar to enumerate_files() however it searches inside IDA directory or its subdirectories""" - return enumerate_files(idadir(subdir), fname, callback) -# -%} +// TODO: These could be wrapped +%ignore enumerate_files; +%rename (enumerate_files) py_enumerate_files; +%ignore enumerate_system_files; +%ignore enumerate_sorted_files; +%ignore ioport_bit_t; +%ignore ioport_bits_t; +%ignore ioport_t; +%ignore read_ioports; +%ignore choose_ioport_device; +%ignore find_ioport; +%ignore find_ioport_bit; +%ignore free_ioports; +%ignore lread; +%ignore qlread; +%ignore efilelength; +%ignore qlgets; +%ignore qlgetc; +%ignore lreadbytes; +%ignore lread2bytes; +%ignore lread2bytes; +%ignore lread4bytes; +%ignore lread4bytes; +%ignore lread8bytes; +%ignore lread8bytes; +%ignore qlsize; +%ignore qlseek; +%ignore qltell; +%ignore qlfile; +%ignore make_linput; +%ignore unmake_linput; +%ignore create_remote_linput; +%ignore make_filehandle_linput; + +// FIXME: These should be wrapped for completeness +%ignore eread; +%ignore ewrite; + +// Ignore kernel-only & unexported symbols +%ignore get_thread_priority; +%ignore set_thread_priority; +%ignore checkdspace; +%ignore lowdiskgo; +%ignore ida_argv; +%ignore exename; + +%include "diskio.hpp" + +%{ +// +//-------------------------------------------------------------------------- +int idaapi py_enumerate_files_cb(const char *file, void *ud) +{ + // No need to 'PYW_GIL_GET' here, as this is called synchronously + // and from the same thread as the one that executes + // 'py_enumerate_files'. + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_file(PyString_FromString(file)); + newref_t py_ret( + PyObject_CallFunctionObjArgs( + (PyObject *)ud, + py_file.o, + NULL)); + return (py_ret == NULL || !PyNumber_Check(py_ret.o)) ? 1 /* stop enum on failure */ : PyInt_AsLong(py_ret.o); +} +// +%} + +%inline %{ +// +/* +# +class loader_input_t(pyidc_opaque_object_t): + """A helper class to work with linput_t related functions. + This class is also used by file loaders scripts. + """ + def __init__(self): + pass + + def close(self): + """Closes the file""" + pass + + def open(self, filename, remote = False): + """Opens a file (or a remote file) + @return: Boolean + """ + pass + + def set_linput(self, linput): + """Links the current loader_input_t instance to a linput_t instance""" + pass + + @staticmethod + def from_fp(fp): + """A static method to construct an instance from a FILE*""" + pass + + def open_memory(self, start, size): + """Create a linput for process memory (By internally calling idaapi.create_memory_linput()) + This linput will use dbg->read_memory() to read data + @param start: starting address of the input + @param size: size of the memory area to represent as linput + if unknown, may be passed as 0 + """ + pass + + def seek(self, pos, whence = SEEK_SET): + """Set input source position + @return: the new position (not 0 as fseek!) + """ + pass + + def tell(self): + """Returns the current position""" + pass + + def getz(self, sz, fpos = -1): + """Returns a zero terminated string at the given position + @param sz: maximum size of the string + @param fpos: if != -1 then seek will be performed before reading + @return: The string or None on failure. + """ + pass + + def gets(self, len): + """Reads a line from the input file. Returns the read line or None""" + pass + + def read(self, size): + """Reads from the file. Returns the buffer or None""" + pass + + def readbytes(self, size, big_endian): + """Similar to read() but it respect the endianness""" + pass + + def file2base(self, pos, ea1, ea2, patchable): + """ + Load portion of file into the database + This function will include (ea1..ea2) into the addressing space of the + program (make it enabled) + @param li: pointer ot input source + @param pos: position in the file + @param (ea1..ea2): range of destination linear addresses + @param patchable: should the kernel remember correspondance of + file offsets to linear addresses. + @return: 1-ok,0-read error, a warning is displayed + """ + pass + + def get_char(self): + """Reads a single character from the file. Returns None if EOF or the read character""" + pass + + def opened(self): + """Checks if the file is opened or not""" + pass +# +*/ +class loader_input_t +{ +private: + linput_t *li; + int own; + qstring fn; + enum + { + OWN_NONE = 0, // li not created yet + OWN_CREATE = 1, // Owns li because we created it + OWN_FROM_LI = 2, // No ownership we borrowed the li from another class + OWN_FROM_FP = 3, // We got an li instance from an fp instance, we have to unmake_linput() on Close + }; + + //-------------------------------------------------------------------------- + void _from_cobject(PyObject *pycobject) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); + } + + //-------------------------------------------------------------------------- + void assign(const loader_input_t &rhs) + { + fn = rhs.fn; + li = rhs.li; + own = OWN_FROM_LI; + } + + //-------------------------------------------------------------------------- + loader_input_t(const loader_input_t &rhs) + { + assign(rhs); + } +public: + // Special attribute that tells the pyvar_to_idcvar how to convert this + // class from and to IDC. The value of this variable must be set to two + int __idc_cvt_id__; + //-------------------------------------------------------------------------- + loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( pycobject != NULL && PyCObject_Check(pycobject) ) + _from_cobject(pycobject); + } + + //-------------------------------------------------------------------------- + void close() + { + if ( li == NULL ) + return; + + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + if ( own == OWN_CREATE ) + close_linput(li); + else if ( own == OWN_FROM_FP ) + unmake_linput(li); + Py_END_ALLOW_THREADS; + li = NULL; + own = OWN_NONE; + } + + //-------------------------------------------------------------------------- + ~loader_input_t() + { + close(); + } + + //-------------------------------------------------------------------------- + bool open(const char *filename, bool remote = false) + { + close(); + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + li = open_linput(filename, remote); + if ( li != NULL ) + { + // Save file name + fn = filename; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return li != NULL; + } + + //-------------------------------------------------------------------------- + void set_linput(linput_t *linput) + { + close(); + own = OWN_FROM_LI; + li = linput; + fn.sprnt("", linput); + } + + //-------------------------------------------------------------------------- + static loader_input_t *from_linput(linput_t *linput) + { + loader_input_t *l = new loader_input_t(); + l->set_linput(linput); + return l; + } + + //-------------------------------------------------------------------------- + // This method can be used to pass a linput_t* from C code + static loader_input_t *from_cobject(PyObject *pycobject) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(pycobject) ) + return NULL; + loader_input_t *l = new loader_input_t(); + l->_from_cobject(pycobject); + return l; + } + + //-------------------------------------------------------------------------- + static loader_input_t *from_fp(FILE *fp) + { + PYW_GIL_GET; + loader_input_t *l = NULL; + Py_BEGIN_ALLOW_THREADS; + linput_t *fp_li = make_linput(fp); + if ( fp_li != NULL ) + { + l = new loader_input_t(); + l->own = OWN_FROM_FP; + l->fn.sprnt("", fp); + l->li = fp_li; + } + Py_END_ALLOW_THREADS; + return l; + } + + //-------------------------------------------------------------------------- + linput_t *get_linput() + { + return li; + } + + //-------------------------------------------------------------------------- + bool open_memory(ea_t start, asize_t size = 0) + { + PYW_GIL_GET; + linput_t *l; + Py_BEGIN_ALLOW_THREADS; + l = create_memory_linput(start, size); + if ( l != NULL ) + { + close(); + li = l; + fn = ""; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return l != NULL; + } + + //-------------------------------------------------------------------------- + int32 seek(int32 pos, int whence = SEEK_SET) + { + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qlseek(li, pos, whence); + Py_END_ALLOW_THREADS; + return r; + } + + //-------------------------------------------------------------------------- + int32 tell() + { + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qltell(li); + Py_END_ALLOW_THREADS; + return r; + } + + //-------------------------------------------------------------------------- + PyObject *getz(size_t sz, int32 fpos = -1) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(sz + 5); + if ( buf == NULL ) + break; + Py_BEGIN_ALLOW_THREADS; + qlgetz(li, fpos, buf, sz); + Py_END_ALLOW_THREADS; + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *gets(size_t len) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(len + 5); + if ( buf == NULL ) + break; + bool ok; + Py_BEGIN_ALLOW_THREADS; + ok = qlgets(buf, len, li) != NULL; + Py_END_ALLOW_THREADS; + if ( !ok ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *read(size_t size) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + ssize_t r; + Py_BEGIN_ALLOW_THREADS; + r = qlread(li, buf, size); + Py_END_ALLOW_THREADS; + if ( r == -1 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + bool opened() + { + return li != NULL; + } + + //-------------------------------------------------------------------------- + PyObject *readbytes(size_t size, bool big_endian) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + int r; + Py_BEGIN_ALLOW_THREADS; + r = lreadbytes(li, buf, size, big_endian); + Py_END_ALLOW_THREADS; + if ( r == -1 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) + { + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = ::file2base(li, pos, ea1, ea2, patchable); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int32 size() + { + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qlsize(li); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + PyObject *filename() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyString_FromString(fn.c_str()); + } + + //-------------------------------------------------------------------------- + PyObject *get_char() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qlgetc(li); + Py_END_ALLOW_THREADS; + if ( ch == EOF ) + Py_RETURN_NONE; + return Py_BuildValue("c", ch); + } +}; + + +//-------------------------------------------------------------------------- +/* +# +def enumerate_files(path, fname, callback): + """ + Enumerate files in the specified directory while the callback returns 0. + @param path: directory to enumerate files in + @param fname: mask of file names to enumerate + @param callback: a callable object that takes the filename as + its first argument and it returns 0 to continue + enumeration or non-zero to stop enumeration. + @return: + None in case of script errors + tuple(code, fname) : If the callback returns non-zero + """ + pass +# +*/ +PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + do + { + if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) + break; + + const char *_path = PyString_AsString(path); + const char *_fname = PyString_AsString(fname); + if ( _path == NULL || _fname == NULL ) + break; + + char answer[MAXSTR]; + answer[0] = '\0'; + int r = enumerate_files(answer, sizeof(answer), _path, _fname, py_enumerate_files_cb, callback); + return Py_BuildValue("(is)", r, answer); + } while ( false ); + Py_RETURN_NONE; +} +// +%} + +%pythoncode %{ +# +def enumerate_system_files(subdir, fname, callback): + """Similar to enumerate_files() however it searches inside IDA directory or its subdirectories""" + return enumerate_files(idadir(subdir), fname, callback) +# +%} diff --git a/swig/enum.i b/swig/enum.i index 83d4745a..8d357f5d 100644 --- a/swig/enum.i +++ b/swig/enum.i @@ -4,7 +4,8 @@ %ignore save_enums; %ignore term_enums; %ignore set_enum_flag; -%ignore sync_from_enum;; +%ignore sync_from_enum; +%ignore get_enum_name(tid_t); %ignore del_all_enum_members; %ignore get_selected_enum; %ignore add_selected_enum; diff --git a/swig/expr.i b/swig/expr.i index 23556c53..316a35cc 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -1,339 +1,339 @@ -%ignore extfun_t; -%ignore funcset_t; -%ignore extlang_t; -%ignore extlang; -%ignore extlangs_t; -%ignore extlangs; -%ignore register_extlang; -%ignore IDCFuncs; -%ignore set_idc_func; -%ignore set_idc_dtor; -%ignore set_idc_method; -%ignore set_idc_getattr; -%ignore set_idc_setattr; -%ignore set_idc_func_ex; -%ignore run_statements_idc; -%ignore VarLong; -%ignore VarNum; -%ignore extlang_get_attr_exists; -%ignore extlang_create_object_exists; -%ignore create_script_object; -%ignore set_script_attr; -%ignore set_attr_exists; -%ignore get_script_attr; -%ignore extlang_get_attr_exists; -%ignore extlang_compile_file; -%ignore get_extlangs; -%ignore create_idc_object; -%ignore run_script_func; -%ignore VarString; -%ignore VarFloat; -%ignore VarFree; -%ignore calcexpr_long; -%ignore Run; -%ignore ExecuteLine; -%ignore ExecuteFile; -%ignore set_idc_func_body; -%ignore get_idc_func_body; -%ignore idc_stacksize; -%ignore idc_calldepth; -%ignore expr_printf; -%ignore expr_uprintf; -%ignore expr_sprintf; -%ignore expr_printfer; -%ignore init_idc; -%ignore term_idc; -%ignore create_default_idc_classes; -%ignore notify_extlang_changed; -%ignore insn_to_idc; -%ignore find_builtin_idc_func; -%ignore idc_mutex; -%ignore idc_lx; -%ignore idc_vars; -%ignore idc_resolve_label; -%ignore idc_resolver_ea; -%ignore setup_lowcnd_regfuncs; -%cstring_output_maxstr_none(char *errbuf, size_t errbufsize); - -%ignore CompileEx; -%rename (CompileEx) CompileEx_wrap; -%ignore Compile; -%rename (Compile) Compile_wrap; -%ignore calcexpr; -%rename (calcexpr) calcexpr_wrap; -%ignore calc_idc_expr; -%rename (calc_idc_expr) calc_idc_expr_wrap; -%ignore CompileLine(const char *line, char *errbuf, size_t errbufsize, uval_t (idaapi*_getname)(const char *name)=NULL); -%ignore CompileLineEx; -%ignore CompileLine; -%rename (CompileLine) CompileLine_wrap; -%{ -// -struct py_idcfunc_ctx_t -{ - PyObject *py_func; - qstring name; - int nargs; - py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_INCREF(py_func); - } - ~py_idcfunc_ctx_t() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_DECREF(py_func); - } -}; - -//--------------------------------------------------------------------------- -static error_t py_call_idc_func( - void *_ctx, - idc_value_t *argv, - idc_value_t *r) -{ - // Convert IDC arguments to Python list - py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; - int cvt; - char errbuf[MAXSTR]; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_vec_t pargs; - if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, true, errbuf, sizeof(errbuf)) ) - { - // Error during conversion? Create an IDC exception - return PyW_CreateIdcException(r, errbuf); - } - - // Call the Python function - newref_t py_result(PyObject_CallObject( - ctx->py_func, - pargs.empty() ? NULL : pargs[0].o)); - - error_t err; - if ( PyW_GetError(errbuf, sizeof(errbuf)) ) - { - err = PyW_CreateIdcException(r, errbuf); - } - else - { - // Convert the result to IDC - r->clear(); - cvt = pyvar_to_idcvar(py_result, r); - if ( cvt < CIP_OK ) - err = PyW_CreateIdcException(r, "ERROR: bad return value"); - else - err = eOk; - } - - return err; -} - -// -%} - -%inline %{ -// - -//--------------------------------------------------------------------------- -static size_t py_get_call_idc_func() -{ - return (size_t)py_call_idc_func; -} - -//--------------------------------------------------------------------------- -// Internal function: -// - capture the python callable -// - return a C context as a numeric value -static size_t pyw_register_idc_func( - const char *name, - const char *args, - PyObject *py_fp) -{ - return (size_t)new py_idcfunc_ctx_t(py_fp, name, strlen(args)); -} - -//--------------------------------------------------------------------------- -// Internal function: -// - free the C context -static bool pyw_unregister_idc_func(size_t ctxptr) -{ - // Unregister the function - py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)ctxptr; - bool ok = set_idc_func_ex(ctx->name.c_str(), NULL, NULL, 0); - - // Delete the context - delete ctx; - - return ok; -} - -//--------------------------------------------------------------------------- -static bool py_set_idc_func_ex( - const char *name, - size_t fp_ptr, - const char *args, - int flags) -{ - return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); -} - -//--------------------------------------------------------------------------- -// Compile* functions return false when error so the return -// value must be negated for the error string to be returned -bool CompileEx_wrap( - const char *file, - bool del_macros, - char *errbuf, size_t errbufsize) -{ - return !CompileEx(file, del_macros, errbuf, errbufsize); -} - -bool Compile_wrap(const char *file, char *errbuf, size_t errbufsize) -{ - return !Compile(file, errbuf, errbufsize); -} - -bool calcexpr_wrap( - ea_t where, - const char *line, - idc_value_t *rv, - char *errbuf, size_t errbufsize) -{ - return !calcexpr(where, line, rv, errbuf, errbufsize); -} - -bool calc_idc_expr_wrap( - ea_t where, - const char *line, - idc_value_t *rv, - char *errbuf, size_t errbufsize) -{ - return !calc_idc_expr(where, line, rv, errbuf, errbufsize); -} - -bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) -{ - return !CompileLineEx(line, errbuf, errbufsize); -} - -// -%} - -%include "expr.hpp" - -%pythoncode %{ - -# -try: - import types - import ctypes - # Callback for IDC func callback (On Windows, we use stdcall) - # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); - _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) - - # A trampoline function that is called from idcfunc_t that will - # call the Python callback with the argv and r properly serialized to python - call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) -except: - def call_idc_func__(*args): - warning("IDC extensions need ctypes library in order to work") - return 0 - try: - _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) - except: - _IDCFUNC_CB_T = None - - -# -------------------------------------------------------------------------- -EXTFUN_BASE = 0x0001 -"""requires open database""" -EXTFUN_NORET = 0x0002 -"""does not return. the interpreter may clean up its state before calling it.""" -EXTFUN_SAFE = 0x0004 -"""thread safe function. may be called""" - -# -------------------------------------------------------------------------- -class _IdcFunction(object): - """ - Internal class that calls pyw_call_idc_func() with a context - """ - def __init__(self, ctxptr): - self.ctxptr = ctxptr - # Take a reference to the ctypes callback - # (note: this will create a circular reference) - self.cb = _IDCFUNC_CB_T(self) - - fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) - - def __call__(self, args, res): - return call_idc_func__(self.ctxptr, args, res) - - -# -------------------------------------------------------------------------- -# Dictionary to remember IDC function names along with the context pointer -# retrieved by using the internal pyw_register_idc_func() -__IDC_FUNC_CTXS = {} - -# -------------------------------------------------------------------------- -def set_idc_func_ex(name, fp=None, args=(), flags=0): - """ - Extends the IDC language by exposing a new IDC function that is backed up by a Python function - This function also unregisters the IDC function if 'fp' was passed as None - - @param name: IDC function name to expose - @param fp: Python callable that will receive the arguments and return a tuple. - If this argument is None then the IDC function is unregistered - @param args: Arguments. A tuple of idaapi.VT_XXX constants - @param flags: IDC function flags. A combination of EXTFUN_XXX constants - - @return: Boolean. - """ - global __IDC_FUNC_CTXS - - # Get the context - f = __IDC_FUNC_CTXS.get(name, None) - - # Unregistering? - if fp is None: - # Not registered? - if f is None: - return False - - # Break circular reference - del f.cb - - # Delete the name from the dictionary - del __IDC_FUNC_CTXS[name] - - # Delete the context and unregister the function - return _idaapi.pyw_unregister_idc_func(f.ctxptr) - - # Registering a function that is already registered? - if f is not None: - # Unregister it first - set_idc_func_ex(name, None) - - # Convert the tupple argument info to a string - args = "".join([chr(x) for x in args]) - - # Create a context - ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) - if ctxptr == 0: - return False - - # Bind the context with the IdcFunc object - f = _IdcFunction(ctxptr) - - # Remember the Python context - __IDC_FUNC_CTXS[name] = f - - # Register IDC function with a callback - return _idaapi.py_set_idc_func_ex( - name, - f.fp_ptr, - args, - flags) - -# +%ignore extfun_t; +%ignore funcset_t; +%ignore extlang_t; +%ignore extlang; +%ignore extlangs_t; +%ignore extlangs; +%ignore register_extlang; +%ignore IDCFuncs; +%ignore set_idc_func; +%ignore set_idc_dtor; +%ignore set_idc_method; +%ignore set_idc_getattr; +%ignore set_idc_setattr; +%ignore set_idc_func_ex; +%ignore run_statements_idc; +%ignore VarLong; +%ignore VarNum; +%ignore extlang_get_attr_exists; +%ignore extlang_create_object_exists; +%ignore create_script_object; +%ignore set_script_attr; +%ignore set_attr_exists; +%ignore get_script_attr; +%ignore extlang_get_attr_exists; +%ignore extlang_compile_file; +%ignore get_extlangs; +%ignore create_idc_object; +%ignore run_script_func; +%ignore VarString; +%ignore VarFloat; +%ignore VarFree; +%ignore calcexpr_long; +%ignore Run; +%ignore ExecuteLine; +%ignore ExecuteFile; +%ignore set_idc_func_body; +%ignore get_idc_func_body; +%ignore idc_stacksize; +%ignore idc_calldepth; +%ignore expr_printf; +%ignore expr_uprintf; +%ignore expr_sprintf; +%ignore expr_printfer; +%ignore init_idc; +%ignore term_idc; +%ignore create_default_idc_classes; +%ignore notify_extlang_changed; +%ignore insn_to_idc; +%ignore find_builtin_idc_func; +%ignore idc_mutex; +%ignore idc_lx; +%ignore idc_vars; +%ignore idc_resolve_label; +%ignore idc_resolver_ea; +%ignore setup_lowcnd_regfuncs; +%cstring_output_maxstr_none(char *errbuf, size_t errbufsize); + +%ignore CompileEx; +%rename (CompileEx) CompileEx_wrap; +%ignore Compile; +%rename (Compile) Compile_wrap; +%ignore calcexpr; +%rename (calcexpr) calcexpr_wrap; +%ignore calc_idc_expr; +%rename (calc_idc_expr) calc_idc_expr_wrap; +%ignore CompileLine(const char *line, char *errbuf, size_t errbufsize, uval_t (idaapi*_getname)(const char *name)=NULL); +%ignore CompileLineEx; +%ignore CompileLine; +%rename (CompileLine) CompileLine_wrap; +%{ +// +struct py_idcfunc_ctx_t +{ + PyObject *py_func; + qstring name; + int nargs; + py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_INCREF(py_func); + } + ~py_idcfunc_ctx_t() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_DECREF(py_func); + } +}; + +//--------------------------------------------------------------------------- +static error_t py_call_idc_func( + void *_ctx, + idc_value_t *argv, + idc_value_t *r) +{ + // Convert IDC arguments to Python list + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; + int cvt; + char errbuf[MAXSTR]; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_vec_t pargs; + if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, true, errbuf, sizeof(errbuf)) ) + { + // Error during conversion? Create an IDC exception + return PyW_CreateIdcException(r, errbuf); + } + + // Call the Python function + newref_t py_result(PyObject_CallObject( + ctx->py_func, + pargs.empty() ? NULL : pargs[0].o)); + + error_t err; + if ( PyW_GetError(errbuf, sizeof(errbuf)) ) + { + err = PyW_CreateIdcException(r, errbuf); + } + else + { + // Convert the result to IDC + r->clear(); + cvt = pyvar_to_idcvar(py_result, r); + if ( cvt < CIP_OK ) + err = PyW_CreateIdcException(r, "ERROR: bad return value"); + else + err = eOk; + } + + return err; +} + +// +%} + +%inline %{ +// + +//--------------------------------------------------------------------------- +static size_t py_get_call_idc_func() +{ + return (size_t)py_call_idc_func; +} + +//--------------------------------------------------------------------------- +// Internal function: +// - capture the python callable +// - return a C context as a numeric value +static size_t pyw_register_idc_func( + const char *name, + const char *args, + PyObject *py_fp) +{ + return (size_t)new py_idcfunc_ctx_t(py_fp, name, strlen(args)); +} + +//--------------------------------------------------------------------------- +// Internal function: +// - free the C context +static bool pyw_unregister_idc_func(size_t ctxptr) +{ + // Unregister the function + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)ctxptr; + bool ok = set_idc_func_ex(ctx->name.c_str(), NULL, NULL, 0); + + // Delete the context + delete ctx; + + return ok; +} + +//--------------------------------------------------------------------------- +static bool py_set_idc_func_ex( + const char *name, + size_t fp_ptr, + const char *args, + int flags) +{ + return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); +} + +//--------------------------------------------------------------------------- +// Compile* functions return false when error so the return +// value must be negated for the error string to be returned +bool CompileEx_wrap( + const char *file, + bool del_macros, + char *errbuf, size_t errbufsize) +{ + return !CompileEx(file, del_macros, errbuf, errbufsize); +} + +bool Compile_wrap(const char *file, char *errbuf, size_t errbufsize) +{ + return !Compile(file, errbuf, errbufsize); +} + +bool calcexpr_wrap( + ea_t where, + const char *line, + idc_value_t *rv, + char *errbuf, size_t errbufsize) +{ + return !calcexpr(where, line, rv, errbuf, errbufsize); +} + +bool calc_idc_expr_wrap( + ea_t where, + const char *line, + idc_value_t *rv, + char *errbuf, size_t errbufsize) +{ + return !calc_idc_expr(where, line, rv, errbuf, errbufsize); +} + +bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) +{ + return !CompileLineEx(line, errbuf, errbufsize); +} + +// +%} + +%include "expr.hpp" + +%pythoncode %{ + +# +try: + import types + import ctypes + # Callback for IDC func callback (On Windows, we use stdcall) + # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); + _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) + + # A trampoline function that is called from idcfunc_t that will + # call the Python callback with the argv and r properly serialized to python + call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) +except: + def call_idc_func__(*args): + warning("IDC extensions need ctypes library in order to work") + return 0 + try: + _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) + except: + _IDCFUNC_CB_T = None + + +# -------------------------------------------------------------------------- +EXTFUN_BASE = 0x0001 +"""requires open database""" +EXTFUN_NORET = 0x0002 +"""does not return. the interpreter may clean up its state before calling it.""" +EXTFUN_SAFE = 0x0004 +"""thread safe function. may be called""" + +# -------------------------------------------------------------------------- +class _IdcFunction(object): + """ + Internal class that calls pyw_call_idc_func() with a context + """ + def __init__(self, ctxptr): + self.ctxptr = ctxptr + # Take a reference to the ctypes callback + # (note: this will create a circular reference) + self.cb = _IDCFUNC_CB_T(self) + + fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) + + def __call__(self, args, res): + return call_idc_func__(self.ctxptr, args, res) + + +# -------------------------------------------------------------------------- +# Dictionary to remember IDC function names along with the context pointer +# retrieved by using the internal pyw_register_idc_func() +__IDC_FUNC_CTXS = {} + +# -------------------------------------------------------------------------- +def set_idc_func_ex(name, fp=None, args=(), flags=0): + """ + Extends the IDC language by exposing a new IDC function that is backed up by a Python function + This function also unregisters the IDC function if 'fp' was passed as None + + @param name: IDC function name to expose + @param fp: Python callable that will receive the arguments and return a tuple. + If this argument is None then the IDC function is unregistered + @param args: Arguments. A tuple of idaapi.VT_XXX constants + @param flags: IDC function flags. A combination of EXTFUN_XXX constants + + @return: Boolean. + """ + global __IDC_FUNC_CTXS + + # Get the context + f = __IDC_FUNC_CTXS.get(name, None) + + # Unregistering? + if fp is None: + # Not registered? + if f is None: + return False + + # Break circular reference + del f.cb + + # Delete the name from the dictionary + del __IDC_FUNC_CTXS[name] + + # Delete the context and unregister the function + return _idaapi.pyw_unregister_idc_func(f.ctxptr) + + # Registering a function that is already registered? + if f is not None: + # Unregister it first + set_idc_func_ex(name, None) + + # Convert the tupple argument info to a string + args = "".join([chr(x) for x in args]) + + # Create a context + ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) + if ctxptr == 0: + return False + + # Bind the context with the IdcFunc object + f = _IdcFunction(ctxptr) + + # Remember the Python context + __IDC_FUNC_CTXS[name] = f + + # Register IDC function with a callback + return _idaapi.py_set_idc_func_ex( + name, + f.fp_ptr, + args, + flags) + +# %} \ No newline at end of file diff --git a/swig/fpro.i b/swig/fpro.i index 046eac6b..7adaf160 100644 --- a/swig/fpro.i +++ b/swig/fpro.i @@ -1,404 +1,404 @@ -%inline %{ -// -/* -# -class qfile_t(pyidc_opaque_object_t): - """A helper class to work with FILE related functions.""" - def __init__(self): - pass - - def close(self): - """Closes the file""" - pass - - def open(self, filename, mode): - """Opens a file - @param filename: the file name - @param mode: The mode string, ala fopen() style - @return: Boolean - """ - pass - - def set_linput(self, linput): - """Links the current loader_input_t instance to a linput_t instance""" - pass - - @staticmethod - def tmpfile(): - """A static method to construct an instance using a temporary file""" - pass - - def seek(self, pos, whence = SEEK_SET): - """Set input source position - @return: the new position (not 0 as fseek!) - """ - pass - - def tell(self): - """Returns the current position""" - pass - - def gets(self, len): - """Reads a line from the input file. Returns the read line or None""" - pass - - def read(self, size): - """Reads from the file. Returns the buffer or None""" - pass - - def write(self, buf): - """Writes to the file. Returns 0 or the number of bytes written""" - pass - - def readbytes(self, size, big_endian): - """Similar to read() but it respect the endianness""" - pass - - def writebytes(self, size, big_endian): - """Similar to write() but it respect the endianness""" - pass - - def flush(self): - pass - - def get_char(self): - """Reads a single character from the file. Returns None if EOF or the read character""" - pass - - def put_char(self): - """Writes a single character to the file""" - pass - - def opened(self): - """Checks if the file is opened or not""" - pass -# -*/ -class qfile_t -{ -private: - FILE *fp; - bool own; - qstring fn; - - //-------------------------------------------------------------------------- - void assign(const qfile_t &rhs) - { - fn = rhs.fn; - fp = rhs.fp; - own = false; - } - //-------------------------------------------------------------------------- - bool _from_fp(FILE *fp) - { - if ( fp == NULL ) - return false; - own = false; - fn.sprnt("", fp); - this->fp = fp; - return true; - } - inline void _from_cobject(PyObject *pycobject) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); - } -public: - int __idc_cvt_id__; - //-------------------------------------------------------------------------- - qfile_t(const qfile_t &rhs) - { - assign(rhs); - } - - //-------------------------------------------------------------------------- - qfile_t(PyObject *pycobject = NULL) - { - fp = NULL; - own = true; - fn.qclear(); - __idc_cvt_id__ = PY_ICID_OPAQUE; - bool ok; - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - ok = pycobject != NULL && PyCObject_Check(pycobject); - } - if ( ok ) - _from_cobject(pycobject); - } - - //-------------------------------------------------------------------------- - bool opened() - { - return fp != NULL; - } - - //-------------------------------------------------------------------------- - void close() - { - if ( fp == NULL ) - return; - if ( own ) - { - Py_BEGIN_ALLOW_THREADS; - qfclose(fp); - Py_END_ALLOW_THREADS; - } - fp = NULL; - own = true; - } - - //-------------------------------------------------------------------------- - ~qfile_t() - { - close(); - } - - //-------------------------------------------------------------------------- - bool open(const char *filename, const char *mode) - { - close(); - Py_BEGIN_ALLOW_THREADS; - fp = qfopen(filename, mode); - Py_END_ALLOW_THREADS; - if ( fp == NULL ) - return false; - // Save file name - fn = filename; - own = true; - return true; - } - - //-------------------------------------------------------------------------- - static qfile_t *from_fp(FILE *fp) - { - if ( fp == NULL ) - return NULL; - qfile_t *qf = new qfile_t(); - qf->own = false; - qf->fn.sprnt("", fp); - qf->fp = fp; - return qf; - } - - //-------------------------------------------------------------------------- - // This method can be used to pass a FILE* from C code - static qfile_t *from_cobject(PyObject *pycobject) - { - return PyCObject_Check(pycobject) ? from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)) : NULL; - } - - //-------------------------------------------------------------------------- - static qfile_t *tmpfile() - { - FILE *fp; - Py_BEGIN_ALLOW_THREADS; - fp = qtmpfile(); - Py_END_ALLOW_THREADS; - return from_fp(fp); - } - - //-------------------------------------------------------------------------- - FILE *get_fp() - { - return fp; - } - - //-------------------------------------------------------------------------- - int seek(int32 offset, int whence = SEEK_SET) - { - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfseek(fp, offset, whence); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int32 tell() - { - int32 rc; - Py_BEGIN_ALLOW_THREADS; - rc = qftell(fp); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - PyObject *readbytes(int size, bool big_endian) - { - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - PYW_GIL_CHECK_LOCKED_SCOPE(); - int r; - Py_BEGIN_ALLOW_THREADS; - r = freadbytes(fp, buf, size, big_endian); - Py_END_ALLOW_THREADS; - if ( r != 0 ) - { - free(buf); - break; - } - - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *read(int size) - { - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - PYW_GIL_CHECK_LOCKED_SCOPE(); - int r; - Py_BEGIN_ALLOW_THREADS; - r = qfread(fp, buf, size); - Py_END_ALLOW_THREADS; - if ( r <= 0 ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromStringAndSize(buf, r); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - PyObject *gets(int size) - { - do - { - char *buf = (char *) malloc(size + 5); - if ( buf == NULL ) - break; - PYW_GIL_CHECK_LOCKED_SCOPE(); - char *p; - Py_BEGIN_ALLOW_THREADS; - p = qfgets(buf, size, fp); - Py_END_ALLOW_THREADS; - if ( p == NULL ) - { - free(buf); - break; - } - PyObject *ret = PyString_FromString(buf); - free(buf); - return ret; - } while ( false ); - Py_RETURN_NONE; - } - - //-------------------------------------------------------------------------- - int writebytes(PyObject *py_buf, bool big_endian) - { - Py_ssize_t sz; - void *buf; - PYW_GIL_CHECK_LOCKED_SCOPE(); - sz = PyString_GET_SIZE(py_buf); - buf = (void *)PyString_AS_STRING(py_buf); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = fwritebytes(fp, buf, int(sz), big_endian); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int write(PyObject *py_buf) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_buf) ) - return 0; - // Just so that there is no risk that the buffer returned by - // 'PyString_AS_STRING' gets deallocated within the - // Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_buf_ref(py_buf); - void *p = (void *)PyString_AS_STRING(py_buf); - Py_ssize_t sz = PyString_GET_SIZE(py_buf); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfwrite(fp, p, sz); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int puts(const char *str) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfputs(str, fp); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - int32 size() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int32 r; - Py_BEGIN_ALLOW_THREADS; - int pos = qfseek(fp, 0, SEEK_END); - r = qftell(fp); - qfseek(fp, pos, SEEK_SET); - Py_END_ALLOW_THREADS; - return r; - } - - //-------------------------------------------------------------------------- - int flush() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qflush(fp); - Py_END_ALLOW_THREADS; - return rc; - } - - //-------------------------------------------------------------------------- - PyObject *filename() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyString_FromString(fn.c_str()); - } - - //-------------------------------------------------------------------------- - PyObject *get_char() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int ch; - Py_BEGIN_ALLOW_THREADS; - ch = qfgetc(fp); - Py_END_ALLOW_THREADS; - if ( ch == EOF ) - Py_RETURN_NONE; - return Py_BuildValue("c", ch); - } - - //-------------------------------------------------------------------------- - int put_char(char chr) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = qfputc(chr, fp); - Py_END_ALLOW_THREADS; - return rc; - } -}; -// -%} +%inline %{ +// +/* +# +class qfile_t(pyidc_opaque_object_t): + """A helper class to work with FILE related functions.""" + def __init__(self): + pass + + def close(self): + """Closes the file""" + pass + + def open(self, filename, mode): + """Opens a file + @param filename: the file name + @param mode: The mode string, ala fopen() style + @return: Boolean + """ + pass + + def set_linput(self, linput): + """Links the current loader_input_t instance to a linput_t instance""" + pass + + @staticmethod + def tmpfile(): + """A static method to construct an instance using a temporary file""" + pass + + def seek(self, pos, whence = SEEK_SET): + """Set input source position + @return: the new position (not 0 as fseek!) + """ + pass + + def tell(self): + """Returns the current position""" + pass + + def gets(self, len): + """Reads a line from the input file. Returns the read line or None""" + pass + + def read(self, size): + """Reads from the file. Returns the buffer or None""" + pass + + def write(self, buf): + """Writes to the file. Returns 0 or the number of bytes written""" + pass + + def readbytes(self, size, big_endian): + """Similar to read() but it respect the endianness""" + pass + + def writebytes(self, size, big_endian): + """Similar to write() but it respect the endianness""" + pass + + def flush(self): + pass + + def get_char(self): + """Reads a single character from the file. Returns None if EOF or the read character""" + pass + + def put_char(self): + """Writes a single character to the file""" + pass + + def opened(self): + """Checks if the file is opened or not""" + pass +# +*/ +class qfile_t +{ +private: + FILE *fp; + bool own; + qstring fn; + + //-------------------------------------------------------------------------- + void assign(const qfile_t &rhs) + { + fn = rhs.fn; + fp = rhs.fp; + own = false; + } + //-------------------------------------------------------------------------- + bool _from_fp(FILE *fp) + { + if ( fp == NULL ) + return false; + own = false; + fn.sprnt("", fp); + this->fp = fp; + return true; + } + inline void _from_cobject(PyObject *pycobject) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); + } +public: + int __idc_cvt_id__; + //-------------------------------------------------------------------------- + qfile_t(const qfile_t &rhs) + { + assign(rhs); + } + + //-------------------------------------------------------------------------- + qfile_t(PyObject *pycobject = NULL) + { + fp = NULL; + own = true; + fn.qclear(); + __idc_cvt_id__ = PY_ICID_OPAQUE; + bool ok; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + ok = pycobject != NULL && PyCObject_Check(pycobject); + } + if ( ok ) + _from_cobject(pycobject); + } + + //-------------------------------------------------------------------------- + bool opened() + { + return fp != NULL; + } + + //-------------------------------------------------------------------------- + void close() + { + if ( fp == NULL ) + return; + if ( own ) + { + Py_BEGIN_ALLOW_THREADS; + qfclose(fp); + Py_END_ALLOW_THREADS; + } + fp = NULL; + own = true; + } + + //-------------------------------------------------------------------------- + ~qfile_t() + { + close(); + } + + //-------------------------------------------------------------------------- + bool open(const char *filename, const char *mode) + { + close(); + Py_BEGIN_ALLOW_THREADS; + fp = qfopen(filename, mode); + Py_END_ALLOW_THREADS; + if ( fp == NULL ) + return false; + // Save file name + fn = filename; + own = true; + return true; + } + + //-------------------------------------------------------------------------- + static qfile_t *from_fp(FILE *fp) + { + if ( fp == NULL ) + return NULL; + qfile_t *qf = new qfile_t(); + qf->own = false; + qf->fn.sprnt("", fp); + qf->fp = fp; + return qf; + } + + //-------------------------------------------------------------------------- + // This method can be used to pass a FILE* from C code + static qfile_t *from_cobject(PyObject *pycobject) + { + return PyCObject_Check(pycobject) ? from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)) : NULL; + } + + //-------------------------------------------------------------------------- + static qfile_t *tmpfile() + { + FILE *fp; + Py_BEGIN_ALLOW_THREADS; + fp = qtmpfile(); + Py_END_ALLOW_THREADS; + return from_fp(fp); + } + + //-------------------------------------------------------------------------- + FILE *get_fp() + { + return fp; + } + + //-------------------------------------------------------------------------- + int seek(int32 offset, int whence = SEEK_SET) + { + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfseek(fp, offset, whence); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int32 tell() + { + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qftell(fp); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + PyObject *readbytes(int size, bool big_endian) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = freadbytes(fp, buf, size, big_endian); + Py_END_ALLOW_THREADS; + if ( r != 0 ) + { + free(buf); + break; + } + + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *read(int size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = qfread(fp, buf, size); + Py_END_ALLOW_THREADS; + if ( r <= 0 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *gets(int size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + PYW_GIL_CHECK_LOCKED_SCOPE(); + char *p; + Py_BEGIN_ALLOW_THREADS; + p = qfgets(buf, size, fp); + Py_END_ALLOW_THREADS; + if ( p == NULL ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + int writebytes(PyObject *py_buf, bool big_endian) + { + Py_ssize_t sz; + void *buf; + PYW_GIL_CHECK_LOCKED_SCOPE(); + sz = PyString_GET_SIZE(py_buf); + buf = (void *)PyString_AS_STRING(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = fwritebytes(fp, buf, int(sz), big_endian); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int write(PyObject *py_buf) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_buf) ) + return 0; + // Just so that there is no risk that the buffer returned by + // 'PyString_AS_STRING' gets deallocated within the + // Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_buf_ref(py_buf); + void *p = (void *)PyString_AS_STRING(py_buf); + Py_ssize_t sz = PyString_GET_SIZE(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfwrite(fp, p, sz); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int puts(const char *str) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputs(str, fp); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + int32 size() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int32 r; + Py_BEGIN_ALLOW_THREADS; + int pos = qfseek(fp, 0, SEEK_END); + r = qftell(fp); + qfseek(fp, pos, SEEK_SET); + Py_END_ALLOW_THREADS; + return r; + } + + //-------------------------------------------------------------------------- + int flush() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qflush(fp); + Py_END_ALLOW_THREADS; + return rc; + } + + //-------------------------------------------------------------------------- + PyObject *filename() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyString_FromString(fn.c_str()); + } + + //-------------------------------------------------------------------------- + PyObject *get_char() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qfgetc(fp); + Py_END_ALLOW_THREADS; + if ( ch == EOF ) + Py_RETURN_NONE; + return Py_BuildValue("c", ch); + } + + //-------------------------------------------------------------------------- + int put_char(char chr) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputc(chr, fp); + Py_END_ALLOW_THREADS; + return rc; + } +}; +// +%} diff --git a/swig/frame.i b/swig/frame.i index 63ba9f6c..47d9fc5b 100644 --- a/swig/frame.i +++ b/swig/frame.i @@ -19,11 +19,11 @@ %ignore rename_frame; %ignore _set_frame_size; %ignore add_empty_frame; - -%ignore get_stkvar; -%rename (get_stkvar) py_get_stkvar; - -%ignore add_stkvar3; + +%ignore get_stkvar; +%rename (get_stkvar) py_get_stkvar; + +%ignore add_stkvar3; %rename (add_stkvar3) py_add_stkvar3; %ignore calc_frame_offset; diff --git a/swig/gdl.i b/swig/gdl.i index 0443aac9..c5823b8b 100644 --- a/swig/gdl.i +++ b/swig/gdl.i @@ -1,114 +1,114 @@ -%ignore cancellable_graph_t; -%ignore gdl_graph_t; - -%ignore intmap_t; -%ignore intset_t; -%ignore intseq_t; -%ignore node_set_t; -%ignore qflow_chart_t::blocks; -%ignore flow_chart_t; -%ignore default_graph_format; -%ignore setup_graph_subsystem; -%ignore qbasic_block_t::succ; -%ignore qbasic_block_t::pred; - -%include "gdl.hpp" - -%extend qflow_chart_t -{ - qbasic_block_t *__getitem__(int n) - { - return &(self->blocks[n]); - } -} - -%pythoncode %{ -# -# ----------------------------------------------------------------------- -class BasicBlock(object): - """Basic block class. It is returned by the Flowchart class""" - def __init__(self, id, bb, fc): - self._fc = fc - - self.id = id - """Basic block ID""" - - self.startEA = bb.startEA - """startEA of basic block""" - - self.endEA = bb.endEA - """endEA of basic block""" - - self.type = self._fc._q.calc_block_type(self.id) - """Block type (check fc_block_type_t enum)""" - - - def preds(self): - """ - Iterates the predecessors list - """ - q = self._fc._q - for i in xrange(0, self._fc._q.npred(self.id)): - yield self._fc[q.pred(self.id, i)] - - - def succs(self): - """ - Iterates the successors list - """ - q = self._fc._q - for i in xrange(0, q.nsucc(self.id)): - yield self._fc[q.succ(self.id, i)] - -# ----------------------------------------------------------------------- -class FlowChart(object): - """ - Flowchart class used to determine basic blocks. - Check ex_gdl_qflow_chart.py for sample usage. - """ - def __init__(self, f=None, bounds=None, flags=0): - """ - Constructor - @param f: A func_t type, use get_func(ea) to get a reference - @param bounds: A tuple of the form (start, end). Used if "f" is None - @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS - """ - if (f is None) and (bounds is None or type(bounds) != types.TupleType): - raise Exception("Please specifiy either a function or start/end pair") - - if bounds is None: - bounds = (BADADDR, BADADDR) - - # Create the flowchart - self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) - - size = property(lambda self: self._q.size()) - """Number of blocks in the flow chart""" - - - def refresh(): - """Refreshes the flow chart""" - self._q.refresh() - - - def _getitem(self, index): - return BasicBlock(index, self._q[index], self) - - - def __iter__(self): - return (self._getitem(index) for index in xrange(0, self.size)) - - - def __getitem__(self, index): - """ - Returns a basic block - - @return: BasicBlock - """ - if index >= self.size: - raise KeyError - else: - return self._getitem(index) - -# -%} +%ignore cancellable_graph_t; +%ignore gdl_graph_t; + +%ignore intmap_t; +%ignore intset_t; +%ignore intseq_t; +%ignore node_set_t; +%ignore qflow_chart_t::blocks; +%ignore flow_chart_t; +%ignore default_graph_format; +%ignore setup_graph_subsystem; +%ignore qbasic_block_t::succ; +%ignore qbasic_block_t::pred; + +%include "gdl.hpp" + +%extend qflow_chart_t +{ + qbasic_block_t *__getitem__(int n) + { + return &(self->blocks[n]); + } +} + +%pythoncode %{ +# +# ----------------------------------------------------------------------- +class BasicBlock(object): + """Basic block class. It is returned by the Flowchart class""" + def __init__(self, id, bb, fc): + self._fc = fc + + self.id = id + """Basic block ID""" + + self.startEA = bb.startEA + """startEA of basic block""" + + self.endEA = bb.endEA + """endEA of basic block""" + + self.type = self._fc._q.calc_block_type(self.id) + """Block type (check fc_block_type_t enum)""" + + + def preds(self): + """ + Iterates the predecessors list + """ + q = self._fc._q + for i in xrange(0, self._fc._q.npred(self.id)): + yield self._fc[q.pred(self.id, i)] + + + def succs(self): + """ + Iterates the successors list + """ + q = self._fc._q + for i in xrange(0, q.nsucc(self.id)): + yield self._fc[q.succ(self.id, i)] + +# ----------------------------------------------------------------------- +class FlowChart(object): + """ + Flowchart class used to determine basic blocks. + Check ex_gdl_qflow_chart.py for sample usage. + """ + def __init__(self, f=None, bounds=None, flags=0): + """ + Constructor + @param f: A func_t type, use get_func(ea) to get a reference + @param bounds: A tuple of the form (start, end). Used if "f" is None + @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS + """ + if (f is None) and (bounds is None or type(bounds) != types.TupleType): + raise Exception("Please specifiy either a function or start/end pair") + + if bounds is None: + bounds = (BADADDR, BADADDR) + + # Create the flowchart + self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) + + size = property(lambda self: self._q.size()) + """Number of blocks in the flow chart""" + + + def refresh(): + """Refreshes the flow chart""" + self._q.refresh() + + + def _getitem(self, index): + return BasicBlock(index, self._q[index], self) + + + def __iter__(self): + return (self._getitem(index) for index in xrange(0, self.size)) + + + def __getitem__(self, index): + """ + Returns a basic block + + @return: BasicBlock + """ + if index >= self.size: + raise KeyError + else: + return self._getitem(index) + +# +%} diff --git a/swig/graph.i b/swig/graph.i index 72484354..c3fc1b13 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -1,983 +1,983 @@ -%ignore mutable_graph_t; -%ignore graph_visitor_t; -%ignore abstract_graph_t; -%include "graph.hpp" - -%{ -// -class py_graph_t : public py_customidamemo_t -{ - typedef py_customidamemo_t inherited; - -protected: - - virtual void node_info_modified(int n, const node_info_t *ni, uint32 flags) - { - if ( ni == NULL ) - { - node_cache.erase(n); - } - else - { - nodetext_cache_t *c = node_cache.get(n); - if ( c != NULL ) - { - if ( (flags & NIF_TEXT) == NIF_TEXT ) - c->text = ni->text; - if ( (flags & NIF_BG_COLOR) == NIF_BG_COLOR ) - c->bgcolor = ni->bg_color; - } - } - } - - void collect_class_callbacks_ids(callbacks_ids_t *out); - -private: - enum - { - GRCODE_HAVE_USER_HINT = 0x00010000, - GRCODE_HAVE_CLICKED = 0x00020000, - GRCODE_HAVE_DBL_CLICKED = 0x00040000, - GRCODE_HAVE_GOTFOCUS = 0x00080000, - GRCODE_HAVE_LOSTFOCUS = 0x00100000, - GRCODE_HAVE_CHANGED_CURRENT = 0x00200000, - GRCODE_HAVE_COMMAND = 0x00400000, - GRCODE_HAVE_CREATING_GROUP = 0x00800000, - GRCODE_HAVE_DELETING_GROUP = 0x01000000, - GRCODE_HAVE_GROUP_VISIBILITY = 0x02000000, - }; - struct nodetext_cache_t - { - qstring text; - bgcolor_t bgcolor; - nodetext_cache_t(const nodetext_cache_t &rhs): text(rhs.text), bgcolor(rhs.bgcolor) { } - nodetext_cache_t(const char *t, bgcolor_t c): text(t), bgcolor(c) { } - nodetext_cache_t() { } - }; - - class nodetext_cache_map_t: public std::map - { - public: - nodetext_cache_t *get(int node_id) - { - iterator it = find(node_id); - if ( it == end() ) - return NULL; - return &it->second; - } - nodetext_cache_t *add(const int node_id, const char *text, bgcolor_t bgcolor = DEFCOLOR) - { - return &(insert(std::make_pair(node_id, nodetext_cache_t(text, bgcolor))).first->second); - } - }; - - class cmdid_map_t: public std::map - { - private: - Py_ssize_t uid; - public: - - cmdid_map_t() - { - // We start by one and keep zero for error id - uid = 1; - } - - void add(py_graph_t *pyg) - { - (*this)[uid] = pyg; - ++uid; - } - - const Py_ssize_t id() const - { - return uid; - } - - void clear(py_graph_t *pyg) - { - iterator e = end(); - for (iterator it=begin();it!=end();) - { - if ( it->second == pyg ) - { - iterator temp = it++; - erase(temp); - } - else - ++it; - } - } - - py_graph_t *get(Py_ssize_t id) - { - iterator it = find(id); - return it == end() ? NULL : it->second; - } - }; - - static cmdid_map_t cmdid_pyg; - - bool refresh_needed; - nodetext_cache_map_t node_cache; - - // instance callback - int gr_callback(int code, va_list va); - - // static callback - static int idaapi s_callback(void *obj, int code, va_list va) - { - QASSERT(30453, py_customidamemo_t::lookup_info.find_by_py_view(NULL, NULL, (py_graph_t *) obj)); - PYW_GIL_GET; - return ((py_graph_t *)obj)->gr_callback(code, va); - } - - static bool idaapi s_menucb(void *ud) - { - PYW_GIL_GET; - Py_ssize_t id = (Py_ssize_t)ud; - py_graph_t *_this = cmdid_pyg.get(id); - if ( _this != NULL ) - _this->on_command(id); - - return true; - } - - void on_command(Py_ssize_t id) - { - // Check return value to OnRefresh() call - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id)); - PyW_ShowCbErr(S_ON_COMMAND); - } - - // Refresh user-defined graph node number and edges - // It calls Python method and expects that the user already filled - // the nodes and edges. The nodes and edges are retrieved and passed to IDA - void on_user_refresh(mutable_graph_t *g); - - // Retrieves the text for user-defined graph node - // It expects either a string or a tuple (string, bgcolor) - bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color); - - // Retrieves the hint for the user-defined graph - // Calls Python and expects a string or None - int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint); - - // graph is being destroyed - void on_graph_destroyed(mutable_graph_t * /*g*/ = NULL) - { - refresh_needed = true; - node_cache.clear(); - } - - // graph is being clicked - int on_clicked( - graph_viewer_t * /*view*/, - selection_item_t * /*item1*/, - graph_item_t *item2) - { - // in: graph_viewer_t *view - // selection_item_t *current_item1 - // graph_item_t *current_item2 - // out: 0-ok, 1-ignore click - // this callback allows you to ignore some clicks. - // it occurs too early, internal graph variables are not updated yet - // current_item1, current_item2 point to the same thing - // item2 has more information. - // see also: kernwin.hpp, custom_viewer_click_t - if ( item2->n == -1 ) - return 1; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_CLICK, - "i", - item2->n)); - PyW_ShowCbErr(S_ON_CLICK); - return result == NULL || !PyObject_IsTrue(result.o); - } - - // a graph node has been double clicked - int on_dblclicked(graph_viewer_t * /*view*/, selection_item_t *item) - { - // in: graph_viewer_t *view - // selection_item_t *current_item - // out: 0-ok, 1-ignore click - //graph_viewer_t *v = va_arg(va, graph_viewer_t *); - //selection_item_t *s = va_arg(va, selection_item_t *); - if ( item == NULL || !item->is_node ) - return 1; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_DBL_CLICK, - "i", - item->node)); - PyW_ShowCbErr(S_ON_DBL_CLICK); - return result == NULL || !PyObject_IsTrue(result.o); - } - - // a graph viewer got focus - void on_gotfocus(graph_viewer_t * /*view*/) - { - if ( self.o == NULL ) - return; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_ACTIVATE, - NULL)); - PyW_ShowCbErr(S_ON_ACTIVATE); - } - - // a graph viewer lost focus - void on_lostfocus(graph_viewer_t * /*view*/) - { - if ( self.o == NULL ) - return; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_DEACTIVATE, - NULL)); - PyW_ShowCbErr(S_ON_DEACTIVATE); - } - - // a new graph node became the current node - int on_changed_current(graph_viewer_t * /*view*/, int curnode) - { - // in: graph_viewer_t *view - // int curnode - // out: 0-ok, 1-forbid to change the current node - if ( curnode < 0 ) - return 0; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_SELECT, - "i", - curnode)); - PyW_ShowCbErr(S_ON_SELECT); - return !(result != NULL && PyObject_IsTrue(result.o)); - } - - // a group is being created - int on_creating_group(mutable_graph_t *my_g, intvec_t *my_nodes) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_nodes(PyList_New(my_nodes->size())); - int i; - intvec_t::const_iterator p; - for ( i = 0, p=my_nodes->begin(); p != my_nodes->end(); ++p, ++i ) - PyList_SetItem(py_nodes.o, i, PyInt_FromLong(*p)); - newref_t py_result( - PyObject_CallMethod( - self.o, - (char *)S_ON_CREATING_GROUP, - "O", - py_nodes.o)); - PyW_ShowCbErr(S_ON_CREATING_GROUP); - return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o); - } - - // a group is being deleted - int on_deleting_group(mutable_graph_t * /*g*/, int old_group) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - // TODO - return 0; - } - - // a group is being collapsed/uncollapsed - int on_group_visibility(mutable_graph_t * /*g*/, int group, bool expand) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - // TODO - return 0; - } - - - void show() - { - TForm *form; - if ( lookup_info.find_by_py_view(&form, NULL, this) ) - open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); - } - - void jump_to_node(int nid) - { - ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); - if ( nid >= PyList_Size(nodes.o) ) - return; - - viewer_center_on(view, nid); - int x, y; - - // will return a place only when a node was previously selected - place_t *old_pl = get_custom_viewer_place(view, false, &x, &y); - if ( old_pl != NULL ) - { - user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); - new_pl->node = nid; - jumpto(view, new_pl, x, y); - delete new_pl; - } - } - - virtual void refresh() - { - refresh_needed = true; - inherited::refresh(); - } - - int initialize(PyObject *self, const char *title) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !collect_pyobject_callbacks(self) ) - return -1; - - // Create form - HWND hwnd = NULL; - TForm *form = create_tform(title, &hwnd); - if ( hwnd != NULL ) // Created new tform - { - lookup_info_t::entry_t &e = lookup_info.new_entry(this); - // get a unique graph id - netnode id; - char grnode[MAXSTR]; - qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); - id.create(grnode); - graph_viewer_t *pview = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); - if ( pview != NULL ) - viewer_fit_window(pview); - bind(self, pview); - install_custom_viewer_handlers(); - refresh(); - lookup_info.commit(e, form, view); - } - else - { - show(); - } - - viewer_fit_window(view); - return 0; - } - - Py_ssize_t add_command(const char *title, const char *hotkey) - { - if ( !has_callback(GRCODE_HAVE_COMMAND) || view == NULL) - return 0; - Py_ssize_t cmd_id = cmdid_pyg.id(); - bool ok = viewer_add_menu_item(view, title, s_menucb, (void *)cmd_id, hotkey, 0); - if ( !ok ) - return 0; - cmdid_pyg.add(this); - return cmd_id; - } - -public: - py_graph_t() - { - // form = NULL; - refresh_needed = true; - } - - virtual ~py_graph_t() - { - // Remove all associated commands from the list - cmdid_pyg.clear(this); - } - - static void SelectNode(PyObject *self, int nid) - { - if ( nid < 0 ) - return; - - py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) - return; - - _this->jump_to_node(nid); - } - - static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) - { - py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) - return 0; - - return _this->add_command(title, hotkey); - } - - static void Close(PyObject *self) - { - TForm *form; - py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) - return; - close_tform(form, FORM_CLOSE_LATER); - } - - static py_graph_t *Show(PyObject *self) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - py_graph_t *py_graph = view_extract_this(self); - - // New instance? - if ( py_graph == NULL ) - { - qstring title; - if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) - return NULL; - - // Form already created? try to get associated py_graph instance - // so that we reuse it - graph_viewer_t *found_view; - TForm *form = find_tform(title.c_str()); - if ( form != NULL ) - lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); - - if ( py_graph == NULL ) - { - py_graph = new py_graph_t(); - } - else - { - // unbind so we are rebound - py_graph->unbind(); - py_graph->refresh_needed = true; - } - if ( py_graph->initialize(self, title.c_str()) < 0 ) - { - delete py_graph; - py_graph = NULL; - } - } - else - { - py_graph->show(); - } - return py_graph; - } -}; - -//------------------------------------------------------------------------- -void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) -{ - inherited::collect_class_callbacks_ids(out); - out->add(S_ON_REFRESH, 0); - out->add(S_ON_GETTEXT, 0); - out->add(S_M_EDGES, -1); - out->add(S_M_NODES, -1); - out->add(S_ON_HINT, GRCODE_HAVE_USER_HINT); - out->add(S_ON_CLICK, GRCODE_HAVE_CLICKED); - out->add(S_ON_DBL_CLICK, GRCODE_HAVE_DBL_CLICKED); - out->add(S_ON_COMMAND, GRCODE_HAVE_COMMAND); - out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT); - out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS); - out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS); - out->add(S_ON_CREATING_GROUP, GRCODE_HAVE_CREATING_GROUP); - out->add(S_ON_DELETING_GROUP, GRCODE_HAVE_DELETING_GROUP); - out->add(S_ON_GROUP_VISIBILITY, GRCODE_HAVE_GROUP_VISIBILITY); -} - -//------------------------------------------------------------------------- -void py_graph_t::on_user_refresh(mutable_graph_t *g) -{ - if ( !refresh_needed || self == NULL /* Happens at creation-time */ ) - return; - - // Check return value to OnRefresh() call - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL)); - PyW_ShowCbErr(S_ON_REFRESH); - if ( ret != NULL && PyObject_IsTrue(ret.o) ) - { - // Refer to the nodes - ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); - if ( ret != NULL && PyList_Check(nodes.o) ) - { - // Refer to the edges - ref_t edges(PyW_TryGetAttrString(self.o, S_M_EDGES)); - if ( ret != NULL && PyList_Check(edges.o) ) - { - // Resize the nodes - int max_nodes = abs(int(PyList_Size(nodes.o))); - g->clear(); - g->resize(max_nodes); - - // Mark that we refreshed already - refresh_needed = false; - - // Clear cached nodes - node_cache.clear(); - - // Get the edges - for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) - { - // Each list item is a sequence (id1, id2) - borref_t item(PyList_GetItem(edges.o, i)); - if ( !PySequence_Check(item.o) ) - continue; - - // Get and validate each of the two elements in the sequence - int edge_ids[2]; - int j; - for ( j=0; j max_nodes ) - break; - edge_ids[j] = v; - } - - // Incomplete? - if ( j != qnumber(edge_ids) ) - break; - - // Add the edge - g->add_edge(edge_ids[0], edge_ids[1], NULL); - } - } - } - } -} - -//------------------------------------------------------------------------- -bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) -{ - // If already cached then return the value - nodetext_cache_t *c = node_cache.get(node); - if ( c != NULL ) - { - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - return true; - } - - // Not cached, call Python - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node)); - PyW_ShowCbErr(S_ON_GETTEXT); - if ( result == NULL ) - return false; - - bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; - const char *s; - - // User returned a string? - if ( PyString_Check(result.o) ) - { - s = PyString_AsString(result.o); - if ( s == NULL ) - s = ""; - c = node_cache.add(node, s, cl); - } - // User returned a sequence of text and bgcolor - else if ( PySequence_Check(result.o) && PySequence_Size(result.o) == 2 ) - { - newref_t py_str(PySequence_GetItem(result.o, 0)); - newref_t py_color(PySequence_GetItem(result.o, 1)); - - if ( py_str == NULL || !PyString_Check(py_str.o) || (s = PyString_AsString(py_str.o)) == NULL ) - s = ""; - if ( py_color != NULL && PyNumber_Check(py_color.o) ) - cl = bgcolor_t(PyLong_AsUnsignedLong(py_color.o)); - - c = node_cache.add(node, s, cl); - } - - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - - return true; -} - -//------------------------------------------------------------------------- -int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) -{ - // 'hint' must be allocated by qalloc() or qstrdup() - // out: 0-use default hint, 1-use proposed hint - - // We dispatch hints over nodes only - if ( mousenode == -1 ) - return 0; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode)); - PyW_ShowCbErr(S_ON_HINT); - bool ok = result != NULL && PyString_Check(result.o); - if ( ok ) - *hint = qstrdup(PyString_AsString(result.o)); - return ok; // use our hint -} - - -//------------------------------------------------------------------------- -int py_graph_t::gr_callback(int code, va_list va) -{ - int ret; - switch ( code ) - { - // - case grcode_user_text: - { - mutable_graph_t *g = va_arg(va, mutable_graph_t *); - int node = va_arg(va, int); - const char **result = va_arg(va, const char **); - bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); - ret = on_user_text(g, node, result, bgcolor); - break; - } - // - case grcode_destroyed: - on_graph_destroyed(va_arg(va, mutable_graph_t *)); - ret = 0; - break; - - // - case grcode_clicked: - if ( has_callback(GRCODE_HAVE_CLICKED) ) - { - graph_viewer_t *view = va_arg(va, graph_viewer_t *); - selection_item_t *item = va_arg(va, selection_item_t *); - graph_item_t *gitem = va_arg(va, graph_item_t *); - ret = on_clicked(view, item, gitem); - } - else - { - // Ignore the click - ret = 1; - } - break; - // - case grcode_dblclicked: - if ( has_callback(GRCODE_HAVE_DBL_CLICKED) ) - { - graph_viewer_t *view = va_arg(va, graph_viewer_t *); - selection_item_t *item = va_arg(va, selection_item_t *); - ret = on_dblclicked(view, item); - } - else - ret = 0; // We don't want to ignore the double click, but rather - // fallback to the default behavior (e.g., double-clicking - // on an edge will to jump to the node on the other side - // of that edge.) - break; - // - case grcode_gotfocus: - if ( has_callback(GRCODE_HAVE_GOTFOCUS) ) - on_gotfocus(va_arg(va, graph_viewer_t *)); - - ret = 0; - break; - // - case grcode_lostfocus: - if ( has_callback(GRCODE_HAVE_LOSTFOCUS) ) - on_lostfocus(va_arg(va, graph_viewer_t *)); - - ret = 0; - break; - // - case grcode_user_refresh: - on_user_refresh(va_arg(va, mutable_graph_t *)); - - ret = 1; - break; - // - case grcode_user_hint: - if ( has_callback(GRCODE_HAVE_USER_HINT) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t *); - int mousenode = va_arg(va, int); - int mouseedge_src = va_arg(va, int); - int mouseedge_dest = va_arg(va, int); - char **hint = va_arg(va, char **); - ret = on_user_hint(g, mousenode, mouseedge_src, mouseedge_dest, hint); - } - else - { - ret = 0; - } - break; - // - case grcode_changed_current: - if ( has_callback(GRCODE_HAVE_CHANGED_CURRENT) ) - { - graph_viewer_t *view = va_arg(va, graph_viewer_t *); - int cur_node = va_arg(va, int); - ret = on_changed_current(view, cur_node); - } - else - ret = 0; // allow selection change - break; - // - case grcode_creating_group: // a group is being created - if ( has_callback(GRCODE_HAVE_CREATING_GROUP) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t*); - intvec_t *nodes = va_arg(va, intvec_t*); - ret = on_creating_group(g, nodes); - } - else - { - ret = 0; // Ok to create - } - break; - // - case grcode_deleting_group: // a group is being deleted - if ( has_callback(GRCODE_HAVE_DELETING_GROUP) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t*); - int old_group = va_arg(va, int); - ret = on_deleting_group(g, old_group); - } - else - { - ret = 0; // Ok to delete - } - break; - // - case grcode_group_visibility: // a group is being collapsed/uncollapsed - if ( has_callback(GRCODE_HAVE_GROUP_VISIBILITY) ) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t*); - int group = va_arg(va, int); - bool expand = bool(va_arg(va, int)); - ret = on_group_visibility(g, group, expand); - } - else - { - ret = 0; // Ok. - } - break; - // - default: - ret = 0; - break; - } - //grcode_changed_graph, // new graph has been set - //grcode_user_size, // calculate node size for user-defined graph - //grcode_user_title, // render node title of a user-defined graph - //grcode_user_draw, // render node of a user-defined graph - return ret; -} - -//------------------------------------------------------------------------- -py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; - -bool pyg_show(PyObject *self) -{ - return py_graph_t::Show(self) != NULL; -} - -void pyg_close(PyObject *self) -{ - py_graph_t::Close(self); -} - -PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); -} - -void pyg_select_node(PyObject *self, int nid) -{ - py_graph_t::SelectNode(self, nid); -} -// -%} - -%inline %{ -// -void pyg_close(PyObject *self); -PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); -void pyg_select_node(PyObject *self, int nid); -bool pyg_show(PyObject *self); -// -%} - -%pythoncode %{ -# -class GraphViewer(CustomIDAMemo): - """This class wraps the user graphing facility provided by the graph.hpp file""" - def __init__(self, title, close_open = False): - """ - Constructs the GraphView object. - Please do not remove or rename the private fields - - @param title: The title of the graph window - @param close_open: Should it attempt to close an existing graph (with same title) before creating this graph? - """ - self._title = title - self._nodes = [] - self._edges = [] - self._close_open = close_open - - def AddNode(self, obj): - """Creates a node associated with the given object and returns the node id""" - id = len(self._nodes) - self._nodes.append(obj) - return id - - def AddEdge(self, src_node, dest_node): - """Creates an edge between two given node ids""" - self._edges.append( (src_node, dest_node) ) - - def Clear(self): - """Clears all the nodes and edges""" - self._nodes = [] - self._edges = [] - - - def __iter__(self): - return (self._nodes[index] for index in xrange(0, len(self._nodes))) - - - def __getitem__(self, idx): - """Returns a reference to the object associated with this node id""" - if idx >= len(self._nodes): - raise KeyError - else: - return self._nodes[idx] - - def Count(self): - """Returns the node count""" - return len(self._nodes) - - def Close(self): - """ - Closes the graph. - It is possible to call Show() again (which will recreate the graph) - """ - _idaapi.pyg_close(self) - - def Show(self): - """ - Shows an existing graph or creates a new one - - @return: Boolean - """ - if self._close_open: - frm = _idaapi.find_tform(self._title) - if frm: - _idaapi.close_tform(frm, 0) - return _idaapi.pyg_show(self) - - def Select(self, node_id): - """Selects a node on the graph""" - _idaapi.pyg_select_node(self, node_id) - - def AddCommand(self, title, hotkey): - """ - Deprecated: Use - - register_action() - - attach_action_to_popup() - """ - return _idaapi.pyg_add_command(self, title, hotkey) - - def OnRefresh(self): - """ - Event called when the graph is refreshed or first created. - From this event you are supposed to create nodes and edges. - This callback is mandatory. - - @note: ***It is important to clear previous nodes before adding nodes.*** - @return: Returning True tells the graph viewer to use the items. Otherwise old items will be used. - """ - self.Clear() - - return True -# -# def OnGetText(self, node_id): -# """ -# Triggered when the graph viewer wants the text and color for a given node. -# This callback is triggered one time for a given node (the value will be cached and used later without calling Python). -# When you call refresh then again this callback will be called for each node. -# -# This callback is mandatory. -# -# @return: Return a string to describe the node text or return a tuple (node_text, node_color) to describe both text and color -# """ -# return str(self[node_id]) -# -# def OnActivate(self): -# """ -# Triggered when the graph window gets the focus -# @return: None -# """ -# print "Activated...." -# -# def OnDeactivate(self): -# """Triggered when the graph window loses the focus -# @return: None -# """ -# print "Deactivated...." -# -# def OnSelect(self, node_id): -# """ -# Triggered when a node is being selected -# @return: Return True to allow the node to be selected or False to disallow node selection change -# """ -# # allow selection change -# return True -# -# def OnHint(self, node_id): -# """ -# Triggered when the graph viewer wants to retrieve hint text associated with a given node -# -# @return: None if no hint is avail or a string designating the hint -# """ -# return "hint for " + str(node_id) -# -# def OnClose(self): -# """Triggered when the graph viewer window is being closed -# @return: None -# """ -# print "Closing......." -# -# def OnClick(self, node_id): -# """ -# Triggered when a node is clicked -# @return: False to ignore the click and True otherwise -# """ -# print "clicked on", self[node_id] -# return True -# -# def OnDblClick(self, node_id): -# """ -# Triggerd when a node is double-clicked. -# @return: False to ignore the click and True otherwise -# """ -# print "dblclicked on", self[node_id] -# return True -# -# def OnCommand(self, cmd_id): -# """ -# Deprecated -# """ -# pass -# -# -%} +%ignore mutable_graph_t; +%ignore graph_visitor_t; +%ignore abstract_graph_t; +%include "graph.hpp" + +%{ +// +class py_graph_t : public py_customidamemo_t +{ + typedef py_customidamemo_t inherited; + +protected: + + virtual void node_info_modified(int n, const node_info_t *ni, uint32 flags) + { + if ( ni == NULL ) + { + node_cache.erase(n); + } + else + { + nodetext_cache_t *c = node_cache.get(n); + if ( c != NULL ) + { + if ( (flags & NIF_TEXT) == NIF_TEXT ) + c->text = ni->text; + if ( (flags & NIF_BG_COLOR) == NIF_BG_COLOR ) + c->bgcolor = ni->bg_color; + } + } + } + + void collect_class_callbacks_ids(callbacks_ids_t *out); + +private: + enum + { + GRCODE_HAVE_USER_HINT = 0x00010000, + GRCODE_HAVE_CLICKED = 0x00020000, + GRCODE_HAVE_DBL_CLICKED = 0x00040000, + GRCODE_HAVE_GOTFOCUS = 0x00080000, + GRCODE_HAVE_LOSTFOCUS = 0x00100000, + GRCODE_HAVE_CHANGED_CURRENT = 0x00200000, + GRCODE_HAVE_COMMAND = 0x00400000, + GRCODE_HAVE_CREATING_GROUP = 0x00800000, + GRCODE_HAVE_DELETING_GROUP = 0x01000000, + GRCODE_HAVE_GROUP_VISIBILITY = 0x02000000, + }; + struct nodetext_cache_t + { + qstring text; + bgcolor_t bgcolor; + nodetext_cache_t(const nodetext_cache_t &rhs): text(rhs.text), bgcolor(rhs.bgcolor) { } + nodetext_cache_t(const char *t, bgcolor_t c): text(t), bgcolor(c) { } + nodetext_cache_t() { } + }; + + class nodetext_cache_map_t: public std::map + { + public: + nodetext_cache_t *get(int node_id) + { + iterator it = find(node_id); + if ( it == end() ) + return NULL; + return &it->second; + } + nodetext_cache_t *add(const int node_id, const char *text, bgcolor_t bgcolor = DEFCOLOR) + { + return &(insert(std::make_pair(node_id, nodetext_cache_t(text, bgcolor))).first->second); + } + }; + + class cmdid_map_t: public std::map + { + private: + Py_ssize_t uid; + public: + + cmdid_map_t() + { + // We start by one and keep zero for error id + uid = 1; + } + + void add(py_graph_t *pyg) + { + (*this)[uid] = pyg; + ++uid; + } + + const Py_ssize_t id() const + { + return uid; + } + + void clear(py_graph_t *pyg) + { + iterator e = end(); + for (iterator it=begin();it!=end();) + { + if ( it->second == pyg ) + { + iterator temp = it++; + erase(temp); + } + else + ++it; + } + } + + py_graph_t *get(Py_ssize_t id) + { + iterator it = find(id); + return it == end() ? NULL : it->second; + } + }; + + static cmdid_map_t cmdid_pyg; + + bool refresh_needed; + nodetext_cache_map_t node_cache; + + // instance callback + int gr_callback(int code, va_list va); + + // static callback + static int idaapi s_callback(void *obj, int code, va_list va) + { + QASSERT(30453, py_customidamemo_t::lookup_info.find_by_py_view(NULL, NULL, (py_graph_t *) obj)); + PYW_GIL_GET; + return ((py_graph_t *)obj)->gr_callback(code, va); + } + + static bool idaapi s_menucb(void *ud) + { + PYW_GIL_GET; + Py_ssize_t id = (Py_ssize_t)ud; + py_graph_t *_this = cmdid_pyg.get(id); + if ( _this != NULL ) + _this->on_command(id); + + return true; + } + + void on_command(Py_ssize_t id) + { + // Check return value to OnRefresh() call + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id)); + PyW_ShowCbErr(S_ON_COMMAND); + } + + // Refresh user-defined graph node number and edges + // It calls Python method and expects that the user already filled + // the nodes and edges. The nodes and edges are retrieved and passed to IDA + void on_user_refresh(mutable_graph_t *g); + + // Retrieves the text for user-defined graph node + // It expects either a string or a tuple (string, bgcolor) + bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color); + + // Retrieves the hint for the user-defined graph + // Calls Python and expects a string or None + int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint); + + // graph is being destroyed + void on_graph_destroyed(mutable_graph_t * /*g*/ = NULL) + { + refresh_needed = true; + node_cache.clear(); + } + + // graph is being clicked + int on_clicked( + graph_viewer_t * /*view*/, + selection_item_t * /*item1*/, + graph_item_t *item2) + { + // in: graph_viewer_t *view + // selection_item_t *current_item1 + // graph_item_t *current_item2 + // out: 0-ok, 1-ignore click + // this callback allows you to ignore some clicks. + // it occurs too early, internal graph variables are not updated yet + // current_item1, current_item2 point to the same thing + // item2 has more information. + // see also: kernwin.hpp, custom_viewer_click_t + if ( item2->n == -1 ) + return 1; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CLICK, + "i", + item2->n)); + PyW_ShowCbErr(S_ON_CLICK); + return result == NULL || !PyObject_IsTrue(result.o); + } + + // a graph node has been double clicked + int on_dblclicked(graph_viewer_t * /*view*/, selection_item_t *item) + { + // in: graph_viewer_t *view + // selection_item_t *current_item + // out: 0-ok, 1-ignore click + //graph_viewer_t *v = va_arg(va, graph_viewer_t *); + //selection_item_t *s = va_arg(va, selection_item_t *); + if ( item == NULL || !item->is_node ) + return 1; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DBL_CLICK, + "i", + item->node)); + PyW_ShowCbErr(S_ON_DBL_CLICK); + return result == NULL || !PyObject_IsTrue(result.o); + } + + // a graph viewer got focus + void on_gotfocus(graph_viewer_t * /*view*/) + { + if ( self.o == NULL ) + return; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_ACTIVATE, + NULL)); + PyW_ShowCbErr(S_ON_ACTIVATE); + } + + // a graph viewer lost focus + void on_lostfocus(graph_viewer_t * /*view*/) + { + if ( self.o == NULL ) + return; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DEACTIVATE, + NULL)); + PyW_ShowCbErr(S_ON_DEACTIVATE); + } + + // a new graph node became the current node + int on_changed_current(graph_viewer_t * /*view*/, int curnode) + { + // in: graph_viewer_t *view + // int curnode + // out: 0-ok, 1-forbid to change the current node + if ( curnode < 0 ) + return 0; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_SELECT, + "i", + curnode)); + PyW_ShowCbErr(S_ON_SELECT); + return !(result != NULL && PyObject_IsTrue(result.o)); + } + + // a group is being created + int on_creating_group(mutable_graph_t *my_g, intvec_t *my_nodes) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_nodes(PyList_New(my_nodes->size())); + int i; + intvec_t::const_iterator p; + for ( i = 0, p=my_nodes->begin(); p != my_nodes->end(); ++p, ++i ) + PyList_SetItem(py_nodes.o, i, PyInt_FromLong(*p)); + newref_t py_result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CREATING_GROUP, + "O", + py_nodes.o)); + PyW_ShowCbErr(S_ON_CREATING_GROUP); + return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o); + } + + // a group is being deleted + int on_deleting_group(mutable_graph_t * /*g*/, int old_group) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + // a group is being collapsed/uncollapsed + int on_group_visibility(mutable_graph_t * /*g*/, int group, bool expand) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + + void show() + { + TForm *form; + if ( lookup_info.find_by_py_view(&form, NULL, this) ) + open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); + } + + void jump_to_node(int nid) + { + ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); + if ( nid >= PyList_Size(nodes.o) ) + return; + + viewer_center_on(view, nid); + int x, y; + + // will return a place only when a node was previously selected + place_t *old_pl = get_custom_viewer_place(view, false, &x, &y); + if ( old_pl != NULL ) + { + user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); + new_pl->node = nid; + jumpto(view, new_pl, x, y); + delete new_pl; + } + } + + virtual void refresh() + { + refresh_needed = true; + inherited::refresh(); + } + + int initialize(PyObject *self, const char *title) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !collect_pyobject_callbacks(self) ) + return -1; + + // Create form + HWND hwnd = NULL; + TForm *form = create_tform(title, &hwnd); + if ( hwnd != NULL ) // Created new tform + { + lookup_info_t::entry_t &e = lookup_info.new_entry(this); + // get a unique graph id + netnode id; + char grnode[MAXSTR]; + qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); + id.create(grnode); + graph_viewer_t *pview = create_graph_viewer(form, id, s_callback, this, 0); + open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); + if ( pview != NULL ) + viewer_fit_window(pview); + bind(self, pview); + install_custom_viewer_handlers(); + refresh(); + lookup_info.commit(e, form, view); + } + else + { + show(); + } + + viewer_fit_window(view); + return 0; + } + + Py_ssize_t add_command(const char *title, const char *hotkey) + { + if ( !has_callback(GRCODE_HAVE_COMMAND) || view == NULL) + return 0; + Py_ssize_t cmd_id = cmdid_pyg.id(); + bool ok = viewer_add_menu_item(view, title, s_menucb, (void *)cmd_id, hotkey, 0); + if ( !ok ) + return 0; + cmdid_pyg.add(this); + return cmd_id; + } + +public: + py_graph_t() + { + // form = NULL; + refresh_needed = true; + } + + virtual ~py_graph_t() + { + // Remove all associated commands from the list + cmdid_pyg.clear(this); + } + + static void SelectNode(PyObject *self, int nid) + { + if ( nid < 0 ) + return; + + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return; + + _this->jump_to_node(nid); + } + + static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) + { + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return 0; + + return _this->add_command(title, hotkey); + } + + static void Close(PyObject *self) + { + TForm *form; + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) + return; + close_tform(form, FORM_CLOSE_LATER); + } + + static py_graph_t *Show(PyObject *self) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_graph_t *py_graph = view_extract_this(self); + + // New instance? + if ( py_graph == NULL ) + { + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return NULL; + + // Form already created? try to get associated py_graph instance + // so that we reuse it + graph_viewer_t *found_view; + TForm *form = find_tform(title.c_str()); + if ( form != NULL ) + lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); + + if ( py_graph == NULL ) + { + py_graph = new py_graph_t(); + } + else + { + // unbind so we are rebound + py_graph->unbind(false); + py_graph->refresh_needed = true; + } + if ( py_graph->initialize(self, title.c_str()) < 0 ) + { + delete py_graph; + py_graph = NULL; + } + } + else + { + py_graph->show(); + } + return py_graph; + } +}; + +//------------------------------------------------------------------------- +void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + inherited::collect_class_callbacks_ids(out); + out->add(S_ON_REFRESH, 0); + out->add(S_ON_GETTEXT, 0); + out->add(S_M_EDGES, -1); + out->add(S_M_NODES, -1); + out->add(S_ON_HINT, GRCODE_HAVE_USER_HINT); + out->add(S_ON_CLICK, GRCODE_HAVE_CLICKED); + out->add(S_ON_DBL_CLICK, GRCODE_HAVE_DBL_CLICKED); + out->add(S_ON_COMMAND, GRCODE_HAVE_COMMAND); + out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT); + out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS); + out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS); + out->add(S_ON_CREATING_GROUP, GRCODE_HAVE_CREATING_GROUP); + out->add(S_ON_DELETING_GROUP, GRCODE_HAVE_DELETING_GROUP); + out->add(S_ON_GROUP_VISIBILITY, GRCODE_HAVE_GROUP_VISIBILITY); +} + +//------------------------------------------------------------------------- +void py_graph_t::on_user_refresh(mutable_graph_t *g) +{ + if ( !refresh_needed || self == NULL /* Happens at creation-time */ ) + return; + + // Check return value to OnRefresh() call + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL)); + PyW_ShowCbErr(S_ON_REFRESH); + if ( ret != NULL && PyObject_IsTrue(ret.o) ) + { + // Refer to the nodes + ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); + if ( ret != NULL && PyList_Check(nodes.o) ) + { + // Refer to the edges + ref_t edges(PyW_TryGetAttrString(self.o, S_M_EDGES)); + if ( ret != NULL && PyList_Check(edges.o) ) + { + // Resize the nodes + int max_nodes = abs(int(PyList_Size(nodes.o))); + g->clear(); + g->resize(max_nodes); + + // Mark that we refreshed already + refresh_needed = false; + + // Clear cached nodes + node_cache.clear(); + + // Get the edges + for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) + { + // Each list item is a sequence (id1, id2) + borref_t item(PyList_GetItem(edges.o, i)); + if ( !PySequence_Check(item.o) ) + continue; + + // Get and validate each of the two elements in the sequence + int edge_ids[2]; + int j; + for ( j=0; j max_nodes ) + break; + edge_ids[j] = v; + } + + // Incomplete? + if ( j != qnumber(edge_ids) ) + break; + + // Add the edge + g->add_edge(edge_ids[0], edge_ids[1], NULL); + } + } + } + } +} + +//------------------------------------------------------------------------- +bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) +{ + // If already cached then return the value + nodetext_cache_t *c = node_cache.get(node); + if ( c != NULL ) + { + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + return true; + } + + // Not cached, call Python + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node)); + PyW_ShowCbErr(S_ON_GETTEXT); + if ( result == NULL ) + return false; + + bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; + const char *s; + + // User returned a string? + if ( PyString_Check(result.o) ) + { + s = PyString_AsString(result.o); + if ( s == NULL ) + s = ""; + c = node_cache.add(node, s, cl); + } + // User returned a sequence of text and bgcolor + else if ( PySequence_Check(result.o) && PySequence_Size(result.o) == 2 ) + { + newref_t py_str(PySequence_GetItem(result.o, 0)); + newref_t py_color(PySequence_GetItem(result.o, 1)); + + if ( py_str == NULL || !PyString_Check(py_str.o) || (s = PyString_AsString(py_str.o)) == NULL ) + s = ""; + if ( py_color != NULL && PyNumber_Check(py_color.o) ) + cl = bgcolor_t(PyLong_AsUnsignedLong(py_color.o)); + + c = node_cache.add(node, s, cl); + } + + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + + return true; +} + +//------------------------------------------------------------------------- +int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) +{ + // 'hint' must be allocated by qalloc() or qstrdup() + // out: 0-use default hint, 1-use proposed hint + + // We dispatch hints over nodes only + if ( mousenode == -1 ) + return 0; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode)); + PyW_ShowCbErr(S_ON_HINT); + bool ok = result != NULL && PyString_Check(result.o); + if ( ok ) + *hint = qstrdup(PyString_AsString(result.o)); + return ok; // use our hint +} + + +//------------------------------------------------------------------------- +int py_graph_t::gr_callback(int code, va_list va) +{ + int ret; + switch ( code ) + { + // + case grcode_user_text: + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int node = va_arg(va, int); + const char **result = va_arg(va, const char **); + bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); + ret = on_user_text(g, node, result, bgcolor); + break; + } + // + case grcode_destroyed: + on_graph_destroyed(va_arg(va, mutable_graph_t *)); + ret = 0; + break; + + // + case grcode_clicked: + if ( has_callback(GRCODE_HAVE_CLICKED) ) + { + graph_viewer_t *view = va_arg(va, graph_viewer_t *); + selection_item_t *item = va_arg(va, selection_item_t *); + graph_item_t *gitem = va_arg(va, graph_item_t *); + ret = on_clicked(view, item, gitem); + } + else + { + // Ignore the click + ret = 1; + } + break; + // + case grcode_dblclicked: + if ( has_callback(GRCODE_HAVE_DBL_CLICKED) ) + { + graph_viewer_t *view = va_arg(va, graph_viewer_t *); + selection_item_t *item = va_arg(va, selection_item_t *); + ret = on_dblclicked(view, item); + } + else + ret = 0; // We don't want to ignore the double click, but rather + // fallback to the default behavior (e.g., double-clicking + // on an edge will to jump to the node on the other side + // of that edge.) + break; + // + case grcode_gotfocus: + if ( has_callback(GRCODE_HAVE_GOTFOCUS) ) + on_gotfocus(va_arg(va, graph_viewer_t *)); + + ret = 0; + break; + // + case grcode_lostfocus: + if ( has_callback(GRCODE_HAVE_LOSTFOCUS) ) + on_lostfocus(va_arg(va, graph_viewer_t *)); + + ret = 0; + break; + // + case grcode_user_refresh: + on_user_refresh(va_arg(va, mutable_graph_t *)); + + ret = 1; + break; + // + case grcode_user_hint: + if ( has_callback(GRCODE_HAVE_USER_HINT) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int mousenode = va_arg(va, int); + int mouseedge_src = va_arg(va, int); + int mouseedge_dest = va_arg(va, int); + char **hint = va_arg(va, char **); + ret = on_user_hint(g, mousenode, mouseedge_src, mouseedge_dest, hint); + } + else + { + ret = 0; + } + break; + // + case grcode_changed_current: + if ( has_callback(GRCODE_HAVE_CHANGED_CURRENT) ) + { + graph_viewer_t *view = va_arg(va, graph_viewer_t *); + int cur_node = va_arg(va, int); + ret = on_changed_current(view, cur_node); + } + else + ret = 0; // allow selection change + break; + // + case grcode_creating_group: // a group is being created + if ( has_callback(GRCODE_HAVE_CREATING_GROUP) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + intvec_t *nodes = va_arg(va, intvec_t*); + ret = on_creating_group(g, nodes); + } + else + { + ret = 0; // Ok to create + } + break; + // + case grcode_deleting_group: // a group is being deleted + if ( has_callback(GRCODE_HAVE_DELETING_GROUP) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int old_group = va_arg(va, int); + ret = on_deleting_group(g, old_group); + } + else + { + ret = 0; // Ok to delete + } + break; + // + case grcode_group_visibility: // a group is being collapsed/uncollapsed + if ( has_callback(GRCODE_HAVE_GROUP_VISIBILITY) ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int group = va_arg(va, int); + bool expand = bool(va_arg(va, int)); + ret = on_group_visibility(g, group, expand); + } + else + { + ret = 0; // Ok. + } + break; + // + default: + ret = 0; + break; + } + //grcode_changed_graph, // new graph has been set + //grcode_user_size, // calculate node size for user-defined graph + //grcode_user_title, // render node title of a user-defined graph + //grcode_user_draw, // render node of a user-defined graph + return ret; +} + +//------------------------------------------------------------------------- +py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; + +bool pyg_show(PyObject *self) +{ + return py_graph_t::Show(self) != NULL; +} + +void pyg_close(PyObject *self) +{ + py_graph_t::Close(self); +} + +PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); +} + +void pyg_select_node(PyObject *self, int nid) +{ + py_graph_t::SelectNode(self, nid); +} +// +%} + +%inline %{ +// +void pyg_close(PyObject *self); +PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); +void pyg_select_node(PyObject *self, int nid); +bool pyg_show(PyObject *self); +// +%} + +%pythoncode %{ +# +class GraphViewer(CustomIDAMemo): + """This class wraps the user graphing facility provided by the graph.hpp file""" + def __init__(self, title, close_open = False): + """ + Constructs the GraphView object. + Please do not remove or rename the private fields + + @param title: The title of the graph window + @param close_open: Should it attempt to close an existing graph (with same title) before creating this graph? + """ + self._title = title + self._nodes = [] + self._edges = [] + self._close_open = close_open + + def AddNode(self, obj): + """Creates a node associated with the given object and returns the node id""" + id = len(self._nodes) + self._nodes.append(obj) + return id + + def AddEdge(self, src_node, dest_node): + """Creates an edge between two given node ids""" + self._edges.append( (src_node, dest_node) ) + + def Clear(self): + """Clears all the nodes and edges""" + self._nodes = [] + self._edges = [] + + + def __iter__(self): + return (self._nodes[index] for index in xrange(0, len(self._nodes))) + + + def __getitem__(self, idx): + """Returns a reference to the object associated with this node id""" + if idx >= len(self._nodes): + raise KeyError + else: + return self._nodes[idx] + + def Count(self): + """Returns the node count""" + return len(self._nodes) + + def Close(self): + """ + Closes the graph. + It is possible to call Show() again (which will recreate the graph) + """ + _idaapi.pyg_close(self) + + def Show(self): + """ + Shows an existing graph or creates a new one + + @return: Boolean + """ + if self._close_open: + frm = _idaapi.find_tform(self._title) + if frm: + _idaapi.close_tform(frm, 0) + return _idaapi.pyg_show(self) + + def Select(self, node_id): + """Selects a node on the graph""" + _idaapi.pyg_select_node(self, node_id) + + def AddCommand(self, title, hotkey): + """ + Deprecated: Use + - register_action() + - attach_action_to_popup() + """ + return _idaapi.pyg_add_command(self, title, hotkey) + + def OnRefresh(self): + """ + Event called when the graph is refreshed or first created. + From this event you are supposed to create nodes and edges. + This callback is mandatory. + + @note: ***It is important to clear previous nodes before adding nodes.*** + @return: Returning True tells the graph viewer to use the items. Otherwise old items will be used. + """ + self.Clear() + + return True +# +# def OnGetText(self, node_id): +# """ +# Triggered when the graph viewer wants the text and color for a given node. +# This callback is triggered one time for a given node (the value will be cached and used later without calling Python). +# When you call refresh then again this callback will be called for each node. +# +# This callback is mandatory. +# +# @return: Return a string to describe the node text or return a tuple (node_text, node_color) to describe both text and color +# """ +# return str(self[node_id]) +# +# def OnActivate(self): +# """ +# Triggered when the graph window gets the focus +# @return: None +# """ +# print "Activated...." +# +# def OnDeactivate(self): +# """Triggered when the graph window loses the focus +# @return: None +# """ +# print "Deactivated...." +# +# def OnSelect(self, node_id): +# """ +# Triggered when a node is being selected +# @return: Return True to allow the node to be selected or False to disallow node selection change +# """ +# # allow selection change +# return True +# +# def OnHint(self, node_id): +# """ +# Triggered when the graph viewer wants to retrieve hint text associated with a given node +# +# @return: None if no hint is avail or a string designating the hint +# """ +# return "hint for " + str(node_id) +# +# def OnClose(self): +# """Triggered when the graph viewer window is being closed +# @return: None +# """ +# print "Closing......." +# +# def OnClick(self, node_id): +# """ +# Triggered when a node is clicked +# @return: False to ignore the click and True otherwise +# """ +# print "clicked on", self[node_id] +# return True +# +# def OnDblClick(self, node_id): +# """ +# Triggerd when a node is double-clicked. +# @return: False to ignore the click and True otherwise +# """ +# print "dblclicked on", self[node_id] +# return True +# +# def OnCommand(self, cmd_id): +# """ +# Deprecated +# """ +# pass +# +# +%} diff --git a/swig/idaapi.i b/swig/idaapi.i index 850cf36f..1cc9c697 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -1,2785 +1,2785 @@ -%module(docstring="IDA Plugin SDK API wrapper",directors="1",threads="1") idaapi -// * http://swig.10945.n7.nabble.com/How-to-release-Python-GIL-td5027.html -// * http://stackoverflow.com/questions/1576737/releasing-python-gil-in-c-code -// * http://matt.eifelle.com/2007/11/23/enabling-thread-support-in-swig-and-python/ -%nothread; // We don't want SWIG to release the GIL for *every* IDA API call. -// Suppress 'previous definition of XX' warnings -#pragma SWIG nowarn=302 -// and others... -#pragma SWIG nowarn=312 -#pragma SWIG nowarn=325 -#pragma SWIG nowarn=314 -#pragma SWIG nowarn=362 -#pragma SWIG nowarn=383 -#pragma SWIG nowarn=389 -#pragma SWIG nowarn=401 -#pragma SWIG nowarn=451 -#pragma SWIG nowarn=454 // Setting a pointer/reference variable may leak memory - -%constant size_t SIZE_MAX = size_t(-1); -%{ - -#ifndef USE_DANGEROUS_FUNCTIONS - #define USE_DANGEROUS_FUNCTIONS 1 -#endif - -#include - -void raise_python_stl_bad_alloc(const std::bad_alloc &ba) -{ - Py_INCREF(PyExc_MemoryError); - PyErr_SetString(PyExc_MemoryError, "Out of memory (bad_alloc)"); -} - -void raise_python_unknown_exception() -{ - Py_INCREF(PyExc_RuntimeError); - PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); -} - -void raise_python_stl_exception(const std::exception &e) -{ - const char *what = e.what(); - if ( what == NULL || what[0] == '\0' ) - { - raise_python_unknown_exception(); - } - else - { - Py_INCREF(PyExc_RuntimeError); - PyErr_SetString(PyExc_RuntimeError, what); - } -} -%} - -%define %exception_set_default_handlers() -%exception { - try - { - $action - } - catch ( const std::bad_alloc &ba ) { raise_python_stl_bad_alloc(ba); SWIG_fail; } - catch ( const std::exception &e ) { raise_python_stl_exception(e); SWIG_fail; } - catch (...) { raise_python_unknown_exception(); SWIG_fail; } -} -%enddef -%exception_set_default_handlers(); - -// Enable automatic docstring generation -%feature(autodoc,0); - -%{ -/* strnlen() arrived on OSX at v10.7. Provide it ourselves if needed. */ -#ifdef __MAC__ -#ifndef MAC_OS_X_VERSION_10_7 -#define MAC_OS_X_VERSION_10_7 1070 -#endif -#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) -inline size_t strnlen(const char *s, size_t maxlen) -{ - const char *found = (const char *) memchr(s, 0, maxlen); - return found != NULL ? size_t(found - s) : maxlen; -} -#endif -#endif -%} - -%define SWIG_DECLARE_PY_CLINKED_OBJECT(type) -%inline %{ -static PyObject *type##_create() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new type(), NULL); -} -static bool type##_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - delete (type *)PyCObject_AsVoidPtr(py_obj); - return true; -} -static type *type##_get_clink(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return (type *)pyobj_get_clink(self); -} -static PyObject *type##_get_clink_ptr(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyLong_FromUnsignedLongLong( - (unsigned PY_LONG_LONG)pyobj_get_clink(self)); -} -%} -%enddef - -// We use those special maps because SWIG wraps passed PyObject* with 'SwigPtr_PyObject' and 'SwigVar_PyObject' -// They act like autoptr and decrement the reference of the object when the scope ends -// We need to keep a reference outside SWIG and let the caller manage its references -%typemap(directorin) PyObject * "/*%din%*/Py_XINCREF($1_name);$input = $1_name;" -%typemap(directorout) PyObject * "/*%dout%*/$result = result;Py_XINCREF($result);" - -%{ -#include - -#ifdef HAVE_SSIZE_T -#define _SSIZE_T_DEFINED 1 -#endif - -#if defined(__NT__) && !defined(_WINDOWS_) - #define _WINDOWS_ // kernwin.hpp needs it to declare create_tform() - typedef void *HWND; // we don't need to include windows.h for just this definition -#endif - -#include "ida.hpp" -#include "idp.hpp" -#include "allins.hpp" -#include "auto.hpp" -#include "bytes.hpp" -#include "dbg.hpp" -#include "diskio.hpp" -#include "entry.hpp" -#include "enum.hpp" -#include "expr.hpp" -#include "frame.hpp" -#include "fixup.hpp" -#include "funcs.hpp" -#include "gdl.hpp" -#include "idd.hpp" -#include "ints.hpp" -#include "kernwin.hpp" -#include "lines.hpp" -#include "loader.hpp" -#include "moves.hpp" -#include "netnode.hpp" -#include "nalt.hpp" -#include "name.hpp" -#include "offset.hpp" -#include "queue.hpp" -#include "search.hpp" -#include "srarea.hpp" -#include "strlist.hpp" -#include "struct.hpp" -#include "typeinf.hpp" -#include "registry.hpp" -#include "ua.hpp" -#include "xref.hpp" -#include "ieee.h" -#include "err.h" -#include "fpro.h" -#include -#include "graph.hpp" -#ifdef WITH_HEXRAYS -#include "hexrays.hpp" -#endif -#include "pywraps.hpp" - -// - -//------------------------------------------------------------------------ -// String constants used -static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; -static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; -static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; -static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; - -// Constants used by get_idaapi_class_reference() -#define PY_CLSID_CVT_INT64 0 -#define PY_CLSID_APPCALL_SKEL_OBJ 1 -#define PY_CLSID_CVT_BYREF 2 -#define PY_CLSID_LAST 3 - -//--------------------------------------------------------------------------- -// Use these macros to define script<->C fields -#define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt } -#define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0) -#define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1) - -//--------------------------------------------------------------------------- -enum scfield_types_t -{ - // Numeric fields - FT_FIRST_NUM, - FT_INT, - FT_SIZET, - FT_SSIZET, - FT_NUM16, - FT_NUM32, - FT_LAST_NUM, - // String field - FT_STR, - FT_CHAR, - // Object fields - FT_ARR, - // Allocated array of strings - FT_STRARR, - // Allocated array of 16bit numbers - FT_NUM16ARR, - // Fixed size character array. The size must be passed in the definition - FT_CHRARR_STATIC, -}; - -//--------------------------------------------------------------------------- -struct scfld_t -{ - const char *field_name; - uint32 field_type; - size_t field_offs; - bool is_optional; -}; - -#define FT_VALUE_MASK 0xFFFF0000 -// Possible return values of conversion functions -#define FT_NOT_FOUND -1 -#define FT_BAD_TYPE -2 -#define FT_OK 1 - -//------------------------------------------------------------------------- -Py_ssize_t pyvar_walk_list( - const ref_t &py_list, - int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), - void *ud) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - Py_ssize_t size = CIP_FAILED; - do - { - PyObject *o = py_list.o; - if ( !PyList_CheckExact(o) && !PyW_IsSequenceType(o) ) - break; - - bool is_seq = !PyList_CheckExact(o); - size = is_seq ? PySequence_Size(o) : PyList_Size(o); - if ( cb == NULL ) - break; - - Py_ssize_t i; - for ( i=0; i= CIP_OK; - if ( !ok ) - PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); - return ok; -} - -//------------------------------------------------------------------------ -static idc_class_t *get_py_idc_cvt_opaque() -{ - return find_idc_class(S_PY_IDC_OPAQUE_T); -} - -//------------------------------------------------------------------------- -// Utility function to create opaque / convertible Python <-> IDC variables -// The referred Python variable will have its reference increased -static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Create an IDC object of this special helper class - if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) - return false; - - // Store the CVT id - idc_value_t idc_val; - idc_val.set_long(PY_ICID_OPAQUE); - VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); - - // Store the value as a PVOID referencing the given Python object - py_var.incref(); - idc_val.set_pvoid(py_var.o); - VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); - - return true; -} - -//------------------------------------------------------------------------ -// IDC Opaque object destructor: when the IDC object dies we kill the -// opaque Python object along with it -static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; -static error_t idaapi py_idc_opaque_dtor( - idc_value_t *argv, - idc_value_t * /*res*/) -{ - // This can be called at plugin registration time, when a - // 'script_plugin_t' instance is ::free()'d. It is - // not guaranteed that we have the GIL at that point. - PYW_GIL_GET; - - // Get the value from the object - idc_value_t idc_val; - VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); - - // Extract the Python object reference - PyObject *py_obj = (PyObject *)idc_val.pvoid; - - // Decrease its reference (and eventually destroy it) - Py_DECREF(py_obj); - - return eOk; -} - -//------------------------------------------------------------------------- -// Converts a Python variable into an IDC variable -// This function returns on one CIP_XXXX -int pyvar_to_idcvar( - const ref_t &py_var, - idc_value_t *idc_var, - int *gvar_sn) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // None / NULL - if ( py_var == NULL || py_var.o == Py_None ) - { - idc_var->set_long(0); - } - // Numbers? - else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) - { - return CIP_OK; - } - // String - else if ( PyString_Check(py_var.o) ) - { - idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); - } - // Boolean - else if ( PyBool_Check(py_var.o) ) - { - idc_var->set_long(py_var.o == Py_True ? 1 : 0); - } - // Float - else if ( PyFloat_Check(py_var.o) ) - { - double dresult = PyFloat_AsDouble(py_var.o); - ieee_realcvt((void *)&dresult, idc_var->e, 3); - idc_var->vtype = VT_FLOAT; - } - // void* - else if ( PyCObject_Check(py_var.o) ) - { - idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); - } - // Python list? - else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) - { - // Create the object - VarObject(idc_var); - - // Determine list size and type - bool is_seq = !PyList_CheckExact(py_var.o); - Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); - bool ok = true; - qstring attr_name; - - // Convert each item - for ( Py_ssize_t i=0; i= CIP_OK; - if ( ok ) - { - // Form the attribute name - newref_t py_int(PyInt_FromSsize_t(i)); - ok = PyW_ObjectToString(py_int.o, &attr_name); - if ( !ok ) - break; - // Store the attribute - VarSetAttr(idc_var, attr_name.c_str(), &v); - } - if ( !ok ) - break; - } - return ok ? CIP_OK : CIP_FAILED; - } - // Dictionary: we convert to an IDC object - else if ( PyDict_Check(py_var.o) ) - { - // Create an empty IDC object - VarObject(idc_var); - - // Get the dict.items() list - newref_t py_items(PyDict_Items(py_var.o)); - - // Get the size of the list - qstring key_name; - bool ok = true; - Py_ssize_t size = PySequence_Size(py_items.o); - for ( Py_ssize_t i=0; i (key, value) - PyObject *py_item = PyList_GetItem(py_items.o, i); - - // Extract key/value - newref_t key(PySequence_GetItem(py_item, 0)); - newref_t val(PySequence_GetItem(py_item, 1)); - - // Get key's string representation - PyW_ObjectToString(key.o, &key_name); - - // Convert the attribute into an IDC value - idc_value_t v; - ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; - if ( ok ) - { - // Store the attribute - VarSetAttr(idc_var, key_name.c_str(), &v); - } - if ( !ok ) - break; - } - return ok ? CIP_OK : CIP_FAILED; - } - // Possible function? - else if ( PyCallable_Check(py_var.o) ) - { - idc_var->clear(); - idc_var->vtype = VT_FUNC; - idc_var->funcidx = -1; // Does not apply - return CIP_OK; - } - // Objects: - // - pyidc_cvt objects: int64, byref, opaque - // - other python objects - else - { - // Get the type - int cvt_id = get_pyidc_cvt_type(py_var.o); - switch ( cvt_id ) - { - // - // INT64 - // - case PY_ICID_INT64: - { - // Get the value attribute - ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); - if ( attr == NULL ) - return false; - idc_var->set_int64(PyLong_AsLongLong(attr.o)); - return CIP_OK; - } - // - // BYREF - // - case PY_ICID_BYREF: - { - // BYREF always require this parameter - if ( gvar_sn == NULL ) - return CIP_FAILED; - - // Get the value attribute - ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); - if ( attr == NULL ) - return CIP_FAILED; - - // Create a global variable - char buf[MAXSTR]; - qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); - idc_value_t *gvar = add_idc_gvar(buf); - // Convert the python value into the IDC global variable - bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; - if ( ok ) - { - (*gvar_sn)++; - // Create a reference to this global variable - VarRef(idc_var, gvar); - } - return ok ? CIP_OK : CIP_FAILED; - } - // - // OPAQUE - // - case PY_ICID_OPAQUE: - { - if ( !wrap_PyObject_ptr(py_var, idc_var) ) - return CIP_FAILED; - return CIP_OK_OPAQUE; - } - // - // Other objects - // - default: - // A normal object? - newref_t py_dir(PyObject_Dir(py_var.o)); - Py_ssize_t size = PyList_Size(py_dir.o); - if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) - return CIP_FAILED; - // Create the IDC object - VarObject(idc_var); - for ( Py_ssize_t i=0; i 2 ) - && (strncmp(field_name, "__", 2) == 0 ) - && (strncmp(field_name+len-2, "__", 2) == 0) ) - { - continue; - } - - idc_value_t v; - // Get the non-private attribute from the object - newref_t attr(PyObject_GetAttrString(py_var.o, field_name)); - if (attr == NULL - // Convert the attribute into an IDC value - || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK) - { - return CIP_FAILED; - } - - // Store the attribute - VarSetAttr(idc_var, field_name, &v); - } - } - } - return CIP_OK; -} - -//------------------------------------------------------------------------- -inline PyObject *cvt_to_pylong(int32 v) -{ - return PyLong_FromLong(v); -} - -inline PyObject *cvt_to_pylong(int64 v) -{ - return PyLong_FromLongLong(v); -} - -//------------------------------------------------------------------------- -// Converts an IDC variable to a Python variable -// If py_var points to an existing object then the object will be updated -// If py_var points to an existing immutable object then ZERO is returned -// Returns one of CIP_xxxx. Check pywraps.hpp -int idcvar_to_pyvar( - const idc_value_t &idc_var, - ref_t *py_var) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - switch ( idc_var.vtype ) - { - case VT_PVOID: - if ( *py_var == NULL ) - { - newref_t nr(PyCObject_FromVoidPtr(idc_var.pvoid, NULL)); - *py_var = nr; - } - else - { - return CIP_IMMUTABLE; - } - break; - - case VT_INT64: - { - // Recycle? - if ( *py_var != NULL ) - { - // Recycling an int64 object? - int t = get_pyidc_cvt_type(py_var->o); - if ( t != PY_ICID_INT64 ) - return CIP_IMMUTABLE; // Cannot recycle immutable object - // Update the attribute - PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); - return CIP_OK; - } - ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); - if ( py_cls == NULL ) - return CIP_FAILED; - *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); - if ( PyW_GetError() || *py_var == NULL ) - return CIP_FAILED; - break; - } - -#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) - case VT_STR: - *py_var = newref_t(PyString_FromString(idc_var.str)); - break; - -#endif - case VT_STR2: - if ( *py_var == NULL ) - { - const qstring &s = idc_var.qstr(); - *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); - break; - } - else - return CIP_IMMUTABLE; // Cannot recycle immutable object - case VT_LONG: - // Cannot recycle immutable objects - if ( *py_var != NULL ) - return CIP_IMMUTABLE; - *py_var = newref_t(cvt_to_pylong(idc_var.num)); - break; - case VT_FLOAT: - if ( *py_var == NULL ) - { - double x; - if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) - INTERR(30160); - - *py_var = newref_t(PyFloat_FromDouble(x)); - break; - } - else - return CIP_IMMUTABLE; - - case VT_REF: - { - if ( *py_var == NULL ) - { - ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); - if ( py_cls == NULL ) - return CIP_FAILED; - - // Create a byref object with None value. We populate it later - *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); - if ( PyW_GetError() || *py_var == NULL ) - return CIP_FAILED; - } - int t = get_pyidc_cvt_type(py_var->o); - if ( t != PY_ICID_BYREF ) - return CIP_FAILED; - - // Dereference - // (Since we are not using VREF_COPY flag, we can safely const_cast) - idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); - if ( dref_v == NULL ) - return CIP_FAILED; - - // Can we recycle the object? - ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); - if ( new_py_val != NULL ) - { - // Recycle - t = idcvar_to_pyvar(*dref_v, &new_py_val); - - // Success? Nothing more to be done - if ( t == CIP_OK ) - return CIP_OK; - - // Clear it so we don't recycle it - new_py_val = ref_t(); - } - // Try to convert (not recycle) - if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) - return CIP_FAILED; - - // Update the attribute - PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); - break; - } - - // Can convert back into a Python object or Python dictionary - // (Depending if py_var will be recycled and it was a dictionary) - case VT_OBJ: - { - // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields - idc_value_t idc_val; - if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk - && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) - { - // Extract the object - *py_var = borref_t((PyObject *) idc_val.pvoid); - return CIP_OK_OPAQUE; - } - ref_t obj; - bool is_dict = false; - - // Need to create a new object? - if ( *py_var == NULL ) - { - // Get skeleton class reference - ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); - if ( py_cls == NULL ) - return CIP_FAILED; - - // Call constructor - obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); - if ( PyW_GetError() || obj == NULL ) - return CIP_FAILED; - } - else - { - // Recycle existing variable - obj = *py_var; - if ( PyDict_Check(obj.o) ) - is_dict = true; - } - - // Walk the IDC attributes and store into python - for (const char *attr_name = VarFirstAttr(&idc_var); - attr_name != NULL; - attr_name = VarNextAttr(&idc_var, attr_name) ) - { - // Get the attribute - idc_value_t v; - VarGetAttr(&idc_var, attr_name, &v, true); - - // Convert attribute to a python value (recursively) - ref_t py_attr; - int cvt = idcvar_to_pyvar(v, &py_attr); - if ( cvt <= CIP_IMMUTABLE ) - return CIP_FAILED; - if ( is_dict ) - PyDict_SetItemString(obj.o, attr_name, py_attr.o); - else - PyObject_SetAttrString(obj.o, attr_name, py_attr.o); - } - *py_var = obj; - break; - } - // Unhandled type - default: - *py_var = ref_t(); - return CIP_FAILED; - } - return CIP_OK; -} - -//------------------------------------------------------------------------- -// Converts IDC arguments to Python argument list or just one tuple -// If 'decref' is NULL then 'pargs' will contain one element which is the tuple -bool pyw_convert_idc_args( - const idc_value_t args[], - int nargs, - ref_vec_t &pargs, - bool as_tupple, - char *errbuf, - size_t errbufsize) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - ref_t py_tuple; - - pargs.qclear(); - - if ( as_tupple ) - { - py_tuple = newref_t(PyTuple_New(nargs)); - if ( py_tuple == NULL ) - { - if ( errbuf != 0 && errbufsize > 0 ) - qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); - return false; - } - } - - for ( int i=0; i 0 ) - qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype); - return false; - } - - if ( as_tupple ) - { - // PyTuple_SetItem() steals the reference. - py_obj.incref(); - QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0); - } - else - { - pargs.push_back(py_obj); - } - } - - // Add the tuple to the list of args only now. Doing so earlier will - // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will - // cause 'PyTuple_SetItem()' to fail. - if ( as_tupple ) - pargs.push_back(py_tuple); - - return true; -} - - - - -//------------------------------------------------------------------------ -// String constants used -static const char S_PYINVOKE0[] = "_py_invoke0"; -static const char S_PY_SWIEX_CLSNAME[] = "switch_info_ex_t"; -static const char S_PY_OP_T_CLSNAME[] = "op_t"; -static const char S_PROPS[] = "props"; -static const char S_NAME[] = "name"; -static const char S_TITLE[] = "title"; -static const char S_ASM_KEYWORD[] = "asm_keyword"; -static const char S_MENU_NAME[] = "menu_name"; -static const char S_HOTKEY[] = "hotkey"; -static const char S_EMBEDDED[] = "embedded"; -static const char S_POPUP_NAMES[] = "popup_names"; -static const char S_FLAGS[] = "flags"; -static const char S_VALUE_SIZE[] = "value_size"; -static const char S_MAY_CREATE_AT[] = "may_create_at"; -static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; -static const char S_ID[] = "id"; -static const char S_PRINTF[] = "printf"; -static const char S_TEXT_WIDTH[] = "text_width"; -static const char S_SCAN[] = "scan"; -static const char S_ANALYZE[] = "analyze"; -static const char S_CBSIZE[] = "cbsize"; -static const char S_ON_CLICK[] = "OnClick"; -static const char S_ON_CLOSE[] = "OnClose"; -static const char S_ON_DBL_CLICK[] = "OnDblClick"; -static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; -static const char S_ON_KEYDOWN[] = "OnKeydown"; -static const char S_ON_COMPLETE_LINE[] = "OnCompleteLine"; -static const char S_ON_CREATE[] = "OnCreate"; -static const char S_ON_POPUP[] = "OnPopup"; -static const char S_ON_HINT[] = "OnHint"; -static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; -static const char S_ON_EDIT_LINE[] = "OnEditLine"; -static const char S_ON_INSERT_LINE[] = "OnInsertLine"; -static const char S_ON_GET_LINE[] = "OnGetLine"; -static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; -static const char S_ON_REFRESH[] = "OnRefresh"; -static const char S_ON_REFRESHED[] = "OnRefreshed"; -static const char S_ON_EXECUTE_LINE[] = "OnExecuteLine"; -static const char S_ON_SELECT_LINE[] = "OnSelectLine"; -static const char S_ON_SELECTION_CHANGE[] = "OnSelectionChange"; -static const char S_ON_COMMAND[] = "OnCommand"; -static const char S_ON_GET_ICON[] = "OnGetIcon"; -static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; -static const char S_ON_GET_SIZE[] = "OnGetSize"; -static const char S_ON_GETTEXT[] = "OnGetText"; -static const char S_ON_ACTIVATE[] = "OnActivate"; -static const char S_ON_DEACTIVATE[] = "OnDeactivate"; -static const char S_ON_SELECT[] = "OnSelect"; -static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; -static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; -static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; -static const char S_M_EDGES[] = "_edges"; -static const char S_M_NODES[] = "_nodes"; -static const char S_M_THIS[] = "_this"; -static const char S_M_TITLE[] = "_title"; -static const char S_CLINK_NAME[] = "__clink__"; -static const char S_ON_VIEW_ACTIVATED[] = "OnViewActivated"; -static const char S_ON_VIEW_DEACTIVATED[] = "OnViewDeactivated"; -static const char S_ON_VIEW_KEYDOWN[] = "OnViewKeydown"; -static const char S_ON_VIEW_CLICK[] = "OnViewClick"; -static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick"; -static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos"; -static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched"; -static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver"; -static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved"; - - -#ifdef __PYWRAPS__ -static const char S_PY_IDAAPI_MODNAME[] = "__main__"; -#else -static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; -#endif - -//------------------------------------------------------------------------ -static ref_t py_cvt_helper_module; -static bool pywraps_initialized = false; - -//--------------------------------------------------------------------------- -// Context structure used by add|del_menu_item() -struct py_add_del_menu_item_ctx -{ - qstring menupath; - PyObject *cb_data; -}; - -//--------------------------------------------------------------------------- -// Context structure used by add|del_idc_hotkey() -struct py_idchotkey_ctx_t -{ - qstring hotkey; - PyObject *pyfunc; -}; - -//--------------------------------------------------------------------------- -// Context structure used by register/unregister timer -struct py_timer_ctx_t -{ - qtimer_t timer_id; - PyObject *pycallback; -}; - -//------------------------------------------------------------------------ -// check if we have a file which is known to be executed automatically -// by SWIG or Python runtime -bool pywraps_check_autoscripts(char *buf, size_t bufsize) -{ - static const char *const exts[] = - { - "py", - "pyc", - "pyd", - "pyo", - "pyw", - }; - - static const char *const fns[] = - { - "swig_runtime_data" SWIG_RUNTIME_VERSION, - "sitecustomize", - "usercustomize" - }; - - for ( size_t ifn=0; ifn < qnumber(fns); ++ifn ) - { - // check for a script or module with several possible extensions - for ( size_t iext=0; iext < qnumber(exts); ++iext ) - { - qsnprintf(buf, bufsize, "%s.%s", fns[ifn], exts[iext]); - if ( qfileexist(buf) ) - return true; - } - // check for a subdirectory under current directory - if ( qfileexist(fns[ifn]) ) - { - qstrncpy(buf, fns[ifn], bufsize); - return true; - } - } - return false; -} - -//------------------------------------------------------------------------ -error_t PyW_CreateIdcException(idc_value_t *res, const char *msg) -{ - // Create exception object - VarObject(res, find_idc_class("exception")); - - // Set the message field - idc_value_t v; - v.set_string(msg); - VarSetAttr(res, "message", &v); - - // Throw exception - return set_qerrno(eExecThrow); -} - -//------------------------------------------------------------------------ -// Calls a Python callable encoded in IDC.pvoid member -static const char idc_py_invoke0_args[] = { VT_PVOID, 0 }; -static error_t idaapi idc_py_invoke0( - idc_value_t *argv, - idc_value_t *res) -{ - PYW_GIL_GET; - PyObject *pyfunc = (PyObject *) argv[0].pvoid; - newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); - - // Report Python error as IDC exception - qstring err; - error_t err_code = eOk; - if ( PyW_GetError(&err) ) - err_code = PyW_CreateIdcException(res, err.c_str()); - return err_code; -} - -//------------------------------------------------------------------------ -// This function must be called on initialization -bool init_pywraps() -{ - if ( pywraps_initialized ) - return true; - - // Take a reference to the idaapi python module - // (We need it to create instances of certain classes) - if ( py_cvt_helper_module == NULL ) - { - // Take a reference to the module so we can create the needed class instances - py_cvt_helper_module = PyW_TryImportModule(S_PY_IDAAPI_MODNAME); - if ( py_cvt_helper_module == NULL ) - return false; - } - - // Register the IDC PyInvoke0 method (helper function for add_idc_hotkey()) - if ( !set_idc_func_ex(S_PYINVOKE0, idc_py_invoke0, idc_py_invoke0_args, 0) ) - return false; - - // IDC opaque class not registered? - if ( get_py_idc_cvt_opaque() == NULL ) - { - // Add the class - idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); - if ( idc_cvt_opaque == NULL ) - return false; - - // Form the dtor name - char dtor_name[MAXSTR]; - qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); - - // Register the dtor function - if ( !set_idc_func_ex(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args, 0) ) - return false; - - // Link the dtor function to the class - set_idc_dtor(idc_cvt_opaque, dtor_name); - } - - pywraps_initialized = true; - return true; -} - -//------------------------------------------------------------------------ -// This function must be called on de-initialization -void deinit_pywraps() -{ - if ( !pywraps_initialized ) - return; - - pywraps_initialized = false; - - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - py_cvt_helper_module = ref_t(); // Deref. - } - - // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) - set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); -} - -//------------------------------------------------------------------------ -// Utility function to create a class instance whose constructor takes zero arguments -ref_t create_idaapi_class_instance0(const char *clsname) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_cls(get_idaapi_attr(clsname)); - if ( py_cls == NULL ) - return ref_t(); - - ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); - if ( PyW_GetError() || py_obj == NULL ) - py_obj = ref_t(); - return py_obj; -} - -//------------------------------------------------------------------------ -// Utility function to create linked class instances -ref_t create_idaapi_linked_class_instance( - const char *clsname, - void *lnk) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_cls(get_idaapi_attr(clsname)); - if ( py_cls == NULL ) - return ref_t(); - - newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); - ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, py_lnk.o, NULL)); - if ( PyW_GetError() || py_obj == NULL ) - py_obj = ref_t(); - return py_obj; -} - -//------------------------------------------------------------------------ -// Gets a class type reference in idaapi -// With the class type reference we can create a new instance of that type -// This function takes a reference to the idaapi module and keeps the reference -ref_t get_idaapi_attr_by_id(const int class_id) -{ - if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) - return ref_t(); - - // Some class names. The array is parallel with the PY_CLSID_xxx consts - static const char *class_names[]= - { - "PyIdc_cvt_int64__", - "object_t", - "PyIdc_cvt_refclass__" - }; - PYW_GIL_CHECK_LOCKED_SCOPE(); - return newref_t(PyObject_GetAttrString(py_cvt_helper_module.o, class_names[class_id])); -} - -//------------------------------------------------------------------------ -// Gets a class reference by name -ref_t get_idaapi_attr(const char *attrname) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return py_cvt_helper_module == NULL - ? ref_t() - : PyW_TryGetAttrString(py_cvt_helper_module.o, attrname); -} - -//------------------------------------------------------------------------ -// Returns a qstring from an object attribute -bool PyW_GetStringAttr( - PyObject *py_obj, - const char *attr_name, - qstring *str) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); - if ( py_attr == NULL ) - return false; - - bool ok = PyString_Check(py_attr.o) != 0; - if ( ok ) - *str = PyString_AsString(py_attr.o); - - return ok; -} - -//------------------------------------------------------------------------ -// Returns an attribute or NULL -// No errors will be set if the attribute did not exist -ref_t PyW_TryGetAttrString(PyObject *py_obj, const char *attr) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t o; - if ( PyObject_HasAttrString(py_obj, attr) ) - o = newref_t(PyObject_GetAttrString(py_obj, attr)); - return o; -} - -//------------------------------------------------------------------------ -// Tries to import a module and clears the exception on failure -ref_t PyW_TryImportModule(const char *name) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t result(PyImport_ImportModule(name)); - if ( result == NULL && PyErr_Occurred() ) - PyErr_Clear(); - return result; -} - -//------------------------------------------------------------------------- -// Converts a Python number into an IDC value (32 or 64bits) -// The function will first try to convert the number into a 32bit value -// If the number does not fit then VT_INT64 will be used -// NB: This function cannot properly detect if the Python value should be -// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which -// can fit in a C long but Python creates a PyLong object for it. -// And because of that we are confused as to whether to convert to 32 or 64 -bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = true; - do - { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - { - rc = false; - break; - } - - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - break; - } - // Clear last error - PyErr_Clear(); - // Can be fit into a C unsigned long? - l = (long) PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - break; - } - PyErr_Clear(); - idc_var->set_int64(PyLong_AsLongLong(py_var)); - } while ( false ); - return rc; -} - -//------------------------------------------------------------------------- -// Parses a Python object as a long or long long -bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = true; -#define SETNUM(numexpr, is64_expr) \ - do \ - { \ - if ( num != NULL ) \ - *num = numexpr; \ - if ( is_64 != NULL ) \ - *is_64 = is64_expr; \ - } while ( false ) - - do - { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - { - rc = false; - break; - } - - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(l), false); - break; - } - - // Clear last error - PyErr_Clear(); - - // Can be fit into a C unsigned long? - unsigned long ul = PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(ul), false); - break; - } - PyErr_Clear(); - - // Try to parse as int64 - PY_LONG_LONG ll = PyLong_AsLongLong(py_var); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(ll), true); - break; - } - PyErr_Clear(); - - // Try to parse as uint64 - unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); - PyObject *err = PyErr_Occurred(); - if ( err == NULL ) - { - SETNUM(uint64(ull), true); - break; - } - // Negative number? _And_ it with uint64(-1) - rc = false; - if ( err == PyExc_TypeError ) - { - newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); - newref_t py_num(PyNumber_And(py_var, py_mask.o)); - if ( py_num != NULL && py_mask != NULL ) - { - PyErr_Clear(); - ull = PyLong_AsUnsignedLongLong(py_num.o); - if ( !PyErr_Occurred() ) - { - SETNUM(uint64(ull), true); - rc = true; - } - } - } - PyErr_Clear(); - } while ( false ); - return rc; -#undef SETNUM -} - -//------------------------------------------------------------------------- -// Checks if a given object is of sequence type -bool PyW_IsSequenceType(PyObject *obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = true; - do - { - if ( !PySequence_Check(obj) ) - { - rc = false; - break; - } - - Py_ssize_t sz = PySequence_Size(obj); - if ( sz == -1 || PyErr_Occurred() != NULL ) - { - PyErr_Clear(); - rc = false; - break; - } - } while ( false ); - return rc; -} - -//------------------------------------------------------------------------- -// Returns the string representation of an object -bool PyW_ObjectToString(PyObject *obj, qstring *out) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_str(PyObject_Str(obj)); - bool ok = py_str != NULL; - if ( ok ) - *out = PyString_AsString(py_str.o); - else - out->qclear(); - return ok; -} - -//-------------------------------------------------------------------------- -// Checks if a Python error occured and fills the out parameter with the -// exception string -bool PyW_GetError(qstring *out, bool clear_err) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( PyErr_Occurred() == NULL ) - return false; - - // Error occurred but details not needed? - if ( out == NULL ) - { - // Just clear the error - if ( clear_err ) - PyErr_Clear(); - return true; - } - - // Get the exception info - PyObject *err_type, *err_value, *err_traceback, *py_ret(NULL); - PyErr_Fetch(&err_type, &err_value, &err_traceback); - - if ( !clear_err ) - PyErr_Restore(err_type, err_value, err_traceback); - - // Resolve FormatExc() - ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); - - // Helper there? - if ( py_fmtexc != NULL ) - { - // Call helper - py_ret = PyObject_CallFunctionObjArgs( - py_fmtexc.o, - err_type, - err_value, - err_traceback, - NULL); - } - - // Clear the error - if ( clear_err ) - PyErr_Clear(); - - // Helper failed?! - if ( py_ret == NULL ) - { - // Just convert the 'value' part of the original error - py_ret = PyObject_Str(err_value); - } - - // No exception text? - if ( py_ret == NULL ) - { - *out = "IDAPython: unknown error!"; - } - else - { - *out = PyString_AsString(py_ret); - Py_DECREF(py_ret); - } - - if ( clear_err ) - { - Py_XDECREF(err_traceback); - Py_XDECREF(err_value); - Py_XDECREF(err_type); - } - return true; -} - -//------------------------------------------------------------------------- -bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstring s; - if ( !PyW_GetError(&s, clear_err) ) - return false; - - qstrncpy(buf, s.c_str(), bufsz); - return true; -} - -//------------------------------------------------------------------------- -// A loud version of PyGetError() which gets the error and displays it -// This method is used to display errors that occurred in a callback -bool PyW_ShowCbErr(const char *cb_name) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - static qstring err_str; - if ( !PyW_GetError(&err_str) ) - return false; - - warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); - return true; -} - -//--------------------------------------------------------------------------- -void *pyobj_get_clink(PyObject *pyobj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Try to query the link attribute - ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); - void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; - return t; -} - - - -//------------------------------------------------------------------------ - -//------------------------------------------------------------------------ -class pywraps_notify_when_t -{ - ref_vec_t table[NW_EVENTSCNT]; - qstring err; - bool in_notify; - struct notify_when_args_t - { - int when; - PyObject *py_callable; - }; - typedef qvector notify_when_args_vec_t; - notify_when_args_vec_t delayed_notify_when_list; - - //------------------------------------------------------------------------ - static int idaapi idp_callback(void *ud, int event_id, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; - switch ( event_id ) - { - case processor_t::newfile: - case processor_t::oldfile: - { - int old = event_id == processor_t::oldfile ? 1 : 0; - char *dbname = va_arg(va, char *); - _this->notify(NW_OPENIDB_SLOT, old); - } - break; - case processor_t::closebase: - _this->notify(NW_CLOSEIDB_SLOT); - break; - } - // event not processed, let other plugins or the processor module handle it - return 0; - } - - //------------------------------------------------------------------------ - bool unnotify_when(int when, PyObject *py_callable) - { - int cnt = 0; - for ( int slot=0; slot 0; - } - - //------------------------------------------------------------------------ - void register_callback(int slot, PyObject *py_callable) - { - borref_t callable_ref(py_callable); - ref_vec_t &tbl = table[slot]; - ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); - - // Already added - if ( it != it_end ) - return; - - // Insert the element - tbl.push_back(callable_ref); - } - - //------------------------------------------------------------------------ - void unregister_callback(int slot, PyObject *py_callable) - { - borref_t callable_ref(py_callable); - ref_vec_t &tbl = table[slot]; - ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); - - // Not found? - if ( it == it_end ) - return; - - // Delete the element - tbl.erase(it); - } - -public: - //------------------------------------------------------------------------ - bool init() - { - return hook_to_notification_point(HT_IDP, idp_callback, this); - } - - //------------------------------------------------------------------------ - bool deinit() - { - // Uninstall all objects - ref_vec_t::iterator it, it_end; - for ( int slot=0; sloto); - } - // ...and remove the notification - return unhook_from_notification_point(HT_IDP, idp_callback, this); - } - - //------------------------------------------------------------------------ - bool notify_when(int when, PyObject *py_callable) - { - // While in notify() do not allow insertion or deletion to happen on the spot - // Instead we will queue them so that notify() will carry the action when it finishes - // dispatching the notification handlers - if ( in_notify ) - { - notify_when_args_t &args = delayed_notify_when_list.push_back(); - args.when = when; - args.py_callable = py_callable; - return true; - } - // Uninstalling the notification? - if ( (when & NW_REMOVE) != 0 ) - return unnotify_when(when & ~NW_REMOVE, py_callable); - - int cnt = 0; - for ( int slot=0; slot 0; - } - - //------------------------------------------------------------------------ - bool notify(int slot, ...) - { - va_list va; - va_start(va, slot); - bool ok = notify_va(slot, va); - va_end(va); - return ok; - } - - //------------------------------------------------------------------------ - bool notify_va(int slot, va_list va) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Sanity bounds check! - if ( slot < 0 || slot >= NW_EVENTSCNT ) - return false; - - bool ok = true; - in_notify = true; - int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; - - { - for (ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); - it != it_end; - ++it) - { - // Form the notification code - newref_t py_code(PyInt_FromLong(1 << slot)); - ref_t py_result; - switch ( slot ) - { - case NW_CLOSEIDB_SLOT: - case NW_INITIDA_SLOT: - case NW_TERMIDA_SLOT: - { - py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); - break; - } - case NW_OPENIDB_SLOT: - { - newref_t py_old(PyInt_FromLong(old)); - py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); - } - break; - } - if ( PyW_GetError(&err) || py_result == NULL ) - { - PyErr_Clear(); - warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); - ok = false; - } - } - } - in_notify = false; - - // Process any delayed notify_when() calls that - if ( !delayed_notify_when_list.empty() ) - { - for (notify_when_args_vec_t::iterator it = delayed_notify_when_list.begin(), it_end=delayed_notify_when_list.end(); - it != it_end; - ++it) - { - notify_when(it->when, it->py_callable); - } - delayed_notify_when_list.qclear(); - } - - return ok; - } - - //------------------------------------------------------------------------ - pywraps_notify_when_t() - { - in_notify = false; - } -}; - -static pywraps_notify_when_t *g_nw = NULL; - -//------------------------------------------------------------------------ -// Initializes the notify_when mechanism -// (Normally called by IDAPython plugin.init()) -bool pywraps_nw_init() -{ - if ( g_nw != NULL ) - return true; - - g_nw = new pywraps_notify_when_t(); - if ( g_nw->init() ) - return true; - - // Things went bad, undo! - delete g_nw; - g_nw = NULL; - return false; -} - -//------------------------------------------------------------------------ -bool pywraps_nw_notify(int slot, ...) -{ - if ( g_nw == NULL ) - return false; - - // Appears to be called from 'driver_notifywhen.cpp', which - // itself is called from possibly non-python code. - // I.e., we must acquire the GIL. - PYW_GIL_GET; - va_list va; - va_start(va, slot); - bool ok = g_nw->notify_va(slot, va); - va_end(va); - - return ok; -} - -//------------------------------------------------------------------------ -// Deinitializes the notify_when mechanism -bool pywraps_nw_term() -{ - if ( g_nw == NULL ) - return true; - - // If could not deinitialize then return w/o stopping nw - if ( !g_nw->deinit() ) - return false; - - // Cleanup - delete g_nw; - g_nw = NULL; - return true; -} - -// -%} - -// Do not create separate wrappers for default arguments -%feature("compactdefaultargs"); - -%constant ea_t BADADDR = ea_t(-1); -%constant sel_t BADSEL = sel_t(-1); -%constant nodeidx_t BADNODE = nodeidx_t(-1); - -// Help SWIG to figure out the ulonglong type -#ifdef SWIGWIN -typedef unsigned __int64 ulonglong; -typedef __int64 longlong; -#else -typedef unsigned long long ulonglong; -typedef long long longlong; -#endif - -typedef int error_t; - -%include "typemaps.i" - -%include "cstring.i" -%include "carrays.i" -%include "cpointer.i" - -%include "typeconv.i" - -%pythoncode %{ -# - -import struct -import traceback -import os -import sys -import bisect -import __builtin__ -import imp - -def require(modulename, package=None): - """ - Load, or reload a module. - - When under heavy development, a user's tool might consist of multiple - modules. If those are imported using the standard 'import' mechanism, - there is no guarantee that the Python implementation will re-read - and re-evaluate the module's Python code. In fact, it usually doesn't. - What should be done instead is 'reload()'-ing that module. - - This is a simple helper function that will do just that: In case the - module doesn't exist, it 'import's it, and if it does exist, - 'reload()'s it. - - For more information, see: . - """ - if modulename in sys.modules.keys(): - reload(sys.modules[modulename]) - else: - import importlib - import inspect - m = importlib.import_module(modulename, package) - frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1] - importer_module = inspect.getmodule(frame_obj) - if importer_module is None: # No importer module; called from command line - importer_module = sys.modules['__main__'] - setattr(importer_module, modulename, m) - sys.modules[modulename] = m - -# ----------------------------------------------------------------------- - -# Seek constants -SEEK_SET = 0 # from the file start -SEEK_CUR = 1 # from the current position -SEEK_END = 2 # from the file end - -# Plugin constants -PLUGIN_MOD = 0x0001 -PLUGIN_DRAW = 0x0002 -PLUGIN_SEG = 0x0004 -PLUGIN_UNL = 0x0008 -PLUGIN_HIDE = 0x0010 -PLUGIN_DBG = 0x0020 -PLUGIN_PROC = 0x0040 -PLUGIN_FIX = 0x0080 -PLUGIN_SKIP = 0 -PLUGIN_OK = 1 -PLUGIN_KEEP = 2 - -# PyIdc conversion object IDs -PY_ICID_INT64 = 0 -"""int64 object""" -PY_ICID_BYREF = 1 -"""byref object""" -PY_ICID_OPAQUE = 2 -"""opaque object""" - -# Step trace options (used with set_step_trace_options()) -ST_OVER_DEBUG_SEG = 0x01 -"""step tracing will be disabled when IP is in a debugger segment""" - -ST_OVER_LIB_FUNC = 0x02 -"""step tracing will be disabled when IP is in a library function""" - -# ----------------------------------------------------------------------- -class pyidc_opaque_object_t(object): - """This is the base class for all Python<->IDC opaque objects""" - __idc_cvt_id__ = PY_ICID_OPAQUE - -# ----------------------------------------------------------------------- -class py_clinked_object_t(pyidc_opaque_object_t): - """ - This is a utility and base class for C linked objects - """ - def __init__(self, lnk = None): - # static link: if a link was provided - self.__static_clink__ = True if lnk else False - - # Create link if it was not provided - self.__clink__ = lnk if lnk else self._create_clink() - - def __del__(self): - """Delete the link upon object destruction (only if not static)""" - self._free() - - def _free(self): - """Explicitly delete the link (only if not static)""" - if not self.__static_clink__ and self.__clink__ is not None: - self._del_clink(self.__clink__) - self.__clink__ = None - - def copy(self): - """Returns a new copy of this class""" - - # Create an unlinked instance - inst = self.__class__() - - # Assign self to the new instance - inst.assign(self) - - return inst - - # - # Methods to be overwritten - # - def _create_clink(self): - """ - Overwrite me. - Creates a new clink - @return: PyCObject representing the C link - """ - pass - - def _del_clink(self, lnk): - """ - Overwrite me. - This method deletes the link - """ - pass - - def _get_clink_ptr(self): - """ - Overwrite me. - Returns the C link pointer as a 64bit number - """ - pass - - def assign(self, other): - """ - Overwrite me. - This method allows you to assign an instance contents to anothers - @return: Boolean - """ - pass - - clink = property(lambda self: self.__clink__) - """Returns the C link as a PyObject""" - - clink_ptr = property(lambda self: self._get_clink_ptr()) - """Returns the C link pointer as a number""" - -# ----------------------------------------------------------------------- -class object_t(object): - """Helper class used to initialize empty objects""" - def __init__(self, **kwds): - self.__dict__ = kwds - - def __getitem__(self, idx): - """Allow access to object attributes by index (like dictionaries)""" - return getattr(self, idx) - -# ----------------------------------------------------------------------- -def _bounded_getitem_iterator(self): - """Helper function, to be set as __iter__ method for qvector-, or array-based classes.""" - for i in range(len(self)): - yield self[i] - -# ----------------------------------------------------------------------- -class plugin_t(pyidc_opaque_object_t): - """Base class for all scripted plugins.""" - pass - -# ----------------------------------------------------------------------- -class pyidc_cvt_helper__(object): - """ - This is a special helper object that helps detect which kind - of object is this python object wrapping and how to convert it - back and from IDC. - This object is characterized by its special attribute and its value - """ - def __init__(self, cvt_id, value): - self.__idc_cvt_id__ = cvt_id - self.value = value - - def __set_value(self, v): - self.__idc_cvt_value__ = v - def __get_value(self): - return self.__idc_cvt_value__ - value = property(__get_value, __set_value) - -# ----------------------------------------------------------------------- -class PyIdc_cvt_int64__(pyidc_cvt_helper__): - """Helper class for explicitly representing VT_INT64 values""" - - def __init__(self, v): - # id = 0 = int64 object - super(self.__class__, self).__init__(PY_ICID_INT64, v) - - # operation table - __op_table = \ - { - 0: lambda a, b: a + b, - 1: lambda a, b: a - b, - 2: lambda a, b: a * b, - 3: lambda a, b: a / b - } - # carries the operation given its number - def __op(self, op_n, other, rev=False): - a = self.value - # other operand of same type? then take its value field - if type(other) == type(self): - b = other.value - else: - b = other - if rev: - t = a - a = b - b = t - # construct a new object and return as the result - return self.__class__(self.__op_table[op_n](a, b)) - - # overloaded operators - def __add__(self, other): return self.__op(0, other) - def __sub__(self, other): return self.__op(1, other) - def __mul__(self, other): return self.__op(2, other) - def __div__(self, other): return self.__op(3, other) - def __radd__(self, other): return self.__op(0, other, True) - def __rsub__(self, other): return self.__op(1, other, True) - def __rmul__(self, other): return self.__op(2, other, True) - def __rdiv__(self, other): return self.__op(3, other, True) - -# ----------------------------------------------------------------------- -# qstrvec_t clinked object -class _qstrvec_t(py_clinked_object_t): - """ - WARNING: It is very unlikely an IDAPython user should ever, ever - have to use this type. It should only be used for IDAPython internals. - - For example, in py_askusingform.py, we ctypes-expose to the IDA - kernel & UI a qstrvec instance, in case a DropdownListControl is - constructed. - That's because that's what AskUsingForm expects, and we have no - choice but to make a DropdownListControl hold a qstrvec_t. - This is, afaict, the only situation where a Python - _qstrvec_t is required. - """ - - def __init__(self, items=None): - py_clinked_object_t.__init__(self) - # Populate the list if needed - if items: - self.from_list(items) - - def _create_clink(self): - return _idaapi.qstrvec_t_create() - - def _del_clink(self, lnk): - return _idaapi.qstrvec_t_destroy(lnk) - - def _get_clink_ptr(self): - return _idaapi.qstrvec_t_get_clink_ptr(self) - - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.qstrvec_t_assign(self, other) - - def __setitem__(self, idx, s): - """Sets string at the given index""" - return _idaapi.qstrvec_t_set(self, idx, s) - - def __getitem__(self, idx): - """Gets the string at the given index""" - return _idaapi.qstrvec_t_get(self, idx) - - def __get_size(self): - return _idaapi.qstrvec_t_size(self) - - size = property(__get_size) - """Returns the count of elements""" - - def addressof(self, idx): - """Returns the address (as number) of the qstring at the given index""" - return _idaapi.qstrvec_t_addressof(self, idx) - - def add(self, s): - """Add a string to the vector""" - return _idaapi.qstrvec_t_add(self, s) - - def from_list(self, lst): - """Populates the vector from a Python string list""" - return _idaapi.qstrvec_t_from_list(self, lst) - - def clear(self, qclear=False): - """ - Clears all strings from the vector. - @param qclear: Just reset the size but do not actually free the memory - """ - return _idaapi.qstrvec_t_clear(self, qclear) - - def insert(self, idx, s): - """Insert a string into the vector""" - return _idaapi.qstrvec_t_insert(self, idx, s) - - def remove(self, idx): - """Removes a string from the vector""" - return _idaapi.qstrvec_t_remove(self, idx) - -# ----------------------------------------------------------------------- -class PyIdc_cvt_refclass__(pyidc_cvt_helper__): - """Helper class for representing references to immutable objects""" - def __init__(self, v): - # id = one = byref object - super(self.__class__, self).__init__(PY_ICID_BYREF, v) - - def cstr(self): - """Returns the string as a C string (up to the zero termination)""" - return as_cstr(self.value) - -# ----------------------------------------------------------------------- -def as_cstr(val): - """ - Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) - It scans for the first \x00 and returns the string value up to that point. - """ - if isinstance(val, PyIdc_cvt_refclass__): - val = val.value - - n = val.find('\x00') - return val if n == -1 else val[:n] - -# ----------------------------------------------------------------------- -def as_unicode(s): - """Convenience function to convert a string into appropriate unicode format""" - # use UTF16 big/little endian, depending on the environment? - return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) - -# ----------------------------------------------------------------------- -def as_uint32(v): - """Returns a number as an unsigned int32 number""" - return v & 0xffffffff - -# ----------------------------------------------------------------------- -def as_int32(v): - """Returns a number as a signed int32 number""" - return -((~v & 0xffffffff)+1) - -# ----------------------------------------------------------------------- -def as_signed(v, nbits = 32): - """ - Returns a number as signed. The number of bits are specified by the user. - The MSB holds the sign. - """ - return -(( ~v & ((1 << nbits)-1) ) + 1) if v & (1 << nbits-1) else v - -# ---------------------------------------------------------------------- -def copy_bits(v, s, e=-1): - """ - Copy bits from a value - @param v: the value - @param s: starting bit (0-based) - @param e: ending bit - """ - # end-bit not specified? use start bit (thus extract one bit) - if e == -1: - e = s - # swap start and end if start > end - if s > e: - e, s = s, e - - mask = ~(((1 << (e-s+1))-1) << s) - - return (v & mask) >> s - -# ---------------------------------------------------------------------- -__struct_unpack_table = { - 1: ('b', 'B'), - 2: ('h', 'H'), - 4: ('l', 'L'), - 8: ('q', 'Q') -} - -# ---------------------------------------------------------------------- -def struct_unpack(buffer, signed = False, offs = 0): - """ - Unpack a buffer given its length and offset using struct.unpack_from(). - This function will know how to unpack the given buffer by using the lookup table '__struct_unpack_table' - If the buffer is of unknown length then None is returned. Otherwise the unpacked value is returned. - """ - # Supported length? - n = len(buffer) - if n not in __struct_unpack_table: - return None - # Conver to number - signed = 1 if signed else 0 - - # Unpack - return struct.unpack_from(__struct_unpack_table[n][signed], buffer, offs)[0] - - -# ------------------------------------------------------------ -def IDAPython_ExecSystem(cmd): - """ - Executes a command with popen(). - """ - try: - f = os.popen(cmd, "r") - s = ''.join(f.readlines()) - f.close() - return s - except Exception as e: - return "%s\n%s" % (str(e), traceback.format_exc()) - -# ------------------------------------------------------------ -def IDAPython_FormatExc(etype, value, tb, limit=None): - """ - This function is used to format an exception given the - values returned by a PyErr_Fetch() - """ - try: - return ''.join(traceback.format_exception(etype, value, tb, limit)) - except: - return str(value) - - -# ------------------------------------------------------------ -def IDAPython_ExecScript(script, g): - """ - Run the specified script. - It also addresses http://code.google.com/p/idapython/issues/detail?id=42 - - This function is used by the low-level plugin code. - """ - scriptpath = os.path.dirname(script) - if len(scriptpath) and scriptpath not in sys.path: - sys.path.append(scriptpath) - - argv = sys.argv - sys.argv = [ script ] - - # Adjust the __file__ path in the globals we pass to the script - old__file__ = g['__file__'] if '__file__' in g else '' - g['__file__'] = script - - try: - execfile(script, g) - PY_COMPILE_ERR = None - except Exception as e: - PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) - print(PY_COMPILE_ERR) - finally: - # Restore state - g['__file__'] = old__file__ - sys.argv = argv - - return PY_COMPILE_ERR - -# ------------------------------------------------------------ -def IDAPython_LoadProcMod(script, g): - """ - Load processor module. - """ - pname = g['__name__'] if g and g.has_key("__name__") else '__main__' - parent = sys.modules[pname] - - scriptpath, scriptname = os.path.split(script) - if len(scriptpath) and scriptpath not in sys.path: - sys.path.append(scriptpath) - - procmod_name = os.path.splitext(scriptname)[0] - procobj = None - fp = None - try: - fp, pathname, description = imp.find_module(procmod_name) - procmod = imp.load_module(procmod_name, fp, pathname, description) - if parent: - setattr(parent, procmod_name, procmod) - # export attrs from parent to processor module - parent_attrs = getattr(parent, '__all__', - (attr for attr in dir(parent) if not attr.startswith('_'))) - for pa in parent_attrs: - setattr(procmod, pa, getattr(parent, pa)) - # instantiate processor object - if getattr(procmod, 'PROCESSOR_ENTRY', None): - procobj = procmod.PROCESSOR_ENTRY() - PY_COMPILE_ERR = None - except Exception as e: - PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) - print(PY_COMPILE_ERR) - finally: - if fp: fp.close() - - sys.path.remove(scriptpath) - - return (PY_COMPILE_ERR, procobj) - -# ------------------------------------------------------------ -def IDAPython_UnLoadProcMod(script, g): - """ - Unload processor module. - """ - pname = g['__name__'] if g and g.has_key("__name__") else '__main__' - parent = sys.modules[pname] - - scriptname = os.path.split(script)[1] - procmod_name = os.path.splitext(scriptname)[0] - if getattr(parent, procmod_name, None): - delattr(parent, procmod_name) - del sys.modules[procmod_name] - PY_COMPILE_ERR = None - return PY_COMPILE_ERR - -# ---------------------------------------------------------------------- -class __IDAPython_Completion_Util(object): - """Internal utility class for auto-completion support""" - def __init__(self): - self.n = 0 - self.completion = None - self.lastmodule = None - - @staticmethod - def parse_identifier(line, prefix, prefix_start): - """ - Parse a line and extracts identifier - """ - id_start = prefix_start - while id_start > 0: - ch = line[id_start] - if not ch.isalpha() and ch != '.' and ch != '_': - id_start += 1 - break - id_start -= 1 - - return line[id_start:prefix_start + len(prefix)] - - @staticmethod - def dir_of(m, prefix): - return [x for x in dir(m) if x.startswith(prefix)] - - @classmethod - def get_completion(cls, id, prefix): - try: - m = sys.modules['__main__'] - - parts = id.split('.') - c = len(parts) - - for i in xrange(0, c-1): - m = getattr(m, parts[i]) - except Exception as e: - return (None, None) - else: - # search in the module - completion = cls.dir_of(m, prefix) - - # no completion found? looking from the global scope? then try the builtins - if not completion and c == 1: - completion = cls.dir_of(__builtin__, prefix) - - return (m, completion) if completion else (None, None) - - def __call__(self, prefix, n, line, prefix_start): - if n == 0: - self.n = n - id = self.parse_identifier(line, prefix, prefix_start) - self.lastmodule, self.completion = self.get_completion(id, prefix) - - if self.completion is None or n >= len(self.completion): - return None - - s = self.completion[n] - try: - attr = getattr(self.lastmodule, s) - # Is it callable? - if callable(attr): - return s + ("" if line.startswith("?") else "(") - # Is it iterable? - elif isinstance(attr, basestring) or getattr(attr, '__iter__', False): - return s + "[" - except: - pass - - return s - -# Instantiate an IDAPython command completion object (for use with IDA's CLI bar) -IDAPython_Completion = __IDAPython_Completion_Util() - -def _listify_types(*classes): - for cls in classes: - cls.__getitem__ = cls.at - cls.__len__ = cls.size - cls.__iter__ = _bounded_getitem_iterator - - - -# The general callback format of notify_when() is: -# def notify_when_callback(nw_code) -# In the case of NW_OPENIDB, the callback is: -# def notify_when_callback(nw_code, is_old_database) -NW_OPENIDB = 0x0001 -"""Notify when the database is opened. Its callback is of the form: def notify_when_callback(nw_code, is_old_database)""" -NW_CLOSEIDB = 0x0002 -"""Notify when the database is closed. Its callback is of the form: def notify_when_callback(nw_code)""" -NW_INITIDA = 0x0004 -"""Notify when the IDA starts. Its callback is of the form: def notify_when_callback(nw_code)""" -NW_TERMIDA = 0x0008 -"""Notify when the IDA terminates. Its callback is of the form: def notify_when_callback(nw_code)""" -NW_REMOVE = 0x0010 -"""Use this flag with other flags to uninstall a notifywhen callback""" - -# -%} - -%include "pro.i" - -// Do not move this. We need to override the define from pro.h -#define CASSERT(type) - -// Convert all of these -%cstring_output_maxstr_none(char *buf, size_t bufsize); -%binary_output_or_none(void *buf, size_t bufsize); -%binary_output_with_size(void *buf, size_t *bufsize); - -// Accept single Python string for const void * + size input arguments -// For example: put_many_bytes() and patch_many_bytes() -%apply (char *STRING, int LENGTH) { (const void *buf, size_t size) }; -%apply (char *STRING, int LENGTH) { (const void *buf, size_t len) }; -%apply (char *STRING, int LENGTH) { (const void *value, size_t length) }; -%apply (char *STRING, int LENGTH) { (const void *dataptr,size_t len) }; - -// Create wrapper classes for basic type arrays -%array_class(uchar, uchar_array); -%array_class(tid_t, tid_array); -%array_class(ea_t, ea_array); -%array_class(sel_t, sel_array); -%array_class(uval_t, uval_array); -%pointer_class(int, int_pointer); -%pointer_class(ea_t, ea_pointer); -%pointer_class(sval_t, sval_pointer); -%pointer_class(sel_t, sel_pointer); - -%include "ida.i" -%include "idd.i" -%include "idp.i" -%include "netnode.i" -%include "nalt.i" - -%include "allins.i" -%include "area.i" -%include "auto.i" -%include "bytes.i" -%include "dbg.i" -%include "diskio.i" -%include "entry.i" -%include "enum.i" -%include "expr.i" -%include "fixup.i" -%include "frame.i" -%include "funcs.i" -%include "typeinf.i" -#ifdef WITH_HEXRAYS - %include "hexrays.i" -#endif - -SWIG_DECLARE_PY_CLINKED_OBJECT(qstrvec_t) - -%{ -PyObject *qstrvec2pylist(qstrvec_t &vec) -{ - size_t n = vec.size(); - PyObject *py_list = PyList_New(n); - for ( size_t i=0; i < n; ++i ) - PyList_SetItem(py_list, i, PyString_FromString(vec[i].c_str())); - return py_list; -} -%} - -%inline %{ - -// -//------------------------------------------------------------------------ -/* -# -def parse_command_line(cmdline): - """ - Parses a space separated string (quotes and escape character are supported) - @param cmdline: The command line to parse - @return: A list of strings or None on failure - """ - pass -# -*/ -static PyObject *py_parse_command_line(const char *cmdline) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstrvec_t args; - if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 ) - Py_RETURN_NONE; - return qstrvec2pylist(args); -} - -//------------------------------------------------------------------------- -/* -# -def get_inf_structure(): - """ - Returns the global variable 'inf' (an instance of idainfo structure, see ida.hpp) - """ - pass -# -*/ -idainfo *get_inf_structure(void) -{ - return &inf; -} - -//------------------------------------------------------------------------- -// Declarations from Python.cpp -/* -# -def set_script_timeout(timeout): - """ - Changes the script timeout value. The script wait box dialog will be hidden and shown again when the timeout elapses. - See also L{disable_script_timeout}. - - @param timeout: This value is in seconds. - If this value is set to zero then the script will never timeout. - @return: Returns the old timeout value - """ - pass -# -*/ -int set_script_timeout(int timeout); - -/* -# -def disable_script_timeout(): - """ - Disables the script timeout and hides the script wait box. - Calling L{set_script_timeout} will not have any effects until the script is compiled and executed again - - @return: None - """ - pass -# -*/ -void disable_script_timeout(); - -/* -# -def enable_extlang_python(enable): - """ - Enables or disables Python extlang. - When enabled, all expressions will be evaluated by Python. - @param enable: Set to True to enable, False otherwise - """ - pass -# -*/ -void enable_extlang_python(bool enable); -void enable_python_cli(bool enable); - -/* -# -def RunPythonStatement(stmt): - """ - This is an IDC function exported from the Python plugin. - It is used to evaluate Python statements from IDC. - @param stmt: The statement to evaluate - @return: 0 - on success otherwise a string containing the error - """ - pass -# -*/ - -//--------------------------------------------------------------------------- -// qstrvec_t wrapper (INTERNAL! Don't expose. See py_idaapi.py) -//--------------------------------------------------------------------------- -static bool qstrvec_t_assign(PyObject *self, PyObject *other) -{ - qstrvec_t *lhs = qstrvec_t_get_clink(self); - qstrvec_t *rhs = qstrvec_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - *lhs = *rhs; - return true; -} - -static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - Py_RETURN_NONE; - else - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)&sv->at(idx)); -} - - -static bool qstrvec_t_set( - PyObject *self, - size_t idx, - const char *s) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - return false; - (*sv)[idx] = s; - return true; -} - -static bool qstrvec_t_from_list( - PyObject *self, - PyObject *py_list) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - qstrvec_t *sv = qstrvec_t_get_clink(self); - return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); -} - -static size_t qstrvec_t_size(PyObject *self) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - return sv == NULL ? 0 : sv->size(); -} - -static PyObject *qstrvec_t_get(PyObject *self, size_t idx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - Py_RETURN_NONE; - return PyString_FromString(sv->at(idx).c_str()); -} - -static bool qstrvec_t_add(PyObject *self, const char *s) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL ) - return false; - sv->push_back(s); - return true; -} - -static bool qstrvec_t_clear(PyObject *self, bool qclear) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL ) - return false; - - if ( qclear ) - sv->qclear(); - else - sv->clear(); - - return true; -} - -static bool qstrvec_t_insert( - PyObject *self, - size_t idx, - const char *s) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - return false; - sv->insert(sv->begin() + idx, s); - return true; -} - -static bool qstrvec_t_remove(PyObject *self, size_t idx) -{ - qstrvec_t *sv = qstrvec_t_get_clink(self); - if ( sv == NULL || idx >= sv->size() ) - return false; - - sv->erase(sv->begin()+idx); - return true; -} - -//--------------------------------------------------------------------------- - - - -//------------------------------------------------------------------------ -/* -# -def notify_when(when, callback): - """ - Register a callback that will be called when an event happens. - @param when: one of NW_XXXX constants - @param callback: This callback prototype varies depending on the 'when' parameter: - The general callback format: - def notify_when_callback(nw_code) - In the case of NW_OPENIDB: - def notify_when_callback(nw_code, is_old_database) - @return: Boolean - """ - pass -# -*/ -static bool notify_when(int when, PyObject *py_callable) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( g_nw == NULL || !PyCallable_Check(py_callable) ) - return false; - return g_nw->notify_when(when, py_callable); -} - -// -%} - -%include "gdl.i" -%include "ints.i" -%include "kernwin.i" -%include "lines.i" -%include "loader.i" -%include "moves.i" -%include "name.i" -%include "offset.i" -%include "queue.i" -%include "search.i" -%include "segment.i" -%include "srarea.i" -%include "strlist.i" -%include "struct.i" -%include "ua.i" -%include "xref.i" -%include "view.i" -%include "graph.i" -%include "fpro.i" -%include "registry.i" +%module(docstring="IDA Plugin SDK API wrapper",directors="1",threads="1") idaapi +// * http://swig.10945.n7.nabble.com/How-to-release-Python-GIL-td5027.html +// * http://stackoverflow.com/questions/1576737/releasing-python-gil-in-c-code +// * http://matt.eifelle.com/2007/11/23/enabling-thread-support-in-swig-and-python/ +%nothread; // We don't want SWIG to release the GIL for *every* IDA API call. +// Suppress 'previous definition of XX' warnings +#pragma SWIG nowarn=302 +// and others... +#pragma SWIG nowarn=312 +#pragma SWIG nowarn=325 +#pragma SWIG nowarn=314 +#pragma SWIG nowarn=362 +#pragma SWIG nowarn=383 +#pragma SWIG nowarn=389 +#pragma SWIG nowarn=401 +#pragma SWIG nowarn=451 +#pragma SWIG nowarn=454 // Setting a pointer/reference variable may leak memory + +%constant size_t SIZE_MAX = size_t(-1); +%{ + +#ifndef USE_DANGEROUS_FUNCTIONS + #define USE_DANGEROUS_FUNCTIONS 1 +#endif + +#include + +void raise_python_stl_bad_alloc(const std::bad_alloc &ba) +{ + Py_INCREF(PyExc_MemoryError); + PyErr_SetString(PyExc_MemoryError, "Out of memory (bad_alloc)"); +} + +void raise_python_unknown_exception() +{ + Py_INCREF(PyExc_RuntimeError); + PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); +} + +void raise_python_stl_exception(const std::exception &e) +{ + const char *what = e.what(); + if ( what == NULL || what[0] == '\0' ) + { + raise_python_unknown_exception(); + } + else + { + Py_INCREF(PyExc_RuntimeError); + PyErr_SetString(PyExc_RuntimeError, what); + } +} +%} + +%define %exception_set_default_handlers() +%exception { + try + { + $action + } + catch ( const std::bad_alloc &ba ) { raise_python_stl_bad_alloc(ba); SWIG_fail; } + catch ( const std::exception &e ) { raise_python_stl_exception(e); SWIG_fail; } + catch (...) { raise_python_unknown_exception(); SWIG_fail; } +} +%enddef +%exception_set_default_handlers(); + +// Enable automatic docstring generation +%feature(autodoc,0); + +%{ +/* strnlen() arrived on OSX at v10.7. Provide it ourselves if needed. */ +#ifdef __MAC__ +#ifndef MAC_OS_X_VERSION_10_7 +#define MAC_OS_X_VERSION_10_7 1070 +#endif +#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) +inline size_t strnlen(const char *s, size_t maxlen) +{ + const char *found = (const char *) memchr(s, 0, maxlen); + return found != NULL ? size_t(found - s) : maxlen; +} +#endif +#endif +%} + +%define SWIG_DECLARE_PY_CLINKED_OBJECT(type) +%inline %{ +static PyObject *type##_create() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new type(), NULL); +} +static bool type##_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + delete (type *)PyCObject_AsVoidPtr(py_obj); + return true; +} +static type *type##_get_clink(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return (type *)pyobj_get_clink(self); +} +static PyObject *type##_get_clink_ptr(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyLong_FromUnsignedLongLong( + (unsigned PY_LONG_LONG)pyobj_get_clink(self)); +} +%} +%enddef + +// We use those special maps because SWIG wraps passed PyObject* with 'SwigPtr_PyObject' and 'SwigVar_PyObject' +// They act like autoptr and decrement the reference of the object when the scope ends +// We need to keep a reference outside SWIG and let the caller manage its references +%typemap(directorin) PyObject * "/*%din%*/Py_XINCREF($1_name);$input = $1_name;" +%typemap(directorout) PyObject * "/*%dout%*/$result = result;Py_XINCREF($result);" + +%{ +#include + +#ifdef HAVE_SSIZE_T +#define _SSIZE_T_DEFINED 1 +#endif + +#if defined(__NT__) && !defined(_WINDOWS_) + #define _WINDOWS_ // kernwin.hpp needs it to declare create_tform() + typedef void *HWND; // we don't need to include windows.h for just this definition +#endif + +#include "ida.hpp" +#include "idp.hpp" +#include "allins.hpp" +#include "auto.hpp" +#include "bytes.hpp" +#include "dbg.hpp" +#include "diskio.hpp" +#include "entry.hpp" +#include "enum.hpp" +#include "expr.hpp" +#include "frame.hpp" +#include "fixup.hpp" +#include "funcs.hpp" +#include "gdl.hpp" +#include "idd.hpp" +#include "ints.hpp" +#include "kernwin.hpp" +#include "lines.hpp" +#include "loader.hpp" +#include "moves.hpp" +#include "netnode.hpp" +#include "nalt.hpp" +#include "name.hpp" +#include "offset.hpp" +#include "queue.hpp" +#include "search.hpp" +#include "srarea.hpp" +#include "strlist.hpp" +#include "struct.hpp" +#include "typeinf.hpp" +#include "registry.hpp" +#include "ua.hpp" +#include "xref.hpp" +#include "ieee.h" +#include "err.h" +#include "fpro.h" +#include +#include "graph.hpp" +#ifdef WITH_HEXRAYS +#include "hexrays.hpp" +#endif +#include "pywraps.hpp" + +// + +//------------------------------------------------------------------------ +// String constants used +static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; +static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; +static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; +static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; + +// Constants used by get_idaapi_class_reference() +#define PY_CLSID_CVT_INT64 0 +#define PY_CLSID_APPCALL_SKEL_OBJ 1 +#define PY_CLSID_CVT_BYREF 2 +#define PY_CLSID_LAST 3 + +//--------------------------------------------------------------------------- +// Use these macros to define script<->C fields +#define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt } +#define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0) +#define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1) + +//--------------------------------------------------------------------------- +enum scfield_types_t +{ + // Numeric fields + FT_FIRST_NUM, + FT_INT, + FT_SIZET, + FT_SSIZET, + FT_NUM16, + FT_NUM32, + FT_LAST_NUM, + // String field + FT_STR, + FT_CHAR, + // Object fields + FT_ARR, + // Allocated array of strings + FT_STRARR, + // Allocated array of 16bit numbers + FT_NUM16ARR, + // Fixed size character array. The size must be passed in the definition + FT_CHRARR_STATIC, +}; + +//--------------------------------------------------------------------------- +struct scfld_t +{ + const char *field_name; + uint32 field_type; + size_t field_offs; + bool is_optional; +}; + +#define FT_VALUE_MASK 0xFFFF0000 +// Possible return values of conversion functions +#define FT_NOT_FOUND -1 +#define FT_BAD_TYPE -2 +#define FT_OK 1 + +//------------------------------------------------------------------------- +Py_ssize_t pyvar_walk_list( + const ref_t &py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), + void *ud) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + Py_ssize_t size = CIP_FAILED; + do + { + PyObject *o = py_list.o; + if ( !PyList_CheckExact(o) && !PyW_IsSequenceType(o) ) + break; + + bool is_seq = !PyList_CheckExact(o); + size = is_seq ? PySequence_Size(o) : PyList_Size(o); + if ( cb == NULL ) + break; + + Py_ssize_t i; + for ( i=0; i= CIP_OK; + if ( !ok ) + PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); + return ok; +} + +//------------------------------------------------------------------------ +static idc_class_t *get_py_idc_cvt_opaque() +{ + return find_idc_class(S_PY_IDC_OPAQUE_T); +} + +//------------------------------------------------------------------------- +// Utility function to create opaque / convertible Python <-> IDC variables +// The referred Python variable will have its reference increased +static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Create an IDC object of this special helper class + if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) + return false; + + // Store the CVT id + idc_value_t idc_val; + idc_val.set_long(PY_ICID_OPAQUE); + VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); + + // Store the value as a PVOID referencing the given Python object + py_var.incref(); + idc_val.set_pvoid(py_var.o); + VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); + + return true; +} + +//------------------------------------------------------------------------ +// IDC Opaque object destructor: when the IDC object dies we kill the +// opaque Python object along with it +static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; +static error_t idaapi py_idc_opaque_dtor( + idc_value_t *argv, + idc_value_t * /*res*/) +{ + // This can be called at plugin registration time, when a + // 'script_plugin_t' instance is ::free()'d. It is + // not guaranteed that we have the GIL at that point. + PYW_GIL_GET; + + // Get the value from the object + idc_value_t idc_val; + VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); + + // Extract the Python object reference + PyObject *py_obj = (PyObject *)idc_val.pvoid; + + // Decrease its reference (and eventually destroy it) + Py_DECREF(py_obj); + + return eOk; +} + +//------------------------------------------------------------------------- +// Converts a Python variable into an IDC variable +// This function returns on one CIP_XXXX +int pyvar_to_idcvar( + const ref_t &py_var, + idc_value_t *idc_var, + int *gvar_sn) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // None / NULL + if ( py_var == NULL || py_var.o == Py_None ) + { + idc_var->set_long(0); + } + // Numbers? + else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) + { + return CIP_OK; + } + // String + else if ( PyString_Check(py_var.o) ) + { + idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); + } + // Boolean + else if ( PyBool_Check(py_var.o) ) + { + idc_var->set_long(py_var.o == Py_True ? 1 : 0); + } + // Float + else if ( PyFloat_Check(py_var.o) ) + { + double dresult = PyFloat_AsDouble(py_var.o); + ieee_realcvt((void *)&dresult, idc_var->e, 3); + idc_var->vtype = VT_FLOAT; + } + // void* + else if ( PyCObject_Check(py_var.o) ) + { + idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); + } + // Python list? + else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) + { + // Create the object + VarObject(idc_var); + + // Determine list size and type + bool is_seq = !PyList_CheckExact(py_var.o); + Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); + bool ok = true; + qstring attr_name; + + // Convert each item + for ( Py_ssize_t i=0; i= CIP_OK; + if ( ok ) + { + // Form the attribute name + newref_t py_int(PyInt_FromSsize_t(i)); + ok = PyW_ObjectToString(py_int.o, &attr_name); + if ( !ok ) + break; + // Store the attribute + VarSetAttr(idc_var, attr_name.c_str(), &v); + } + if ( !ok ) + break; + } + return ok ? CIP_OK : CIP_FAILED; + } + // Dictionary: we convert to an IDC object + else if ( PyDict_Check(py_var.o) ) + { + // Create an empty IDC object + VarObject(idc_var); + + // Get the dict.items() list + newref_t py_items(PyDict_Items(py_var.o)); + + // Get the size of the list + qstring key_name; + bool ok = true; + Py_ssize_t size = PySequence_Size(py_items.o); + for ( Py_ssize_t i=0; i (key, value) + PyObject *py_item = PyList_GetItem(py_items.o, i); + + // Extract key/value + newref_t key(PySequence_GetItem(py_item, 0)); + newref_t val(PySequence_GetItem(py_item, 1)); + + // Get key's string representation + PyW_ObjectToString(key.o, &key_name); + + // Convert the attribute into an IDC value + idc_value_t v; + ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; + if ( ok ) + { + // Store the attribute + VarSetAttr(idc_var, key_name.c_str(), &v); + } + if ( !ok ) + break; + } + return ok ? CIP_OK : CIP_FAILED; + } + // Possible function? + else if ( PyCallable_Check(py_var.o) ) + { + idc_var->clear(); + idc_var->vtype = VT_FUNC; + idc_var->funcidx = -1; // Does not apply + return CIP_OK; + } + // Objects: + // - pyidc_cvt objects: int64, byref, opaque + // - other python objects + else + { + // Get the type + int cvt_id = get_pyidc_cvt_type(py_var.o); + switch ( cvt_id ) + { + // + // INT64 + // + case PY_ICID_INT64: + { + // Get the value attribute + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); + if ( attr == NULL ) + return false; + idc_var->set_int64(PyLong_AsLongLong(attr.o)); + return CIP_OK; + } + // + // BYREF + // + case PY_ICID_BYREF: + { + // BYREF always require this parameter + if ( gvar_sn == NULL ) + return CIP_FAILED; + + // Get the value attribute + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); + if ( attr == NULL ) + return CIP_FAILED; + + // Create a global variable + char buf[MAXSTR]; + qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); + idc_value_t *gvar = add_idc_gvar(buf); + // Convert the python value into the IDC global variable + bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; + if ( ok ) + { + (*gvar_sn)++; + // Create a reference to this global variable + VarRef(idc_var, gvar); + } + return ok ? CIP_OK : CIP_FAILED; + } + // + // OPAQUE + // + case PY_ICID_OPAQUE: + { + if ( !wrap_PyObject_ptr(py_var, idc_var) ) + return CIP_FAILED; + return CIP_OK_OPAQUE; + } + // + // Other objects + // + default: + // A normal object? + newref_t py_dir(PyObject_Dir(py_var.o)); + Py_ssize_t size = PyList_Size(py_dir.o); + if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) + return CIP_FAILED; + // Create the IDC object + VarObject(idc_var); + for ( Py_ssize_t i=0; i 2 ) + && (strncmp(field_name, "__", 2) == 0 ) + && (strncmp(field_name+len-2, "__", 2) == 0) ) + { + continue; + } + + idc_value_t v; + // Get the non-private attribute from the object + newref_t attr(PyObject_GetAttrString(py_var.o, field_name)); + if (attr == NULL + // Convert the attribute into an IDC value + || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK) + { + return CIP_FAILED; + } + + // Store the attribute + VarSetAttr(idc_var, field_name, &v); + } + } + } + return CIP_OK; +} + +//------------------------------------------------------------------------- +inline PyObject *cvt_to_pylong(int32 v) +{ + return PyLong_FromLong(v); +} + +inline PyObject *cvt_to_pylong(int64 v) +{ + return PyLong_FromLongLong(v); +} + +//------------------------------------------------------------------------- +// Converts an IDC variable to a Python variable +// If py_var points to an existing object then the object will be updated +// If py_var points to an existing immutable object then ZERO is returned +// Returns one of CIP_xxxx. Check pywraps.hpp +int idcvar_to_pyvar( + const idc_value_t &idc_var, + ref_t *py_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + switch ( idc_var.vtype ) + { + case VT_PVOID: + if ( *py_var == NULL ) + { + newref_t nr(PyCObject_FromVoidPtr(idc_var.pvoid, NULL)); + *py_var = nr; + } + else + { + return CIP_IMMUTABLE; + } + break; + + case VT_INT64: + { + // Recycle? + if ( *py_var != NULL ) + { + // Recycling an int64 object? + int t = get_pyidc_cvt_type(py_var->o); + if ( t != PY_ICID_INT64 ) + return CIP_IMMUTABLE; // Cannot recycle immutable object + // Update the attribute + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); + return CIP_OK; + } + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); + if ( py_cls == NULL ) + return CIP_FAILED; + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); + if ( PyW_GetError() || *py_var == NULL ) + return CIP_FAILED; + break; + } + +#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) + case VT_STR: + *py_var = newref_t(PyString_FromString(idc_var.str)); + break; + +#endif + case VT_STR2: + if ( *py_var == NULL ) + { + const qstring &s = idc_var.qstr(); + *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); + break; + } + else + return CIP_IMMUTABLE; // Cannot recycle immutable object + case VT_LONG: + // Cannot recycle immutable objects + if ( *py_var != NULL ) + return CIP_IMMUTABLE; + *py_var = newref_t(cvt_to_pylong(idc_var.num)); + break; + case VT_FLOAT: + if ( *py_var == NULL ) + { + double x; + if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) + INTERR(30160); + + *py_var = newref_t(PyFloat_FromDouble(x)); + break; + } + else + return CIP_IMMUTABLE; + + case VT_REF: + { + if ( *py_var == NULL ) + { + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); + if ( py_cls == NULL ) + return CIP_FAILED; + + // Create a byref object with None value. We populate it later + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); + if ( PyW_GetError() || *py_var == NULL ) + return CIP_FAILED; + } + int t = get_pyidc_cvt_type(py_var->o); + if ( t != PY_ICID_BYREF ) + return CIP_FAILED; + + // Dereference + // (Since we are not using VREF_COPY flag, we can safely const_cast) + idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); + if ( dref_v == NULL ) + return CIP_FAILED; + + // Can we recycle the object? + ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); + if ( new_py_val != NULL ) + { + // Recycle + t = idcvar_to_pyvar(*dref_v, &new_py_val); + + // Success? Nothing more to be done + if ( t == CIP_OK ) + return CIP_OK; + + // Clear it so we don't recycle it + new_py_val = ref_t(); + } + // Try to convert (not recycle) + if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) + return CIP_FAILED; + + // Update the attribute + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); + break; + } + + // Can convert back into a Python object or Python dictionary + // (Depending if py_var will be recycled and it was a dictionary) + case VT_OBJ: + { + // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields + idc_value_t idc_val; + if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk + && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) + { + // Extract the object + *py_var = borref_t((PyObject *) idc_val.pvoid); + return CIP_OK_OPAQUE; + } + ref_t obj; + bool is_dict = false; + + // Need to create a new object? + if ( *py_var == NULL ) + { + // Get skeleton class reference + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); + if ( py_cls == NULL ) + return CIP_FAILED; + + // Call constructor + obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); + if ( PyW_GetError() || obj == NULL ) + return CIP_FAILED; + } + else + { + // Recycle existing variable + obj = *py_var; + if ( PyDict_Check(obj.o) ) + is_dict = true; + } + + // Walk the IDC attributes and store into python + for (const char *attr_name = VarFirstAttr(&idc_var); + attr_name != NULL; + attr_name = VarNextAttr(&idc_var, attr_name) ) + { + // Get the attribute + idc_value_t v; + VarGetAttr(&idc_var, attr_name, &v, true); + + // Convert attribute to a python value (recursively) + ref_t py_attr; + int cvt = idcvar_to_pyvar(v, &py_attr); + if ( cvt <= CIP_IMMUTABLE ) + return CIP_FAILED; + if ( is_dict ) + PyDict_SetItemString(obj.o, attr_name, py_attr.o); + else + PyObject_SetAttrString(obj.o, attr_name, py_attr.o); + } + *py_var = obj; + break; + } + // Unhandled type + default: + *py_var = ref_t(); + return CIP_FAILED; + } + return CIP_OK; +} + +//------------------------------------------------------------------------- +// Converts IDC arguments to Python argument list or just one tuple +// If 'decref' is NULL then 'pargs' will contain one element which is the tuple +bool pyw_convert_idc_args( + const idc_value_t args[], + int nargs, + ref_vec_t &pargs, + bool as_tupple, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + ref_t py_tuple; + + pargs.qclear(); + + if ( as_tupple ) + { + py_tuple = newref_t(PyTuple_New(nargs)); + if ( py_tuple == NULL ) + { + if ( errbuf != 0 && errbufsize > 0 ) + qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); + return false; + } + } + + for ( int i=0; i 0 ) + qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype); + return false; + } + + if ( as_tupple ) + { + // PyTuple_SetItem() steals the reference. + py_obj.incref(); + QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0); + } + else + { + pargs.push_back(py_obj); + } + } + + // Add the tuple to the list of args only now. Doing so earlier will + // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will + // cause 'PyTuple_SetItem()' to fail. + if ( as_tupple ) + pargs.push_back(py_tuple); + + return true; +} + + + + +//------------------------------------------------------------------------ +// String constants used +static const char S_PYINVOKE0[] = "_py_invoke0"; +static const char S_PY_SWIEX_CLSNAME[] = "switch_info_ex_t"; +static const char S_PY_OP_T_CLSNAME[] = "op_t"; +static const char S_PROPS[] = "props"; +static const char S_NAME[] = "name"; +static const char S_TITLE[] = "title"; +static const char S_ASM_KEYWORD[] = "asm_keyword"; +static const char S_MENU_NAME[] = "menu_name"; +static const char S_HOTKEY[] = "hotkey"; +static const char S_EMBEDDED[] = "embedded"; +static const char S_POPUP_NAMES[] = "popup_names"; +static const char S_FLAGS[] = "flags"; +static const char S_VALUE_SIZE[] = "value_size"; +static const char S_MAY_CREATE_AT[] = "may_create_at"; +static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; +static const char S_ID[] = "id"; +static const char S_PRINTF[] = "printf"; +static const char S_TEXT_WIDTH[] = "text_width"; +static const char S_SCAN[] = "scan"; +static const char S_ANALYZE[] = "analyze"; +static const char S_CBSIZE[] = "cbsize"; +static const char S_ON_CLICK[] = "OnClick"; +static const char S_ON_CLOSE[] = "OnClose"; +static const char S_ON_DBL_CLICK[] = "OnDblClick"; +static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; +static const char S_ON_KEYDOWN[] = "OnKeydown"; +static const char S_ON_COMPLETE_LINE[] = "OnCompleteLine"; +static const char S_ON_CREATE[] = "OnCreate"; +static const char S_ON_POPUP[] = "OnPopup"; +static const char S_ON_HINT[] = "OnHint"; +static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; +static const char S_ON_EDIT_LINE[] = "OnEditLine"; +static const char S_ON_INSERT_LINE[] = "OnInsertLine"; +static const char S_ON_GET_LINE[] = "OnGetLine"; +static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; +static const char S_ON_REFRESH[] = "OnRefresh"; +static const char S_ON_REFRESHED[] = "OnRefreshed"; +static const char S_ON_EXECUTE_LINE[] = "OnExecuteLine"; +static const char S_ON_SELECT_LINE[] = "OnSelectLine"; +static const char S_ON_SELECTION_CHANGE[] = "OnSelectionChange"; +static const char S_ON_COMMAND[] = "OnCommand"; +static const char S_ON_GET_ICON[] = "OnGetIcon"; +static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; +static const char S_ON_GET_SIZE[] = "OnGetSize"; +static const char S_ON_GETTEXT[] = "OnGetText"; +static const char S_ON_ACTIVATE[] = "OnActivate"; +static const char S_ON_DEACTIVATE[] = "OnDeactivate"; +static const char S_ON_SELECT[] = "OnSelect"; +static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; +static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; +static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; +static const char S_M_EDGES[] = "_edges"; +static const char S_M_NODES[] = "_nodes"; +static const char S_M_THIS[] = "_this"; +static const char S_M_TITLE[] = "_title"; +static const char S_CLINK_NAME[] = "__clink__"; +static const char S_ON_VIEW_ACTIVATED[] = "OnViewActivated"; +static const char S_ON_VIEW_DEACTIVATED[] = "OnViewDeactivated"; +static const char S_ON_VIEW_KEYDOWN[] = "OnViewKeydown"; +static const char S_ON_VIEW_CLICK[] = "OnViewClick"; +static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick"; +static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos"; +static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched"; +static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver"; +static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved"; + + +#ifdef __PYWRAPS__ +static const char S_PY_IDAAPI_MODNAME[] = "__main__"; +#else +static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; +#endif + +//------------------------------------------------------------------------ +static ref_t py_cvt_helper_module; +static bool pywraps_initialized = false; + +//--------------------------------------------------------------------------- +// Context structure used by add|del_menu_item() +struct py_add_del_menu_item_ctx +{ + qstring menupath; + PyObject *cb_data; +}; + +//--------------------------------------------------------------------------- +// Context structure used by add|del_idc_hotkey() +struct py_idchotkey_ctx_t +{ + qstring hotkey; + PyObject *pyfunc; +}; + +//--------------------------------------------------------------------------- +// Context structure used by register/unregister timer +struct py_timer_ctx_t +{ + qtimer_t timer_id; + PyObject *pycallback; +}; + +//------------------------------------------------------------------------ +// check if we have a file which is known to be executed automatically +// by SWIG or Python runtime +bool pywraps_check_autoscripts(char *buf, size_t bufsize) +{ + static const char *const exts[] = + { + "py", + "pyc", + "pyd", + "pyo", + "pyw", + }; + + static const char *const fns[] = + { + "swig_runtime_data" SWIG_RUNTIME_VERSION, + "sitecustomize", + "usercustomize" + }; + + for ( size_t ifn=0; ifn < qnumber(fns); ++ifn ) + { + // check for a script or module with several possible extensions + for ( size_t iext=0; iext < qnumber(exts); ++iext ) + { + qsnprintf(buf, bufsize, "%s.%s", fns[ifn], exts[iext]); + if ( qfileexist(buf) ) + return true; + } + // check for a subdirectory under current directory + if ( qfileexist(fns[ifn]) ) + { + qstrncpy(buf, fns[ifn], bufsize); + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +error_t PyW_CreateIdcException(idc_value_t *res, const char *msg) +{ + // Create exception object + VarObject(res, find_idc_class("exception")); + + // Set the message field + idc_value_t v; + v.set_string(msg); + VarSetAttr(res, "message", &v); + + // Throw exception + return set_qerrno(eExecThrow); +} + +//------------------------------------------------------------------------ +// Calls a Python callable encoded in IDC.pvoid member +static const char idc_py_invoke0_args[] = { VT_PVOID, 0 }; +static error_t idaapi idc_py_invoke0( + idc_value_t *argv, + idc_value_t *res) +{ + PYW_GIL_GET; + PyObject *pyfunc = (PyObject *) argv[0].pvoid; + newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); + + // Report Python error as IDC exception + qstring err; + error_t err_code = eOk; + if ( PyW_GetError(&err) ) + err_code = PyW_CreateIdcException(res, err.c_str()); + return err_code; +} + +//------------------------------------------------------------------------ +// This function must be called on initialization +bool init_pywraps() +{ + if ( pywraps_initialized ) + return true; + + // Take a reference to the idaapi python module + // (We need it to create instances of certain classes) + if ( py_cvt_helper_module == NULL ) + { + // Take a reference to the module so we can create the needed class instances + py_cvt_helper_module = PyW_TryImportModule(S_PY_IDAAPI_MODNAME); + if ( py_cvt_helper_module == NULL ) + return false; + } + + // Register the IDC PyInvoke0 method (helper function for add_idc_hotkey()) + if ( !set_idc_func_ex(S_PYINVOKE0, idc_py_invoke0, idc_py_invoke0_args, 0) ) + return false; + + // IDC opaque class not registered? + if ( get_py_idc_cvt_opaque() == NULL ) + { + // Add the class + idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); + if ( idc_cvt_opaque == NULL ) + return false; + + // Form the dtor name + char dtor_name[MAXSTR]; + qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); + + // Register the dtor function + if ( !set_idc_func_ex(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args, 0) ) + return false; + + // Link the dtor function to the class + set_idc_dtor(idc_cvt_opaque, dtor_name); + } + + pywraps_initialized = true; + return true; +} + +//------------------------------------------------------------------------ +// This function must be called on de-initialization +void deinit_pywraps() +{ + if ( !pywraps_initialized ) + return; + + pywraps_initialized = false; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_cvt_helper_module = ref_t(); // Deref. + } + + // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) + set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); +} + +//------------------------------------------------------------------------ +// Utility function to create a class instance whose constructor takes zero arguments +ref_t create_idaapi_class_instance0(const char *clsname) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); + if ( py_cls == NULL ) + return ref_t(); + + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); + if ( PyW_GetError() || py_obj == NULL ) + py_obj = ref_t(); + return py_obj; +} + +//------------------------------------------------------------------------ +// Utility function to create linked class instances +ref_t create_idaapi_linked_class_instance( + const char *clsname, + void *lnk) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); + if ( py_cls == NULL ) + return ref_t(); + + newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, py_lnk.o, NULL)); + if ( PyW_GetError() || py_obj == NULL ) + py_obj = ref_t(); + return py_obj; +} + +//------------------------------------------------------------------------ +// Gets a class type reference in idaapi +// With the class type reference we can create a new instance of that type +// This function takes a reference to the idaapi module and keeps the reference +ref_t get_idaapi_attr_by_id(const int class_id) +{ + if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) + return ref_t(); + + // Some class names. The array is parallel with the PY_CLSID_xxx consts + static const char *class_names[]= + { + "PyIdc_cvt_int64__", + "object_t", + "PyIdc_cvt_refclass__" + }; + PYW_GIL_CHECK_LOCKED_SCOPE(); + return newref_t(PyObject_GetAttrString(py_cvt_helper_module.o, class_names[class_id])); +} + +//------------------------------------------------------------------------ +// Gets a class reference by name +ref_t get_idaapi_attr(const char *attrname) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return py_cvt_helper_module == NULL + ? ref_t() + : PyW_TryGetAttrString(py_cvt_helper_module.o, attrname); +} + +//------------------------------------------------------------------------ +// Returns a qstring from an object attribute +bool PyW_GetStringAttr( + PyObject *py_obj, + const char *attr_name, + qstring *str) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); + if ( py_attr == NULL ) + return false; + + bool ok = PyString_Check(py_attr.o) != 0; + if ( ok ) + *str = PyString_AsString(py_attr.o); + + return ok; +} + +//------------------------------------------------------------------------ +// Returns an attribute or NULL +// No errors will be set if the attribute did not exist +ref_t PyW_TryGetAttrString(PyObject *py_obj, const char *attr) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t o; + if ( PyObject_HasAttrString(py_obj, attr) ) + o = newref_t(PyObject_GetAttrString(py_obj, attr)); + return o; +} + +//------------------------------------------------------------------------ +// Tries to import a module and clears the exception on failure +ref_t PyW_TryImportModule(const char *name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyImport_ImportModule(name)); + if ( result == NULL && PyErr_Occurred() ) + PyErr_Clear(); + return result; +} + +//------------------------------------------------------------------------- +// Converts a Python number into an IDC value (32 or 64bits) +// The function will first try to convert the number into a 32bit value +// If the number does not fit then VT_INT64 will be used +// NB: This function cannot properly detect if the Python value should be +// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which +// can fit in a C long but Python creates a PyLong object for it. +// And because of that we are confused as to whether to convert to 32 or 64 +bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do + { + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + { + rc = false; + break; + } + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + l = (long) PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + PyErr_Clear(); + idc_var->set_int64(PyLong_AsLongLong(py_var)); + } while ( false ); + return rc; +} + +//------------------------------------------------------------------------- +// Parses a Python object as a long or long long +bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; +#define SETNUM(numexpr, is64_expr) \ + do \ + { \ + if ( num != NULL ) \ + *num = numexpr; \ + if ( is_64 != NULL ) \ + *is_64 = is64_expr; \ + } while ( false ) + + do + { + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + { + rc = false; + break; + } + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(l), false); + break; + } + + // Clear last error + PyErr_Clear(); + + // Can be fit into a C unsigned long? + unsigned long ul = PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ul), false); + break; + } + PyErr_Clear(); + + // Try to parse as int64 + PY_LONG_LONG ll = PyLong_AsLongLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ll), true); + break; + } + PyErr_Clear(); + + // Try to parse as uint64 + unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); + PyObject *err = PyErr_Occurred(); + if ( err == NULL ) + { + SETNUM(uint64(ull), true); + break; + } + // Negative number? _And_ it with uint64(-1) + rc = false; + if ( err == PyExc_TypeError ) + { + newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); + newref_t py_num(PyNumber_And(py_var, py_mask.o)); + if ( py_num != NULL && py_mask != NULL ) + { + PyErr_Clear(); + ull = PyLong_AsUnsignedLongLong(py_num.o); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ull), true); + rc = true; + } + } + } + PyErr_Clear(); + } while ( false ); + return rc; +#undef SETNUM +} + +//------------------------------------------------------------------------- +// Checks if a given object is of sequence type +bool PyW_IsSequenceType(PyObject *obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do + { + if ( !PySequence_Check(obj) ) + { + rc = false; + break; + } + + Py_ssize_t sz = PySequence_Size(obj); + if ( sz == -1 || PyErr_Occurred() != NULL ) + { + PyErr_Clear(); + rc = false; + break; + } + } while ( false ); + return rc; +} + +//------------------------------------------------------------------------- +// Returns the string representation of an object +bool PyW_ObjectToString(PyObject *obj, qstring *out) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_str(PyObject_Str(obj)); + bool ok = py_str != NULL; + if ( ok ) + *out = PyString_AsString(py_str.o); + else + out->qclear(); + return ok; +} + +//-------------------------------------------------------------------------- +// Checks if a Python error occured and fills the out parameter with the +// exception string +bool PyW_GetError(qstring *out, bool clear_err) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( PyErr_Occurred() == NULL ) + return false; + + // Error occurred but details not needed? + if ( out == NULL ) + { + // Just clear the error + if ( clear_err ) + PyErr_Clear(); + return true; + } + + // Get the exception info + PyObject *err_type, *err_value, *err_traceback, *py_ret(NULL); + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + if ( !clear_err ) + PyErr_Restore(err_type, err_value, err_traceback); + + // Resolve FormatExc() + ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); + + // Helper there? + if ( py_fmtexc != NULL ) + { + // Call helper + py_ret = PyObject_CallFunctionObjArgs( + py_fmtexc.o, + err_type, + err_value, + err_traceback, + NULL); + } + + // Clear the error + if ( clear_err ) + PyErr_Clear(); + + // Helper failed?! + if ( py_ret == NULL ) + { + // Just convert the 'value' part of the original error + py_ret = PyObject_Str(err_value); + } + + // No exception text? + if ( py_ret == NULL ) + { + *out = "IDAPython: unknown error!"; + } + else + { + *out = PyString_AsString(py_ret); + Py_DECREF(py_ret); + } + + if ( clear_err ) + { + Py_XDECREF(err_traceback); + Py_XDECREF(err_value); + Py_XDECREF(err_type); + } + return true; +} + +//------------------------------------------------------------------------- +bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstring s; + if ( !PyW_GetError(&s, clear_err) ) + return false; + + qstrncpy(buf, s.c_str(), bufsz); + return true; +} + +//------------------------------------------------------------------------- +// A loud version of PyGetError() which gets the error and displays it +// This method is used to display errors that occurred in a callback +bool PyW_ShowCbErr(const char *cb_name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + static qstring err_str; + if ( !PyW_GetError(&err_str) ) + return false; + + warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); + return true; +} + +//--------------------------------------------------------------------------- +void *pyobj_get_clink(PyObject *pyobj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Try to query the link attribute + ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); + void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; + return t; +} + + + +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +class pywraps_notify_when_t +{ + ref_vec_t table[NW_EVENTSCNT]; + qstring err; + bool in_notify; + struct notify_when_args_t + { + int when; + PyObject *py_callable; + }; + typedef qvector notify_when_args_vec_t; + notify_when_args_vec_t delayed_notify_when_list; + + //------------------------------------------------------------------------ + static int idaapi idp_callback(void *ud, int event_id, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; + switch ( event_id ) + { + case processor_t::newfile: + case processor_t::oldfile: + { + int old = event_id == processor_t::oldfile ? 1 : 0; + char *dbname = va_arg(va, char *); + _this->notify(NW_OPENIDB_SLOT, old); + } + break; + case processor_t::closebase: + _this->notify(NW_CLOSEIDB_SLOT); + break; + } + // event not processed, let other plugins or the processor module handle it + return 0; + } + + //------------------------------------------------------------------------ + bool unnotify_when(int when, PyObject *py_callable) + { + int cnt = 0; + for ( int slot=0; slot 0; + } + + //------------------------------------------------------------------------ + void register_callback(int slot, PyObject *py_callable) + { + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); + + // Already added + if ( it != it_end ) + return; + + // Insert the element + tbl.push_back(callable_ref); + } + + //------------------------------------------------------------------------ + void unregister_callback(int slot, PyObject *py_callable) + { + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); + + // Not found? + if ( it == it_end ) + return; + + // Delete the element + tbl.erase(it); + } + +public: + //------------------------------------------------------------------------ + bool init() + { + return hook_to_notification_point(HT_IDP, idp_callback, this); + } + + //------------------------------------------------------------------------ + bool deinit() + { + // Uninstall all objects + ref_vec_t::iterator it, it_end; + for ( int slot=0; sloto); + } + // ...and remove the notification + return unhook_from_notification_point(HT_IDP, idp_callback, this); + } + + //------------------------------------------------------------------------ + bool notify_when(int when, PyObject *py_callable) + { + // While in notify() do not allow insertion or deletion to happen on the spot + // Instead we will queue them so that notify() will carry the action when it finishes + // dispatching the notification handlers + if ( in_notify ) + { + notify_when_args_t &args = delayed_notify_when_list.push_back(); + args.when = when; + args.py_callable = py_callable; + return true; + } + // Uninstalling the notification? + if ( (when & NW_REMOVE) != 0 ) + return unnotify_when(when & ~NW_REMOVE, py_callable); + + int cnt = 0; + for ( int slot=0; slot 0; + } + + //------------------------------------------------------------------------ + bool notify(int slot, ...) + { + va_list va; + va_start(va, slot); + bool ok = notify_va(slot, va); + va_end(va); + return ok; + } + + //------------------------------------------------------------------------ + bool notify_va(int slot, va_list va) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Sanity bounds check! + if ( slot < 0 || slot >= NW_EVENTSCNT ) + return false; + + bool ok = true; + in_notify = true; + int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; + + { + for (ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); + it != it_end; + ++it) + { + // Form the notification code + newref_t py_code(PyInt_FromLong(1 << slot)); + ref_t py_result; + switch ( slot ) + { + case NW_CLOSEIDB_SLOT: + case NW_INITIDA_SLOT: + case NW_TERMIDA_SLOT: + { + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); + break; + } + case NW_OPENIDB_SLOT: + { + newref_t py_old(PyInt_FromLong(old)); + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); + } + break; + } + if ( PyW_GetError(&err) || py_result == NULL ) + { + PyErr_Clear(); + warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); + ok = false; + } + } + } + in_notify = false; + + // Process any delayed notify_when() calls that + if ( !delayed_notify_when_list.empty() ) + { + for (notify_when_args_vec_t::iterator it = delayed_notify_when_list.begin(), it_end=delayed_notify_when_list.end(); + it != it_end; + ++it) + { + notify_when(it->when, it->py_callable); + } + delayed_notify_when_list.qclear(); + } + + return ok; + } + + //------------------------------------------------------------------------ + pywraps_notify_when_t() + { + in_notify = false; + } +}; + +static pywraps_notify_when_t *g_nw = NULL; + +//------------------------------------------------------------------------ +// Initializes the notify_when mechanism +// (Normally called by IDAPython plugin.init()) +bool pywraps_nw_init() +{ + if ( g_nw != NULL ) + return true; + + g_nw = new pywraps_notify_when_t(); + if ( g_nw->init() ) + return true; + + // Things went bad, undo! + delete g_nw; + g_nw = NULL; + return false; +} + +//------------------------------------------------------------------------ +bool pywraps_nw_notify(int slot, ...) +{ + if ( g_nw == NULL ) + return false; + + // Appears to be called from 'driver_notifywhen.cpp', which + // itself is called from possibly non-python code. + // I.e., we must acquire the GIL. + PYW_GIL_GET; + va_list va; + va_start(va, slot); + bool ok = g_nw->notify_va(slot, va); + va_end(va); + + return ok; +} + +//------------------------------------------------------------------------ +// Deinitializes the notify_when mechanism +bool pywraps_nw_term() +{ + if ( g_nw == NULL ) + return true; + + // If could not deinitialize then return w/o stopping nw + if ( !g_nw->deinit() ) + return false; + + // Cleanup + delete g_nw; + g_nw = NULL; + return true; +} + +// +%} + +// Do not create separate wrappers for default arguments +%feature("compactdefaultargs"); + +%constant ea_t BADADDR = ea_t(-1); +%constant sel_t BADSEL = sel_t(-1); +%constant nodeidx_t BADNODE = nodeidx_t(-1); + +// Help SWIG to figure out the ulonglong type +#ifdef SWIGWIN +typedef unsigned __int64 ulonglong; +typedef __int64 longlong; +#else +typedef unsigned long long ulonglong; +typedef long long longlong; +#endif + +typedef int error_t; + +%include "typemaps.i" + +%include "cstring.i" +%include "carrays.i" +%include "cpointer.i" + +%include "typeconv.i" + +%pythoncode %{ +# + +import struct +import traceback +import os +import sys +import bisect +import __builtin__ +import imp + +def require(modulename, package=None): + """ + Load, or reload a module. + + When under heavy development, a user's tool might consist of multiple + modules. If those are imported using the standard 'import' mechanism, + there is no guarantee that the Python implementation will re-read + and re-evaluate the module's Python code. In fact, it usually doesn't. + What should be done instead is 'reload()'-ing that module. + + This is a simple helper function that will do just that: In case the + module doesn't exist, it 'import's it, and if it does exist, + 'reload()'s it. + + For more information, see: . + """ + if modulename in sys.modules.keys(): + reload(sys.modules[modulename]) + else: + import importlib + import inspect + m = importlib.import_module(modulename, package) + frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1] + importer_module = inspect.getmodule(frame_obj) + if importer_module is None: # No importer module; called from command line + importer_module = sys.modules['__main__'] + setattr(importer_module, modulename, m) + sys.modules[modulename] = m + +# ----------------------------------------------------------------------- + +# Seek constants +SEEK_SET = 0 # from the file start +SEEK_CUR = 1 # from the current position +SEEK_END = 2 # from the file end + +# Plugin constants +PLUGIN_MOD = 0x0001 +PLUGIN_DRAW = 0x0002 +PLUGIN_SEG = 0x0004 +PLUGIN_UNL = 0x0008 +PLUGIN_HIDE = 0x0010 +PLUGIN_DBG = 0x0020 +PLUGIN_PROC = 0x0040 +PLUGIN_FIX = 0x0080 +PLUGIN_SKIP = 0 +PLUGIN_OK = 1 +PLUGIN_KEEP = 2 + +# PyIdc conversion object IDs +PY_ICID_INT64 = 0 +"""int64 object""" +PY_ICID_BYREF = 1 +"""byref object""" +PY_ICID_OPAQUE = 2 +"""opaque object""" + +# Step trace options (used with set_step_trace_options()) +ST_OVER_DEBUG_SEG = 0x01 +"""step tracing will be disabled when IP is in a debugger segment""" + +ST_OVER_LIB_FUNC = 0x02 +"""step tracing will be disabled when IP is in a library function""" + +# ----------------------------------------------------------------------- +class pyidc_opaque_object_t(object): + """This is the base class for all Python<->IDC opaque objects""" + __idc_cvt_id__ = PY_ICID_OPAQUE + +# ----------------------------------------------------------------------- +class py_clinked_object_t(pyidc_opaque_object_t): + """ + This is a utility and base class for C linked objects + """ + def __init__(self, lnk = None): + # static link: if a link was provided + self.__static_clink__ = True if lnk else False + + # Create link if it was not provided + self.__clink__ = lnk if lnk else self._create_clink() + + def __del__(self): + """Delete the link upon object destruction (only if not static)""" + self._free() + + def _free(self): + """Explicitly delete the link (only if not static)""" + if not self.__static_clink__ and self.__clink__ is not None: + self._del_clink(self.__clink__) + self.__clink__ = None + + def copy(self): + """Returns a new copy of this class""" + + # Create an unlinked instance + inst = self.__class__() + + # Assign self to the new instance + inst.assign(self) + + return inst + + # + # Methods to be overwritten + # + def _create_clink(self): + """ + Overwrite me. + Creates a new clink + @return: PyCObject representing the C link + """ + pass + + def _del_clink(self, lnk): + """ + Overwrite me. + This method deletes the link + """ + pass + + def _get_clink_ptr(self): + """ + Overwrite me. + Returns the C link pointer as a 64bit number + """ + pass + + def assign(self, other): + """ + Overwrite me. + This method allows you to assign an instance contents to anothers + @return: Boolean + """ + pass + + clink = property(lambda self: self.__clink__) + """Returns the C link as a PyObject""" + + clink_ptr = property(lambda self: self._get_clink_ptr()) + """Returns the C link pointer as a number""" + +# ----------------------------------------------------------------------- +class object_t(object): + """Helper class used to initialize empty objects""" + def __init__(self, **kwds): + self.__dict__ = kwds + + def __getitem__(self, idx): + """Allow access to object attributes by index (like dictionaries)""" + return getattr(self, idx) + +# ----------------------------------------------------------------------- +def _bounded_getitem_iterator(self): + """Helper function, to be set as __iter__ method for qvector-, or array-based classes.""" + for i in range(len(self)): + yield self[i] + +# ----------------------------------------------------------------------- +class plugin_t(pyidc_opaque_object_t): + """Base class for all scripted plugins.""" + pass + +# ----------------------------------------------------------------------- +class pyidc_cvt_helper__(object): + """ + This is a special helper object that helps detect which kind + of object is this python object wrapping and how to convert it + back and from IDC. + This object is characterized by its special attribute and its value + """ + def __init__(self, cvt_id, value): + self.__idc_cvt_id__ = cvt_id + self.value = value + + def __set_value(self, v): + self.__idc_cvt_value__ = v + def __get_value(self): + return self.__idc_cvt_value__ + value = property(__get_value, __set_value) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_int64__(pyidc_cvt_helper__): + """Helper class for explicitly representing VT_INT64 values""" + + def __init__(self, v): + # id = 0 = int64 object + super(self.__class__, self).__init__(PY_ICID_INT64, v) + + # operation table + __op_table = \ + { + 0: lambda a, b: a + b, + 1: lambda a, b: a - b, + 2: lambda a, b: a * b, + 3: lambda a, b: a / b + } + # carries the operation given its number + def __op(self, op_n, other, rev=False): + a = self.value + # other operand of same type? then take its value field + if type(other) == type(self): + b = other.value + else: + b = other + if rev: + t = a + a = b + b = t + # construct a new object and return as the result + return self.__class__(self.__op_table[op_n](a, b)) + + # overloaded operators + def __add__(self, other): return self.__op(0, other) + def __sub__(self, other): return self.__op(1, other) + def __mul__(self, other): return self.__op(2, other) + def __div__(self, other): return self.__op(3, other) + def __radd__(self, other): return self.__op(0, other, True) + def __rsub__(self, other): return self.__op(1, other, True) + def __rmul__(self, other): return self.__op(2, other, True) + def __rdiv__(self, other): return self.__op(3, other, True) + +# ----------------------------------------------------------------------- +# qstrvec_t clinked object +class _qstrvec_t(py_clinked_object_t): + """ + WARNING: It is very unlikely an IDAPython user should ever, ever + have to use this type. It should only be used for IDAPython internals. + + For example, in py_askusingform.py, we ctypes-expose to the IDA + kernel & UI a qstrvec instance, in case a DropdownListControl is + constructed. + That's because that's what AskUsingForm expects, and we have no + choice but to make a DropdownListControl hold a qstrvec_t. + This is, afaict, the only situation where a Python + _qstrvec_t is required. + """ + + def __init__(self, items=None): + py_clinked_object_t.__init__(self) + # Populate the list if needed + if items: + self.from_list(items) + + def _create_clink(self): + return _idaapi.qstrvec_t_create() + + def _del_clink(self, lnk): + return _idaapi.qstrvec_t_destroy(lnk) + + def _get_clink_ptr(self): + return _idaapi.qstrvec_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.qstrvec_t_assign(self, other) + + def __setitem__(self, idx, s): + """Sets string at the given index""" + return _idaapi.qstrvec_t_set(self, idx, s) + + def __getitem__(self, idx): + """Gets the string at the given index""" + return _idaapi.qstrvec_t_get(self, idx) + + def __get_size(self): + return _idaapi.qstrvec_t_size(self) + + size = property(__get_size) + """Returns the count of elements""" + + def addressof(self, idx): + """Returns the address (as number) of the qstring at the given index""" + return _idaapi.qstrvec_t_addressof(self, idx) + + def add(self, s): + """Add a string to the vector""" + return _idaapi.qstrvec_t_add(self, s) + + def from_list(self, lst): + """Populates the vector from a Python string list""" + return _idaapi.qstrvec_t_from_list(self, lst) + + def clear(self, qclear=False): + """ + Clears all strings from the vector. + @param qclear: Just reset the size but do not actually free the memory + """ + return _idaapi.qstrvec_t_clear(self, qclear) + + def insert(self, idx, s): + """Insert a string into the vector""" + return _idaapi.qstrvec_t_insert(self, idx, s) + + def remove(self, idx): + """Removes a string from the vector""" + return _idaapi.qstrvec_t_remove(self, idx) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_refclass__(pyidc_cvt_helper__): + """Helper class for representing references to immutable objects""" + def __init__(self, v): + # id = one = byref object + super(self.__class__, self).__init__(PY_ICID_BYREF, v) + + def cstr(self): + """Returns the string as a C string (up to the zero termination)""" + return as_cstr(self.value) + +# ----------------------------------------------------------------------- +def as_cstr(val): + """ + Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) + It scans for the first \x00 and returns the string value up to that point. + """ + if isinstance(val, PyIdc_cvt_refclass__): + val = val.value + + n = val.find('\x00') + return val if n == -1 else val[:n] + +# ----------------------------------------------------------------------- +def as_unicode(s): + """Convenience function to convert a string into appropriate unicode format""" + # use UTF16 big/little endian, depending on the environment? + return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) + +# ----------------------------------------------------------------------- +def as_uint32(v): + """Returns a number as an unsigned int32 number""" + return v & 0xffffffff + +# ----------------------------------------------------------------------- +def as_int32(v): + """Returns a number as a signed int32 number""" + return -((~v & 0xffffffff)+1) + +# ----------------------------------------------------------------------- +def as_signed(v, nbits = 32): + """ + Returns a number as signed. The number of bits are specified by the user. + The MSB holds the sign. + """ + return -(( ~v & ((1 << nbits)-1) ) + 1) if v & (1 << nbits-1) else v + +# ---------------------------------------------------------------------- +def copy_bits(v, s, e=-1): + """ + Copy bits from a value + @param v: the value + @param s: starting bit (0-based) + @param e: ending bit + """ + # end-bit not specified? use start bit (thus extract one bit) + if e == -1: + e = s + # swap start and end if start > end + if s > e: + e, s = s, e + + mask = ~(((1 << (e-s+1))-1) << s) + + return (v & mask) >> s + +# ---------------------------------------------------------------------- +__struct_unpack_table = { + 1: ('b', 'B'), + 2: ('h', 'H'), + 4: ('l', 'L'), + 8: ('q', 'Q') +} + +# ---------------------------------------------------------------------- +def struct_unpack(buffer, signed = False, offs = 0): + """ + Unpack a buffer given its length and offset using struct.unpack_from(). + This function will know how to unpack the given buffer by using the lookup table '__struct_unpack_table' + If the buffer is of unknown length then None is returned. Otherwise the unpacked value is returned. + """ + # Supported length? + n = len(buffer) + if n not in __struct_unpack_table: + return None + # Conver to number + signed = 1 if signed else 0 + + # Unpack + return struct.unpack_from(__struct_unpack_table[n][signed], buffer, offs)[0] + + +# ------------------------------------------------------------ +def IDAPython_ExecSystem(cmd): + """ + Executes a command with popen(). + """ + try: + f = os.popen(cmd, "r") + s = ''.join(f.readlines()) + f.close() + return s + except Exception as e: + return "%s\n%s" % (str(e), traceback.format_exc()) + +# ------------------------------------------------------------ +def IDAPython_FormatExc(etype, value, tb, limit=None): + """ + This function is used to format an exception given the + values returned by a PyErr_Fetch() + """ + try: + return ''.join(traceback.format_exception(etype, value, tb, limit)) + except: + return str(value) + + +# ------------------------------------------------------------ +def IDAPython_ExecScript(script, g): + """ + Run the specified script. + It also addresses http://code.google.com/p/idapython/issues/detail?id=42 + + This function is used by the low-level plugin code. + """ + scriptpath = os.path.dirname(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + argv = sys.argv + sys.argv = [ script ] + + # Adjust the __file__ path in the globals we pass to the script + old__file__ = g['__file__'] if '__file__' in g else '' + g['__file__'] = script + + try: + execfile(script, g) + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + finally: + # Restore state + g['__file__'] = old__file__ + sys.argv = argv + + return PY_COMPILE_ERR + +# ------------------------------------------------------------ +def IDAPython_LoadProcMod(script, g): + """ + Load processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptpath, scriptname = os.path.split(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + procmod_name = os.path.splitext(scriptname)[0] + procobj = None + fp = None + try: + fp, pathname, description = imp.find_module(procmod_name) + procmod = imp.load_module(procmod_name, fp, pathname, description) + if parent: + setattr(parent, procmod_name, procmod) + # export attrs from parent to processor module + parent_attrs = getattr(parent, '__all__', + (attr for attr in dir(parent) if not attr.startswith('_'))) + for pa in parent_attrs: + setattr(procmod, pa, getattr(parent, pa)) + # instantiate processor object + if getattr(procmod, 'PROCESSOR_ENTRY', None): + procobj = procmod.PROCESSOR_ENTRY() + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + finally: + if fp: fp.close() + + sys.path.remove(scriptpath) + + return (PY_COMPILE_ERR, procobj) + +# ------------------------------------------------------------ +def IDAPython_UnLoadProcMod(script, g): + """ + Unload processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptname = os.path.split(script)[1] + procmod_name = os.path.splitext(scriptname)[0] + if getattr(parent, procmod_name, None): + delattr(parent, procmod_name) + del sys.modules[procmod_name] + PY_COMPILE_ERR = None + return PY_COMPILE_ERR + +# ---------------------------------------------------------------------- +class __IDAPython_Completion_Util(object): + """Internal utility class for auto-completion support""" + def __init__(self): + self.n = 0 + self.completion = None + self.lastmodule = None + + @staticmethod + def parse_identifier(line, prefix, prefix_start): + """ + Parse a line and extracts identifier + """ + id_start = prefix_start + while id_start > 0: + ch = line[id_start] + if not ch.isalpha() and ch != '.' and ch != '_': + id_start += 1 + break + id_start -= 1 + + return line[id_start:prefix_start + len(prefix)] + + @staticmethod + def dir_of(m, prefix): + return [x for x in dir(m) if x.startswith(prefix)] + + @classmethod + def get_completion(cls, id, prefix): + try: + m = sys.modules['__main__'] + + parts = id.split('.') + c = len(parts) + + for i in xrange(0, c-1): + m = getattr(m, parts[i]) + except Exception as e: + return (None, None) + else: + # search in the module + completion = cls.dir_of(m, prefix) + + # no completion found? looking from the global scope? then try the builtins + if not completion and c == 1: + completion = cls.dir_of(__builtin__, prefix) + + return (m, completion) if completion else (None, None) + + def __call__(self, prefix, n, line, prefix_start): + if n == 0: + self.n = n + id = self.parse_identifier(line, prefix, prefix_start) + self.lastmodule, self.completion = self.get_completion(id, prefix) + + if self.completion is None or n >= len(self.completion): + return None + + s = self.completion[n] + try: + attr = getattr(self.lastmodule, s) + # Is it callable? + if callable(attr): + return s + ("" if line.startswith("?") else "(") + # Is it iterable? + elif isinstance(attr, basestring) or getattr(attr, '__iter__', False): + return s + "[" + except: + pass + + return s + +# Instantiate an IDAPython command completion object (for use with IDA's CLI bar) +IDAPython_Completion = __IDAPython_Completion_Util() + +def _listify_types(*classes): + for cls in classes: + cls.__getitem__ = cls.at + cls.__len__ = cls.size + cls.__iter__ = _bounded_getitem_iterator + + + +# The general callback format of notify_when() is: +# def notify_when_callback(nw_code) +# In the case of NW_OPENIDB, the callback is: +# def notify_when_callback(nw_code, is_old_database) +NW_OPENIDB = 0x0001 +"""Notify when the database is opened. Its callback is of the form: def notify_when_callback(nw_code, is_old_database)""" +NW_CLOSEIDB = 0x0002 +"""Notify when the database is closed. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_INITIDA = 0x0004 +"""Notify when the IDA starts. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_TERMIDA = 0x0008 +"""Notify when the IDA terminates. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_REMOVE = 0x0010 +"""Use this flag with other flags to uninstall a notifywhen callback""" + +# +%} + +%include "pro.i" + +// Do not move this. We need to override the define from pro.h +#define CASSERT(type) + +// Convert all of these +%cstring_output_maxstr_none(char *buf, size_t bufsize); +%binary_output_or_none(void *buf, size_t bufsize); +%binary_output_with_size(void *buf, size_t *bufsize); + +// Accept single Python string for const void * + size input arguments +// For example: put_many_bytes() and patch_many_bytes() +%apply (char *STRING, int LENGTH) { (const void *buf, size_t size) }; +%apply (char *STRING, int LENGTH) { (const void *buf, size_t len) }; +%apply (char *STRING, int LENGTH) { (const void *value, size_t length) }; +%apply (char *STRING, int LENGTH) { (const void *dataptr,size_t len) }; + +// Create wrapper classes for basic type arrays +%array_class(uchar, uchar_array); +%array_class(tid_t, tid_array); +%array_class(ea_t, ea_array); +%array_class(sel_t, sel_array); +%array_class(uval_t, uval_array); +%pointer_class(int, int_pointer); +%pointer_class(ea_t, ea_pointer); +%pointer_class(sval_t, sval_pointer); +%pointer_class(sel_t, sel_pointer); + +%include "ida.i" +%include "idd.i" +%include "idp.i" +%include "netnode.i" +%include "nalt.i" + +%include "allins.i" +%include "area.i" +%include "auto.i" +%include "bytes.i" +%include "dbg.i" +%include "diskio.i" +%include "entry.i" +%include "enum.i" +%include "expr.i" +%include "fixup.i" +%include "frame.i" +%include "funcs.i" +%include "typeinf.i" +#ifdef WITH_HEXRAYS + %include "hexrays.i" +#endif + +SWIG_DECLARE_PY_CLINKED_OBJECT(qstrvec_t) + +%{ +PyObject *qstrvec2pylist(qstrvec_t &vec) +{ + size_t n = vec.size(); + PyObject *py_list = PyList_New(n); + for ( size_t i=0; i < n; ++i ) + PyList_SetItem(py_list, i, PyString_FromString(vec[i].c_str())); + return py_list; +} +%} + +%inline %{ + +// +//------------------------------------------------------------------------ +/* +# +def parse_command_line(cmdline): + """ + Parses a space separated string (quotes and escape character are supported) + @param cmdline: The command line to parse + @return: A list of strings or None on failure + """ + pass +# +*/ +static PyObject *py_parse_command_line(const char *cmdline) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstrvec_t args; + if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 ) + Py_RETURN_NONE; + return qstrvec2pylist(args); +} + +//------------------------------------------------------------------------- +/* +# +def get_inf_structure(): + """ + Returns the global variable 'inf' (an instance of idainfo structure, see ida.hpp) + """ + pass +# +*/ +idainfo *get_inf_structure(void) +{ + return &inf; +} + +//------------------------------------------------------------------------- +// Declarations from Python.cpp +/* +# +def set_script_timeout(timeout): + """ + Changes the script timeout value. The script wait box dialog will be hidden and shown again when the timeout elapses. + See also L{disable_script_timeout}. + + @param timeout: This value is in seconds. + If this value is set to zero then the script will never timeout. + @return: Returns the old timeout value + """ + pass +# +*/ +int set_script_timeout(int timeout); + +/* +# +def disable_script_timeout(): + """ + Disables the script timeout and hides the script wait box. + Calling L{set_script_timeout} will not have any effects until the script is compiled and executed again + + @return: None + """ + pass +# +*/ +void disable_script_timeout(); + +/* +# +def enable_extlang_python(enable): + """ + Enables or disables Python extlang. + When enabled, all expressions will be evaluated by Python. + @param enable: Set to True to enable, False otherwise + """ + pass +# +*/ +void enable_extlang_python(bool enable); +void enable_python_cli(bool enable); + +/* +# +def RunPythonStatement(stmt): + """ + This is an IDC function exported from the Python plugin. + It is used to evaluate Python statements from IDC. + @param stmt: The statement to evaluate + @return: 0 - on success otherwise a string containing the error + """ + pass +# +*/ + +//--------------------------------------------------------------------------- +// qstrvec_t wrapper (INTERNAL! Don't expose. See py_idaapi.py) +//--------------------------------------------------------------------------- +static bool qstrvec_t_assign(PyObject *self, PyObject *other) +{ + qstrvec_t *lhs = qstrvec_t_get_clink(self); + qstrvec_t *rhs = qstrvec_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + *lhs = *rhs; + return true; +} + +static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + Py_RETURN_NONE; + else + return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)&sv->at(idx)); +} + + +static bool qstrvec_t_set( + PyObject *self, + size_t idx, + const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + (*sv)[idx] = s; + return true; +} + +static bool qstrvec_t_from_list( + PyObject *self, + PyObject *py_list) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t *sv = qstrvec_t_get_clink(self); + return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); +} + +static size_t qstrvec_t_size(PyObject *self) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + return sv == NULL ? 0 : sv->size(); +} + +static PyObject *qstrvec_t_get(PyObject *self, size_t idx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + Py_RETURN_NONE; + return PyString_FromString(sv->at(idx).c_str()); +} + +static bool qstrvec_t_add(PyObject *self, const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL ) + return false; + sv->push_back(s); + return true; +} + +static bool qstrvec_t_clear(PyObject *self, bool qclear) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL ) + return false; + + if ( qclear ) + sv->qclear(); + else + sv->clear(); + + return true; +} + +static bool qstrvec_t_insert( + PyObject *self, + size_t idx, + const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + sv->insert(sv->begin() + idx, s); + return true; +} + +static bool qstrvec_t_remove(PyObject *self, size_t idx) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + + sv->erase(sv->begin()+idx); + return true; +} + +//--------------------------------------------------------------------------- + + + +//------------------------------------------------------------------------ +/* +# +def notify_when(when, callback): + """ + Register a callback that will be called when an event happens. + @param when: one of NW_XXXX constants + @param callback: This callback prototype varies depending on the 'when' parameter: + The general callback format: + def notify_when_callback(nw_code) + In the case of NW_OPENIDB: + def notify_when_callback(nw_code, is_old_database) + @return: Boolean + """ + pass +# +*/ +static bool notify_when(int when, PyObject *py_callable) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( g_nw == NULL || !PyCallable_Check(py_callable) ) + return false; + return g_nw->notify_when(when, py_callable); +} + +// +%} + +%include "gdl.i" +%include "ints.i" +%include "kernwin.i" +%include "lines.i" +%include "loader.i" +%include "moves.i" +%include "name.i" +%include "offset.i" +%include "queue.i" +%include "search.i" +%include "segment.i" +%include "srarea.i" +%include "strlist.i" +%include "struct.i" +%include "ua.i" +%include "xref.i" +%include "view.i" +%include "graph.i" +%include "fpro.i" +%include "registry.i" diff --git a/swig/idd.i b/swig/idd.i index d8c5794f..48aa6b8a 100644 --- a/swig/idd.i +++ b/swig/idd.i @@ -1,903 +1,903 @@ -%ignore debugger_t; -%ignore memory_info_t; -%ignore lowcnd_t; -%ignore lowcnd_vec_t; -%ignore update_bpt_info_t; -%ignore update_bpt_vec_t; -%ignore register_info_t; -%ignore appcall; -%ignore idd_opinfo_t; -%ignore gdecode_t; -%apply unsigned char { char dtyp }; - -%include "idd.hpp" - -%clear(char dtyp); - -%pythoncode %{ -# -NO_PROCESS = 0xFFFFFFFF -NO_THREAD = 0 -# -%} - -%{ -// - -//------------------------------------------------------------------------- -static bool dbg_can_query() -{ - // Reject the request only if no debugger is set - // or the debugger cannot be queried while not in suspended state - return dbg != NULL && (dbg->may_disturb() || get_process_state() < DSTATE_NOTASK); -} - -//------------------------------------------------------------------------- -static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - PyObject *py_list = PyList_New(areas.size()); - meminfo_vec_t::const_iterator it, it_end(areas.end()); - Py_ssize_t i = 0; - for ( it=areas.begin(); it!=it_end; ++it, ++i ) - { - const memory_info_t &mi = *it; - // startEA endEA name sclass sbase bitness perm - PyList_SetItem(py_list, i, - Py_BuildValue("("PY_FMT64 PY_FMT64 "ss" PY_FMT64 "II)", - pyul_t(mi.startEA), - pyul_t(mi.endEA), - mi.name.c_str(), - mi.sclass.c_str(), - pyul_t(mi.sbase), - (unsigned int)(mi.bitness), - (unsigned int)mi.perm)); - } - return py_list; -} - -//------------------------------------------------------------------------- -PyObject *py_appcall( - ea_t func_ea, - thid_t tid, - PyObject *py_type, - PyObject *py_fields, - PyObject *arg_list) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !PyList_Check(arg_list) ) - return NULL; - - const char *type = py_type == Py_None ? NULL : PyString_AS_STRING(py_type); - const char *fields = py_fields == Py_None ? NULL : PyString_AS_STRING(py_fields); - - // Convert Python arguments into IDC values - qvector idc_args; - int sn = 0; - Py_ssize_t nargs = PyList_Size(arg_list); - idc_args.resize(nargs); - bool ok = true; - for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); - } - // Convert it - if ( pyvar_to_idcvar(py_item, &idc_args[i], &sn) < CIP_OK ) - { - ok = false; - break; - } - } - - // Set exception message - if ( !ok ) - { - PyErr_SetString( - PyExc_ValueError, - "PyAppCall: Failed to convert Python values to IDC values"); - return NULL; - } - - error_t ret; - idc_value_t idc_result; - Py_BEGIN_ALLOW_THREADS; - - if ( (debug & IDA_DEBUG_APPCALL) != 0 ) - { - msg("input variables:\n" - "----------------\n"); - - qstring s; - for ( Py_ssize_t i=0; iob_refcnt == 1); - if ( (debug & IDA_DEBUG_APPCALL) != 0 ) - { - msg("return var:\n" - "-----------\n"); - qstring s; - VarPrint(&s, &idc_result); - msg("%s\n-----------\n", s.c_str()); - } - py_result.incref(); - return py_result.o; -} -// -%} - -%rename (appcall) py_appcall; - -%inline %{ - -// - -//------------------------------------------------------------------------- -/* -# -def dbg_get_registers(): - """ - This function returns the register definition from the currently loaded debugger. - Basically, it returns an array of structure similar to to idd.hpp / register_info_t - @return: - None if no debugger is loaded - tuple(name, flags, class, dtyp, bit_strings, bit_strings_default_mask) - The bit_strings can be a tuple of strings or None (if the register does not have bit_strings) - """ - pass -# -*/ -static PyObject *dbg_get_registers() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( dbg == NULL ) - Py_RETURN_NONE; - - PyObject *py_list = PyList_New(dbg->registers_size); - - for ( int i=0; iregisters_size; i++ ) - { - register_info_t &ri = dbg->registers[i]; - PyObject *py_bits; - - // Does this register have bit strings? - // (Make sure it does not use custom formats because bit_string would be the format name) - if ( ri.bit_strings != NULL && (ri.flags & REGISTER_CUSTFMT) == 0 ) - { - int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; - py_bits = PyList_New(nbits); - for ( int i=0; i -def dbg_get_thread_sreg_base(tid, sreg_value): - """ - Returns the segment register base value - @param tid: thread id - @param sreg_value: segment register (selector) value - @return: - - The base as an 'ea' - - Or None on failure - """ - pass -# -*/ -static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) - Py_RETURN_NONE; - ea_t answer; - thid_t tid = PyInt_AsLong(py_tid); - int sreg_value = PyInt_AsLong(py_sreg_value); - if ( internal_get_sreg_base(tid, sreg_value, &answer) != 1 ) - Py_RETURN_NONE; - - return Py_BuildValue(PY_FMT64, pyul_t(answer)); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_read_memory(ea, sz): - """ - Reads from the debugee's memory at the specified ea - @return: - - The read buffer (as a string) - - Or None on failure - """ - pass -# -*/ -static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - uint64 ea, sz; - if ( !dbg_can_query() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(py_sz, &sz) ) - Py_RETURN_NONE; - - // Create a Python string - PyObject *ret = PyString_FromStringAndSize(NULL, Py_ssize_t(sz)); - if ( ret == NULL ) - Py_RETURN_NONE; - - // Get the internal buffer - Py_ssize_t len; - char *buf; - PyString_AsStringAndSize(ret, &buf, &len); - - if ( (size_t)read_dbg_memory(ea_t(ea), buf, size_t(sz)) != sz ) - { - // Release the string on failure - Py_DECREF(ret); - // Return None on failure - Py_RETURN_NONE; - } - return ret; -} - -//------------------------------------------------------------------------- -/* -# -def dbg_write_memory(ea, buffer): - """ - Writes a buffer to the debugee's memory - @return: Boolean - """ - pass -# -*/ -static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - uint64 ea; - if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyW_GetNumber(py_ea, &ea) ) - Py_RETURN_NONE; - - size_t sz = PyString_GET_SIZE(py_buf); - void *buf = (void *)PyString_AS_STRING(py_buf); - if ( write_dbg_memory(ea_t(ea), buf, sz) != sz ) - Py_RETURN_FALSE; - Py_RETURN_TRUE; -} - -//------------------------------------------------------------------------- -/* -# -def dbg_get_name(): - """ - This function returns the current debugger's name. - @return: Debugger name or None if no debugger is active - """ - pass -# -*/ -static PyObject *dbg_get_name() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( dbg == NULL ) - Py_RETURN_NONE; - else - return PyString_FromString(dbg->name); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_get_memory_info(): - """ - This function returns the memory configuration of a debugged process. - @return: - None if no debugger is active - tuple(startEA, endEA, name, sclass, sbase, bitness, perm) - """ - pass -# -*/ -static PyObject *dbg_get_memory_info() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !dbg_can_query() ) - Py_RETURN_NONE; - - // Invalidate memory - meminfo_vec_t areas; - Py_BEGIN_ALLOW_THREADS; - invalidate_dbgmem_config(); - invalidate_dbgmem_contents(BADADDR, BADADDR); - - get_dbg_memory_info(&areas); - Py_END_ALLOW_THREADS; - return meminfo_vec_t_to_py(areas); -} - -//------------------------------------------------------------------------- -/* -# -def dbg_can_query(): - """ - This function can be used to check if the debugger can be queried: - - debugger is loaded - - process is suspended - - process is not suspended but can take requests. In this case some requests like - memory read/write, bpt management succeed and register querying will fail. - Check if idaapi.get_process_state() < 0 to tell if the process is suspended - @return: Boolean - """ - pass -# -*/ -static bool dbg_can_query(); -PyObject *py_appcall( - ea_t func_ea, - thid_t tid, - PyObject *py_type, - PyObject *py_fields, - PyObject *arg_list); -// - -char get_event_module_name(const debug_event_t* ev, char *buf, size_t bufsize) -{ - qstrncpy(buf, ev->modinfo.name, bufsize); - return true; -} - -ea_t get_event_module_base(const debug_event_t* ev) -{ - return ev->modinfo.base; -} - -asize_t get_event_module_size(const debug_event_t* ev) -{ - return ev->modinfo.size; -} - -char get_event_exc_info(const debug_event_t* ev, char *buf, size_t bufsize) -{ - qstrncpy(buf, ev->exc.info, bufsize); - return true; -} - -char get_event_info(const debug_event_t* ev, char *buf, size_t bufsize) -{ - qstrncpy(buf, ev->info, bufsize); - return true; -} - -ea_t get_event_bpt_hea(const debug_event_t* ev) -{ - return ev->bpt.hea; -} - -uint get_event_exc_code(const debug_event_t* ev) -{ - return ev->exc.code; -} - -ea_t get_event_exc_ea(const debug_event_t* ev) -{ - return ev->exc.ea; -} - -bool can_exc_continue(const debug_event_t* ev) -{ - return ev->exc.can_cont; -} -%} - -%pythoncode %{ -# -import types - -# ----------------------------------------------------------------------- -class Appcall_array__(object): - """This class is used with Appcall.array() method""" - def __init__(self, tp): - self.__type = tp - - def pack(self, L): - """Packs a list or tuple into a byref buffer""" - t = type(L) - if not (t == types.ListType or t == types.TupleType): - raise ValueError, "Either a list or a tuple must be passed" - self.__size = len(L) - if self.__size == 1: - self.__typedobj = Appcall__.typedobj(self.__type + ";") - else: - self.__typedobj = Appcall__.typedobj("%s x[%d];" % (self.__type, self.__size)) - # Now store the object in a string buffer - ok, buf = self.__typedobj.store(L) - if ok: - return Appcall__.byref(buf) - else: - return None - - def try_to_convert_to_list(self, obj): - """Is this object a list? We check for the existance of attribute zero and attribute self.size-1""" - if not (hasattr(obj, "0") and hasattr(obj, str(self.__size-1))): - return obj - # at this point, we are sure we have an "idc list" - # let us convert to a Python list - return [getattr(obj, str(x)) for x in xrange(0, self.__size)] - - def unpack(self, buf, as_list=True): - """Unpacks an array back into a list or an object""" - # take the value from the special ref object - if isinstance(buf, PyIdc_cvt_refclass__): - buf = buf.value - - # we can only unpack from strings - if type(buf) != types.StringType: - raise ValueError, "Cannot unpack this type!" - # now unpack - ok, obj = self.__typedobj.retrieve(buf) - if not ok: - raise ValueError, "Failed while unpacking!" - if not as_list: - return obj - return self.try_to_convert_to_list(obj) - - -# ----------------------------------------------------------------------- -# Wrapper class for the appcall() -class Appcall_callable__(object): - """ - Helper class to issue appcalls using a natural syntax: - appcall.FunctionNameInTheDatabase(arguments, ....) - or - appcall["Function@8"](arguments, ...) - or - f8 = appcall["Function@8"] - f8(arg1, arg2, ...) - or - o = appcall.obj() - i = byref(5) - appcall.funcname(arg1, i, "hello", o) - """ - def __init__(self, ea, tp = None, fld = None): - """Initializes an appcall with a given function ea""" - self.__ea = ea - self.__type = tp - self.__fields = fld - self.__options = None # Appcall options - self.__timeout = None # Appcall timeout - - def __get_timeout(self): - return self.__timeout - - def __set_timeout(self, v): - self.__timeout = v - - timeout = property(__get_timeout, __set_timeout) - """An Appcall instance can change its timeout value with this attribute""" - - def __get_options(self): - return self.__options if self.__options != None else Appcall__.get_appcall_options() - - def __set_options(self, v): - if self.timeout: - # If timeout value is set, then put the timeout flag and encode the timeout value - v |= Appcall__.APPCALL_TIMEOUT | (self.timeout << 16) - else: - # Timeout is not set, then clear the timeout flag - v &= ~Appcall__.APPCALL_TIMEOUT - - self.__options = v - - options = property(__get_options, __set_options) - """Sets the Appcall options locally to this Appcall instance""" - - def __call__(self, *args): - """Make object callable. We redirect execution to idaapi.appcall()""" - if self.ea is None: - raise ValueError, "Object not callable!" - - # convert arguments to a list - arg_list = list(args) - - # Save appcall options and set new global options - old_opt = Appcall__.get_appcall_options() - Appcall__.set_appcall_options(self.options) - - # Do the Appcall (use the wrapped version) - e_obj = None - try: - r = _idaapi.appcall( - self.ea, - _idaapi.get_current_thread(), - self.type, - self.fields, - arg_list) - except Exception as e: - e_obj = e - - # Restore appcall options - Appcall__.set_appcall_options(old_opt) - - # Return or re-raise exception - if e_obj: - raise Exception, e_obj - - return r - - def __get_ea(self): - return self.__ea - - def __set_ea(self, val): - self.__ea = val - - ea = property(__get_ea, __set_ea) - """Returns or sets the EA associated with this object""" - - def __get_size(self): - if self.__type == None: - return -1 - r = _idaapi.calc_type_size(_idaapi.cvar.idati, self.__type) - if not r: - return -1 - return r - - size = property(__get_size) - """Returns the size of the type""" - - def __get_type(self): - return self.__type - - type = property(__get_type) - """Returns the typestring""" - - def __get_fields(self): - return self.__fields - - fields = property(__get_fields) - """Returns the field names""" - - - def retrieve(self, src=None, flags=0): - """ - Unpacks a typed object from the database if an ea is given or from a string if a string was passed - @param src: the address of the object or a string - @return: Returns a tuple of boolean and object or error number (Bool, Error | Object). - """ - - # Nothing passed? Take the address and unpack from the database - if src is None: - src = self.ea - - if type(src) == types.StringType: - return _idaapi.unpack_object_from_bv(_idaapi.cvar.idati, self.type, self.fields, src, flags) - else: - return _idaapi.unpack_object_from_idb(_idaapi.cvar.idati, self.type, self.fields, src, flags) - - def store(self, obj, dest_ea=None, base_ea=0, flags=0): - """ - Packs an object into a given ea if provided or into a string if no address was passed. - @param obj: The object to pack - @param dest_ea: If packing to idb this will be the store location - @param base_ea: If packing to a buffer, this will be the base that will be used to relocate the pointers - - @return: - - If packing to a string then a Tuple(Boolean, packed_string or error code) - - If packing to the database then a return code is returned (0 is success) - """ - - # no ea passed? thus pack to a string - if dest_ea is None: - return _idaapi.pack_object_to_bv(obj, - _idaapi.cvar.idati, - self.type, - self.fields, - base_ea, - flags) - else: - return _idaapi.pack_object_to_idb(obj, - _idaapi.cvar.idati, - self.type, - self.fields, - dest_ea, - flags) - -# ----------------------------------------------------------------------- -class Appcall_consts__(object): - """Helper class used by Appcall.Consts attribute - It is used to retrieve constants via attribute access""" - def __init__(self, default=0): - self.__default = default - - def __getattr__(self, attr): - return Appcall__.valueof(attr, self.__default) - -# ----------------------------------------------------------------------- -class Appcall__(object): - APPCALL_MANUAL = 0x1 - """ - Only set up the appcall, do not run it. - you should call CleanupAppcall() when finished - """ - - APPCALL_DEBEV = 0x2 - """ - Return debug event information - If this bit is set, exceptions during appcall - will generate idc exceptions with full - information about the exception - """ - - APPCALL_TIMEOUT = 0x4 - """ - Appcall with timeout - The timeout value in milliseconds is specified - in the high 2 bytes of the 'options' argument: - If timed out, errbuf will contain "timeout". - """ - - def __init__(self): - self.__consts = Appcall_consts__() - def __get_consts(self): - return self.__consts - Consts = property(__get_consts) - """Use Appcall.Consts.CONST_NAME to access constants""" - - @staticmethod - def __name_or_ea(name_or_ea): - """ - Function that accepts a name or an ea and checks if the address is enabled. - If a name is passed then idaapi.get_name_ea() is applied to retrieve the name - @return: - - Returns the resolved EA or - - Raises an exception if the address is not enabled - """ - - # a string? try to resolve it - if type(name_or_ea) == types.StringType: - ea = _idaapi.get_name_ea(_idaapi.BADADDR, name_or_ea) - else: - ea = name_or_ea - # could not resolve name or invalid address? - if ea == _idaapi.BADADDR or not _idaapi.isEnabled(ea): - raise ValueError, "Undefined function " + name_or_ea - return ea - - @staticmethod - def proto(name_or_ea, prototype, flags = None): - """ - Allows you to instantiate an appcall (callable object) with the desired prototype - @param name_or_ea: The name of the function (will be resolved with LocByName()) - @param prototype: - @return: - - On failure it raises an exception if the prototype could not be parsed - or the address is not resolvable - - Returns a callbable Appcall instance with the given prototypes and flags - """ - - # resolve and raise exception on error - ea = Appcall__.__name_or_ea(name_or_ea) - # parse the type - if flags is None: - flags = 1 | 2 | 4 # PT_SIL | PT_NDC | PT_TYP - - result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, prototype, flags) - if result is None: - raise ValueError, "Could not parse type: " + prototype - - # Return the callable method with type info - return Appcall_callable__(ea, result[1], result[2]) - - def __getattr__(self, name_or_ea): - """Allows you to call functions as if they were member functions (by returning a callable object)""" - # resolve and raise exception on error - ea = self.__name_or_ea(name_or_ea) - if ea == _idaapi.BADADDR: - raise ValueError, "Undefined function " + name - # Return the callable method - return Appcall_callable__(ea) - - def __getitem__(self, idx): - """ - Use self[func_name] syntax if the function name contains invalid characters for an attribute name - See __getattr___ - """ - return self.__getattr__(idx) - - @staticmethod - def valueof(name, default=0): - """ - Returns the numeric value of a given name string. - If the name could not be resolved then the default value will be returned - """ - t, v = _idaapi.get_name_value(_idaapi.BADADDR, name) - if t == 0: # NT_NONE - v = default - return v - - @staticmethod - def int64(v): - """Whenever a 64bit number is needed use this method to construct an object""" - return PyIdc_cvt_int64__(v) - - @staticmethod - def byref(val): - """ - Method to create references to immutable objects - Currently we support references to int/strings - Objects need not be passed by reference (this will be done automatically) - """ - return PyIdc_cvt_refclass__(val) - - @staticmethod - def buffer(str = None, size = 0, fill="\x00"): - """ - Creates a string buffer. The returned value (r) will be a byref object. - Use r.value to get the contents and r.size to get the buffer's size - """ - if str is None: - str = "" - left = size - len(str) - if left > 0: - str = str + (fill * left) - r = Appcall__.byref(str) - r.size = size - return r - - @staticmethod - def obj(**kwds): - """Returns an empty object or objects with attributes as passed via its keywords arguments""" - return object_t(**kwds) - - @staticmethod - def cstr(val): - return as_cstr(val) - - @staticmethod - def unicode(s): - return as_unicode(s) - - @staticmethod - def array(type_name): - """Defines an array type. Later you need to pack() / unpack()""" - return Appcall_array__(type_name) - - @staticmethod - def typedobj(typestr, ea=None): - """ - Parses a type string and returns an appcall object. - One can then use retrieve() member method - @param ea: Optional parameter that later can be used to retrieve the type - @return: Appcall object or raises ValueError exception - """ - # parse the type - result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, typestr, 1 | 2 | 4) # PT_SIL | PT_NDC | PT_TYP - if result is None: - raise ValueError, "Could not parse type: " + typestr - # Return the callable method with type info - return Appcall_callable__(ea, result[1], result[2]) - - @staticmethod - def set_appcall_options(opt): - """Method to change the Appcall options globally (not per Appcall)""" - old_opt = Appcall__.get_appcall_options() - _idaapi.cvar.inf.appcall_options = opt - return old_opt - - @staticmethod - def get_appcall_options(): - """Return the global Appcall options""" - return _idaapi.cvar.inf.appcall_options - - @staticmethod - def cleanup_appcall(tid = 0): - """Equivalent to IDC's CleanupAppcall()""" - return _idaapi.cleanup_appcall(tid) - -Appcall = Appcall__() -# -%} \ No newline at end of file +%ignore debugger_t; +%ignore memory_info_t; +%ignore lowcnd_t; +%ignore lowcnd_vec_t; +%ignore update_bpt_info_t; +%ignore update_bpt_vec_t; +%ignore register_info_t; +%ignore appcall; +%ignore idd_opinfo_t; +%ignore gdecode_t; +%apply unsigned char { char dtyp }; + +%include "idd.hpp" + +%clear(char dtyp); + +%pythoncode %{ +# +NO_PROCESS = 0xFFFFFFFF +NO_THREAD = 0 +# +%} + +%{ +// + +//------------------------------------------------------------------------- +static bool dbg_can_query() +{ + // Reject the request only if no debugger is set + // or the debugger cannot be queried while not in suspended state + return dbg != NULL && (dbg->may_disturb() || get_process_state() < DSTATE_NOTASK); +} + +//------------------------------------------------------------------------- +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + PyObject *py_list = PyList_New(areas.size()); + meminfo_vec_t::const_iterator it, it_end(areas.end()); + Py_ssize_t i = 0; + for ( it=areas.begin(); it!=it_end; ++it, ++i ) + { + const memory_info_t &mi = *it; + // startEA endEA name sclass sbase bitness perm + PyList_SetItem(py_list, i, + Py_BuildValue("("PY_FMT64 PY_FMT64 "ss" PY_FMT64 "II)", + pyul_t(mi.startEA), + pyul_t(mi.endEA), + mi.name.c_str(), + mi.sclass.c_str(), + pyul_t(mi.sbase), + (unsigned int)(mi.bitness), + (unsigned int)mi.perm)); + } + return py_list; +} + +//------------------------------------------------------------------------- +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !PyList_Check(arg_list) ) + return NULL; + + const char *type = py_type == Py_None ? NULL : PyString_AS_STRING(py_type); + const char *fields = py_fields == Py_None ? NULL : PyString_AS_STRING(py_fields); + + // Convert Python arguments into IDC values + qvector idc_args; + int sn = 0; + Py_ssize_t nargs = PyList_Size(arg_list); + idc_args.resize(nargs); + bool ok = true; + for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); + } + // Convert it + if ( pyvar_to_idcvar(py_item, &idc_args[i], &sn) < CIP_OK ) + { + ok = false; + break; + } + } + + // Set exception message + if ( !ok ) + { + PyErr_SetString( + PyExc_ValueError, + "PyAppCall: Failed to convert Python values to IDC values"); + return NULL; + } + + error_t ret; + idc_value_t idc_result; + Py_BEGIN_ALLOW_THREADS; + + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) + { + msg("input variables:\n" + "----------------\n"); + + qstring s; + for ( Py_ssize_t i=0; iob_refcnt == 1); + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) + { + msg("return var:\n" + "-----------\n"); + qstring s; + VarPrint(&s, &idc_result); + msg("%s\n-----------\n", s.c_str()); + } + py_result.incref(); + return py_result.o; +} +// +%} + +%rename (appcall) py_appcall; + +%inline %{ + +// + +//------------------------------------------------------------------------- +/* +# +def dbg_get_registers(): + """ + This function returns the register definition from the currently loaded debugger. + Basically, it returns an array of structure similar to to idd.hpp / register_info_t + @return: + None if no debugger is loaded + tuple(name, flags, class, dtyp, bit_strings, default_bit_strings_mask) + The bit_strings can be a tuple of strings or None (if the register does not have bit_strings) + """ + pass +# +*/ +static PyObject *dbg_get_registers() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( dbg == NULL ) + Py_RETURN_NONE; + + PyObject *py_list = PyList_New(dbg->registers_size); + + for ( int i=0; iregisters_size; i++ ) + { + register_info_t &ri = dbg->registers(i); + PyObject *py_bits; + + // Does this register have bit strings? + // (Make sure it does not use custom formats because bit_string would be the format name) + if ( ri.bit_strings != NULL && (ri.flags & REGISTER_CUSTFMT) == 0 ) + { + int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; + py_bits = PyList_New(nbits); + for ( int i=0; i +def dbg_get_thread_sreg_base(tid, sreg_value): + """ + Returns the segment register base value + @param tid: thread id + @param sreg_value: segment register (selector) value + @return: + - The base as an 'ea' + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) + Py_RETURN_NONE; + ea_t answer; + thid_t tid = PyInt_AsLong(py_tid); + int sreg_value = PyInt_AsLong(py_sreg_value); + if ( internal_get_sreg_base(tid, sreg_value, &answer) != 1 ) + Py_RETURN_NONE; + + return Py_BuildValue(PY_FMT64, pyul_t(answer)); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_read_memory(ea, sz): + """ + Reads from the debugee's memory at the specified ea + @return: + - The read buffer (as a string) + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + uint64 ea, sz; + if ( !dbg_can_query() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(py_sz, &sz) ) + Py_RETURN_NONE; + + // Create a Python string + PyObject *ret = PyString_FromStringAndSize(NULL, Py_ssize_t(sz)); + if ( ret == NULL ) + Py_RETURN_NONE; + + // Get the internal buffer + Py_ssize_t len; + char *buf; + PyString_AsStringAndSize(ret, &buf, &len); + + if ( (size_t)read_dbg_memory(ea_t(ea), buf, size_t(sz)) != sz ) + { + // Release the string on failure + Py_DECREF(ret); + // Return None on failure + Py_RETURN_NONE; + } + return ret; +} + +//------------------------------------------------------------------------- +/* +# +def dbg_write_memory(ea, buffer): + """ + Writes a buffer to the debugee's memory + @return: Boolean + """ + pass +# +*/ +static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + uint64 ea; + if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyW_GetNumber(py_ea, &ea) ) + Py_RETURN_NONE; + + size_t sz = PyString_GET_SIZE(py_buf); + void *buf = (void *)PyString_AS_STRING(py_buf); + if ( write_dbg_memory(ea_t(ea), buf, sz) != sz ) + Py_RETURN_FALSE; + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +/* +# +def dbg_get_name(): + """ + This function returns the current debugger's name. + @return: Debugger name or None if no debugger is active + """ + pass +# +*/ +static PyObject *dbg_get_name() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( dbg == NULL ) + Py_RETURN_NONE; + else + return PyString_FromString(dbg->name); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_get_memory_info(): + """ + This function returns the memory configuration of a debugged process. + @return: + None if no debugger is active + tuple(startEA, endEA, name, sclass, sbase, bitness, perm) + """ + pass +# +*/ +static PyObject *dbg_get_memory_info() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !dbg_can_query() ) + Py_RETURN_NONE; + + // Invalidate memory + meminfo_vec_t areas; + Py_BEGIN_ALLOW_THREADS; + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, BADADDR); + + get_dbg_memory_info(&areas); + Py_END_ALLOW_THREADS; + return meminfo_vec_t_to_py(areas); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_can_query(): + """ + This function can be used to check if the debugger can be queried: + - debugger is loaded + - process is suspended + - process is not suspended but can take requests. In this case some requests like + memory read/write, bpt management succeed and register querying will fail. + Check if idaapi.get_process_state() < 0 to tell if the process is suspended + @return: Boolean + """ + pass +# +*/ +static bool dbg_can_query(); +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list); +// + +char get_event_module_name(const debug_event_t* ev, char *buf, size_t bufsize) +{ + qstrncpy(buf, ev->modinfo.name, bufsize); + return true; +} + +ea_t get_event_module_base(const debug_event_t* ev) +{ + return ev->modinfo.base; +} + +asize_t get_event_module_size(const debug_event_t* ev) +{ + return ev->modinfo.size; +} + +char get_event_exc_info(const debug_event_t* ev, char *buf, size_t bufsize) +{ + qstrncpy(buf, ev->exc.info, bufsize); + return true; +} + +char get_event_info(const debug_event_t* ev, char *buf, size_t bufsize) +{ + qstrncpy(buf, ev->info, bufsize); + return true; +} + +ea_t get_event_bpt_hea(const debug_event_t* ev) +{ + return ev->bpt.hea; +} + +uint get_event_exc_code(const debug_event_t* ev) +{ + return ev->exc.code; +} + +ea_t get_event_exc_ea(const debug_event_t* ev) +{ + return ev->exc.ea; +} + +bool can_exc_continue(const debug_event_t* ev) +{ + return ev->exc.can_cont; +} +%} + +%pythoncode %{ +# +import types + +# ----------------------------------------------------------------------- +class Appcall_array__(object): + """This class is used with Appcall.array() method""" + def __init__(self, tp): + self.__type = tp + + def pack(self, L): + """Packs a list or tuple into a byref buffer""" + t = type(L) + if not (t == types.ListType or t == types.TupleType): + raise ValueError, "Either a list or a tuple must be passed" + self.__size = len(L) + if self.__size == 1: + self.__typedobj = Appcall__.typedobj(self.__type + ";") + else: + self.__typedobj = Appcall__.typedobj("%s x[%d];" % (self.__type, self.__size)) + # Now store the object in a string buffer + ok, buf = self.__typedobj.store(L) + if ok: + return Appcall__.byref(buf) + else: + return None + + def try_to_convert_to_list(self, obj): + """Is this object a list? We check for the existance of attribute zero and attribute self.size-1""" + if not (hasattr(obj, "0") and hasattr(obj, str(self.__size-1))): + return obj + # at this point, we are sure we have an "idc list" + # let us convert to a Python list + return [getattr(obj, str(x)) for x in xrange(0, self.__size)] + + def unpack(self, buf, as_list=True): + """Unpacks an array back into a list or an object""" + # take the value from the special ref object + if isinstance(buf, PyIdc_cvt_refclass__): + buf = buf.value + + # we can only unpack from strings + if type(buf) != types.StringType: + raise ValueError, "Cannot unpack this type!" + # now unpack + ok, obj = self.__typedobj.retrieve(buf) + if not ok: + raise ValueError, "Failed while unpacking!" + if not as_list: + return obj + return self.try_to_convert_to_list(obj) + + +# ----------------------------------------------------------------------- +# Wrapper class for the appcall() +class Appcall_callable__(object): + """ + Helper class to issue appcalls using a natural syntax: + appcall.FunctionNameInTheDatabase(arguments, ....) + or + appcall["Function@8"](arguments, ...) + or + f8 = appcall["Function@8"] + f8(arg1, arg2, ...) + or + o = appcall.obj() + i = byref(5) + appcall.funcname(arg1, i, "hello", o) + """ + def __init__(self, ea, tp = None, fld = None): + """Initializes an appcall with a given function ea""" + self.__ea = ea + self.__type = tp + self.__fields = fld + self.__options = None # Appcall options + self.__timeout = None # Appcall timeout + + def __get_timeout(self): + return self.__timeout + + def __set_timeout(self, v): + self.__timeout = v + + timeout = property(__get_timeout, __set_timeout) + """An Appcall instance can change its timeout value with this attribute""" + + def __get_options(self): + return self.__options if self.__options != None else Appcall__.get_appcall_options() + + def __set_options(self, v): + if self.timeout: + # If timeout value is set, then put the timeout flag and encode the timeout value + v |= Appcall__.APPCALL_TIMEOUT | (self.timeout << 16) + else: + # Timeout is not set, then clear the timeout flag + v &= ~Appcall__.APPCALL_TIMEOUT + + self.__options = v + + options = property(__get_options, __set_options) + """Sets the Appcall options locally to this Appcall instance""" + + def __call__(self, *args): + """Make object callable. We redirect execution to idaapi.appcall()""" + if self.ea is None: + raise ValueError, "Object not callable!" + + # convert arguments to a list + arg_list = list(args) + + # Save appcall options and set new global options + old_opt = Appcall__.get_appcall_options() + Appcall__.set_appcall_options(self.options) + + # Do the Appcall (use the wrapped version) + e_obj = None + try: + r = _idaapi.appcall( + self.ea, + _idaapi.get_current_thread(), + self.type, + self.fields, + arg_list) + except Exception as e: + e_obj = e + + # Restore appcall options + Appcall__.set_appcall_options(old_opt) + + # Return or re-raise exception + if e_obj: + raise Exception, e_obj + + return r + + def __get_ea(self): + return self.__ea + + def __set_ea(self, val): + self.__ea = val + + ea = property(__get_ea, __set_ea) + """Returns or sets the EA associated with this object""" + + def __get_size(self): + if self.__type == None: + return -1 + r = _idaapi.calc_type_size(_idaapi.cvar.idati, self.__type) + if not r: + return -1 + return r + + size = property(__get_size) + """Returns the size of the type""" + + def __get_type(self): + return self.__type + + type = property(__get_type) + """Returns the typestring""" + + def __get_fields(self): + return self.__fields + + fields = property(__get_fields) + """Returns the field names""" + + + def retrieve(self, src=None, flags=0): + """ + Unpacks a typed object from the database if an ea is given or from a string if a string was passed + @param src: the address of the object or a string + @return: Returns a tuple of boolean and object or error number (Bool, Error | Object). + """ + + # Nothing passed? Take the address and unpack from the database + if src is None: + src = self.ea + + if type(src) == types.StringType: + return _idaapi.unpack_object_from_bv(_idaapi.cvar.idati, self.type, self.fields, src, flags) + else: + return _idaapi.unpack_object_from_idb(_idaapi.cvar.idati, self.type, self.fields, src, flags) + + def store(self, obj, dest_ea=None, base_ea=0, flags=0): + """ + Packs an object into a given ea if provided or into a string if no address was passed. + @param obj: The object to pack + @param dest_ea: If packing to idb this will be the store location + @param base_ea: If packing to a buffer, this will be the base that will be used to relocate the pointers + + @return: + - If packing to a string then a Tuple(Boolean, packed_string or error code) + - If packing to the database then a return code is returned (0 is success) + """ + + # no ea passed? thus pack to a string + if dest_ea is None: + return _idaapi.pack_object_to_bv(obj, + _idaapi.cvar.idati, + self.type, + self.fields, + base_ea, + flags) + else: + return _idaapi.pack_object_to_idb(obj, + _idaapi.cvar.idati, + self.type, + self.fields, + dest_ea, + flags) + +# ----------------------------------------------------------------------- +class Appcall_consts__(object): + """Helper class used by Appcall.Consts attribute + It is used to retrieve constants via attribute access""" + def __init__(self, default=0): + self.__default = default + + def __getattr__(self, attr): + return Appcall__.valueof(attr, self.__default) + +# ----------------------------------------------------------------------- +class Appcall__(object): + APPCALL_MANUAL = 0x1 + """ + Only set up the appcall, do not run it. + you should call CleanupAppcall() when finished + """ + + APPCALL_DEBEV = 0x2 + """ + Return debug event information + If this bit is set, exceptions during appcall + will generate idc exceptions with full + information about the exception + """ + + APPCALL_TIMEOUT = 0x4 + """ + Appcall with timeout + The timeout value in milliseconds is specified + in the high 2 bytes of the 'options' argument: + If timed out, errbuf will contain "timeout". + """ + + def __init__(self): + self.__consts = Appcall_consts__() + def __get_consts(self): + return self.__consts + Consts = property(__get_consts) + """Use Appcall.Consts.CONST_NAME to access constants""" + + @staticmethod + def __name_or_ea(name_or_ea): + """ + Function that accepts a name or an ea and checks if the address is enabled. + If a name is passed then idaapi.get_name_ea() is applied to retrieve the name + @return: + - Returns the resolved EA or + - Raises an exception if the address is not enabled + """ + + # a string? try to resolve it + if type(name_or_ea) == types.StringType: + ea = _idaapi.get_name_ea(_idaapi.BADADDR, name_or_ea) + else: + ea = name_or_ea + # could not resolve name or invalid address? + if ea == _idaapi.BADADDR or not _idaapi.isEnabled(ea): + raise ValueError, "Undefined function " + name_or_ea + return ea + + @staticmethod + def proto(name_or_ea, prototype, flags = None): + """ + Allows you to instantiate an appcall (callable object) with the desired prototype + @param name_or_ea: The name of the function (will be resolved with LocByName()) + @param prototype: + @return: + - On failure it raises an exception if the prototype could not be parsed + or the address is not resolvable + - Returns a callbable Appcall instance with the given prototypes and flags + """ + + # resolve and raise exception on error + ea = Appcall__.__name_or_ea(name_or_ea) + # parse the type + if flags is None: + flags = 1 | 2 | 4 # PT_SIL | PT_NDC | PT_TYP + + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, prototype, flags) + if result is None: + raise ValueError, "Could not parse type: " + prototype + + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + def __getattr__(self, name_or_ea): + """Allows you to call functions as if they were member functions (by returning a callable object)""" + # resolve and raise exception on error + ea = self.__name_or_ea(name_or_ea) + if ea == _idaapi.BADADDR: + raise ValueError, "Undefined function " + name + # Return the callable method + return Appcall_callable__(ea) + + def __getitem__(self, idx): + """ + Use self[func_name] syntax if the function name contains invalid characters for an attribute name + See __getattr___ + """ + return self.__getattr__(idx) + + @staticmethod + def valueof(name, default=0): + """ + Returns the numeric value of a given name string. + If the name could not be resolved then the default value will be returned + """ + t, v = _idaapi.get_name_value(_idaapi.BADADDR, name) + if t == 0: # NT_NONE + v = default + return v + + @staticmethod + def int64(v): + """Whenever a 64bit number is needed use this method to construct an object""" + return PyIdc_cvt_int64__(v) + + @staticmethod + def byref(val): + """ + Method to create references to immutable objects + Currently we support references to int/strings + Objects need not be passed by reference (this will be done automatically) + """ + return PyIdc_cvt_refclass__(val) + + @staticmethod + def buffer(str = None, size = 0, fill="\x00"): + """ + Creates a string buffer. The returned value (r) will be a byref object. + Use r.value to get the contents and r.size to get the buffer's size + """ + if str is None: + str = "" + left = size - len(str) + if left > 0: + str = str + (fill * left) + r = Appcall__.byref(str) + r.size = size + return r + + @staticmethod + def obj(**kwds): + """Returns an empty object or objects with attributes as passed via its keywords arguments""" + return object_t(**kwds) + + @staticmethod + def cstr(val): + return as_cstr(val) + + @staticmethod + def unicode(s): + return as_unicode(s) + + @staticmethod + def array(type_name): + """Defines an array type. Later you need to pack() / unpack()""" + return Appcall_array__(type_name) + + @staticmethod + def typedobj(typestr, ea=None): + """ + Parses a type string and returns an appcall object. + One can then use retrieve() member method + @param ea: Optional parameter that later can be used to retrieve the type + @return: Appcall object or raises ValueError exception + """ + # parse the type + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, typestr, 1 | 2 | 4) # PT_SIL | PT_NDC | PT_TYP + if result is None: + raise ValueError, "Could not parse type: " + typestr + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + @staticmethod + def set_appcall_options(opt): + """Method to change the Appcall options globally (not per Appcall)""" + old_opt = Appcall__.get_appcall_options() + _idaapi.cvar.inf.appcall_options = opt + return old_opt + + @staticmethod + def get_appcall_options(): + """Return the global Appcall options""" + return _idaapi.cvar.inf.appcall_options + + @staticmethod + def cleanup_appcall(tid = 0): + """Equivalent to IDC's CleanupAppcall()""" + return _idaapi.cleanup_appcall(tid) + +Appcall = Appcall__() +# +%} diff --git a/swig/idp.i b/swig/idp.i index cde762a7..15a2f819 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -1,1668 +1,1668 @@ -// Ignore the following symbols -%ignore WorkReg; -%ignore AbstractRegister; -%ignore rginfo; -%ignore insn_t::get_canon_mnem; -%ignore insn_t::get_canon_feature; -%ignore insn_t::is_canon_insn; -%ignore bytes_t; -%ignore IDPOPT_STR; -%ignore IDPOPT_NUM; -%ignore IDPOPT_BIT; -%ignore IDPOPT_FLT; -%ignore IDPOPT_I64; -%ignore IDPOPT_OK; -%ignore IDPOPT_BADKEY; -%ignore IDPOPT_BADTYPE; -%ignore IDPOPT_BADVALUE; -%ignore set_options_t; -%ignore read_user_config_file; - -%ignore s_preline; -%ignore ca_operation_t; -%ignore _chkarg_cmd; -%ignore ENUM_SIZE; - -%ignore asm_t::checkarg_dispatch; -%ignore asm_t::func_header; -%ignore asm_t::func_footer; -%ignore asm_t::get_type_name; -%ignore instruc_t; -%ignore processor_t; -%ignore ph; -%ignore IDB_Callback; -%ignore IDP_Callback; -%ignore _py_getreg; -%ignore free_processor_module; -%ignore read_config_file; -%ignore cfg_compiler_changed; - -%ignore gen_idb_event; - -%include "idp.hpp" -%feature("director") IDB_Hooks; -%feature("director") IDP_Hooks; - -%extend areacb_t { - areacb_type_t get_type() - { - areacb_type_t t = AREACB_TYPE_UNKNOWN; - if ( $self == &funcs ) - t = AREACB_TYPE_FUNC; - else if ( $self == &segs ) - t = AREACB_TYPE_SEGMENT; - else if ( $self == &hidden_areas ) - t = AREACB_TYPE_HIDDEN_AREA; - return t; - } -} - -%inline %{ - -// -//------------------------------------------------------------------------- - -//------------------------------------------------------------------------- -/* -# -def AssembleLine(ea, cs, ip, use32, line): - """ - Assemble an instruction to a string (display a warning if an error is found) - - @param ea: linear address of instruction - @param cs: cs of instruction - @param ip: ip of instruction - @param use32: is 32bit segment - @param line: line to assemble - @return: - - None on failure - - or a string containing the assembled instruction - """ - pass -# -*/ -static PyObject *AssembleLine( - ea_t ea, - ea_t cs, - ea_t ip, - bool use32, - const char *line) -{ - int inslen; - char buf[MAXSTR]; - bool ok = false; - if (ph.notify != NULL && - (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) - { - ok = true; - } - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return PyString_FromStringAndSize(buf, inslen); - else - Py_RETURN_NONE; -} - -//--------------------------------------------------------------------------- -/* -# -def assemble(ea, cs, ip, use32, line): - """ - Assemble an instruction into the database (display a warning if an error is found) - @param ea: linear address of instruction - @param cs: cs of instruction - @param ip: ip of instruction - @param use32: is 32bit segment? - @param line: line to assemble - - @return: Boolean. True on success. - """ -# -*/ -bool assemble( - ea_t ea, - ea_t cs, - ea_t ip, - bool use32, - const char *line) -{ - int inslen; - char buf[MAXSTR]; - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool rc = false; - Py_BEGIN_ALLOW_THREADS; - if (ph.notify != NULL) - { - inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); - if (inslen > 0) - { - patch_many_bytes(ea, buf, inslen); - rc = true; - } - } - Py_END_ALLOW_THREADS; - return rc; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_id(): - """ - Returns the 'ph.id' field - """ - pass -# -*/ -static size_t ph_get_id() -{ - return ph.id; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_version(): - """ - Returns the 'ph.version' - """ - pass -# -*/ -static size_t ph_get_version() -{ - return ph.version; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_flag(): - """ - Returns the 'ph.flag' - """ - pass -# -*/ -static size_t ph_get_flag() -{ - return ph.flag; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_cnbits(): - """ - Returns the 'ph.cnbits' - """ - pass -# -*/ -static size_t ph_get_cnbits() -{ - return ph.cnbits; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_dnbits(): - """ - Returns the 'ph.dnbits' - """ - pass -# -*/ -static size_t ph_get_dnbits() -{ - return ph.dnbits; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_regFirstSreg(): - """ - Returns the 'ph.regFirstSreg' - """ - pass -# -*/ -static size_t ph_get_regFirstSreg() -{ - return ph.regFirstSreg; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_regLastSreg(): - """ - Returns the 'ph.regLastSreg' - """ - pass -# -*/ -static size_t ph_get_regLastSreg() -{ - return ph.regLastSreg; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_segreg_size(): - """ - Returns the 'ph.segreg_size' - """ - pass -# -*/ -static size_t ph_get_segreg_size() -{ - return ph.segreg_size; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_regCodeSreg(): - """ - Returns the 'ph.regCodeSreg' - """ - pass -# -*/ -static size_t ph_get_regCodeSreg() -{ - return ph.regCodeSreg; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_regDataSreg(): - """ - Returns the 'ph.regDataSreg' - """ - pass -# -*/ -static size_t ph_get_regDataSreg() -{ - return ph.regDataSreg; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_high_fixup_bits(): - """ - Returns the 'ph.high_fixup_bits' - """ - pass -# -*/ -static size_t ph_get_high_fixup_bits() -{ - return ph.high_fixup_bits; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_icode_return(): - """ - Returns the 'ph.icode_return' - """ - pass -# -*/ -static size_t ph_get_icode_return() -{ - return ph.icode_return; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_instruc_start(): - """ - Returns the 'ph.instruc_start' - """ - pass -# -*/ -static size_t ph_get_instruc_start() -{ - return ph.instruc_start; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_instruc_end(): - """ - Returns the 'ph.instruc_end' - """ - pass -# -*/ -static size_t ph_get_instruc_end() -{ - return ph.instruc_end; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_tbyte_size(): - """ - Returns the 'ph.tbyte_size' field as defined in he processor module - """ - pass -# -*/ -static size_t ph_get_tbyte_size() -{ - return ph.tbyte_size; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_instruc(): - """ - Returns a list of tuples (instruction_name, instruction_feature) containing the - instructions list as defined in he processor module - """ - pass -# -*/ -static PyObject *ph_get_instruc() -{ - Py_ssize_t i = 0; - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyObject *py_result = PyTuple_New(ph.instruc_end - ph.instruc_start); - for ( const instruc_t *p = ph.instruc + ph.instruc_start, *end = ph.instruc + ph.instruc_end; - p != end; - ++p ) - { - PyTuple_SetItem(py_result, i++, Py_BuildValue("(sI)", p->name, p->feature)); - } - return py_result; -} - -//------------------------------------------------------------------------- -/* -# -def ph_get_regnames(): - """ - Returns the list of register names as defined in the processor module - """ - pass -# -*/ -static PyObject *ph_get_regnames() -{ - Py_ssize_t i = 0; - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyObject *py_result = PyList_New(ph.regsNum); - for ( Py_ssize_t i=0; i -def ph_get_operand_info(): - """ - Returns the operand information given an ea and operand number. - - @param ea: address - @param n: operand number - - @return: Returns an idd_opinfo_t as a tuple: (modified, ea, reg_ival, regidx, value_size). - Please refer to idd_opinfo_t structure in the SDK. - """ - pass -# -*/ -static PyObject *ph_get_operand_info( - ea_t ea, - int n) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool ok = false; - idd_opinfo_t opinf; - Py_BEGIN_ALLOW_THREADS; - do - { - if ( dbg == NULL || n == - 1 ) - break; - - // Allocate register space - thid_t tid = get_current_thread(); - regvals_t regvalues; - regvalues.resize(dbg->registers_size); - // Read registers - if ( get_reg_vals(tid, -1, regvalues.begin()) != 1 ) - break; - - // Call the processor module - if ( ph.notify(ph.get_operand_info, - ea, - n, - tid, - _py_getreg, - regvalues.begin(), - &opinf) != 0 ) - { - break; - } - ok = true; - } while (false); - - Py_END_ALLOW_THREADS; - if ( ok ) - return Py_BuildValue("(i" PY_FMT64 "Kii)", - opinf.modified, - opinf.ea, - opinf.value.ival, - opinf.debregidx, - opinf.value_size); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -/* -# -class IDP_Hooks(object): - def hook(self): - """ - Creates an IDP hook - - @return: Boolean true on success - """ - pass - - - def unhook(self): - """ - Removes the IDP hook - @return: Boolean true on success - """ - pass - - - def custom_ana(self): - """ - Analyzes and decodes an instruction at idaapi.cmd.ea - - cmd.itype must be set >= idaapi.CUSTOM_CMD_ITYPE - - cmd.size must be set to the instruction length - - @return: Boolean - - False if the instruction is not recognized - - True if the instruction was decoded. idaapi.cmd should be filled in that case. - """ - pass - - - def custom_out(self): - """ - Outputs the instruction defined in idaapi.cmd - - @return: Boolean (whether this instruction can be outputted or not) - """ - pass - - - def custom_emu(self): - """ - Emulate instruction, create cross-references, plan to analyze - subsequent instructions, modify flags etc. Upon entrance to this function - all information about the instruction is in 'cmd' structure. - - @return: Boolean (whether this instruction has been emulated or not) - """ - pass - - - def custom_outop(self, op): - """ - Notification to generate operand text. - If False was returned, then the standard operand output function will be called. - - The output buffer is inited with init_output_buffer() - and this notification may use out_...() functions to form the operand text - - @return: Boolean (whether the operand has been outputted or not) - """ - pass - - def custom_mnem(self): - """ - Prints the mnemonic of the instruction defined in idaapi.cmd - - @return: - - None: No mnemonic. IDA will use the default mnemonic value if present - - String: The desired mnemonic string - """ - pass - - - def is_sane_insn(self, no_crefs): - """ - is the instruction sane for the current file type? - @param no_crefs: - - 1: the instruction has no code refs to it. - ida just tries to convert unexplored bytes - to an instruction (but there is no other - reason to convert them into an instruction) - - 0: the instruction is created because - of some coderef, user request or another - weighty reason. - @return: 1-ok, <=0-no, the instruction isn't likely to appear in the program - """ - pass - - - def may_be_func(self, no_crefs): - """ - Can a function start here? - @param state: autoanalysis phase - 0: creating functions - 1: creating chunks - - @return: integer (probability 0..100) - """ - pass - - - def closebase(self): - """ - The database will be closed now - """ - pass - - - def savebase(self): - """ - The database is being saved. Processor module should - """ - pass - - - def rename(self, ea, new_name): - """ - The kernel is going to rename a byte. - - @param ea: Address - @param new_name: The new name - - @return: - - If returns value <=0, then the kernel should - not rename it. See also the 'renamed' event - """ - pass - - - def renamed(self, ea, new_name, local_name): - """ - The kernel has renamed a byte - - @param ea: Address - @param new_name: The new name - @param local_name: Is local name - - @return: Ignored - """ - pass - - - def undefine(self, ea): - """ - An item in the database (insn or data) is being deleted - @param ea: Address - @return: - - returns: >0-ok, <=0-the kernel should stop - - if the return value is positive: - bit0 - ignored - bit1 - do not delete srareas at the item end - """ - pass - - - def make_code(self, ea, size): - """ - An instruction is being created - @param ea: Address - @param size: Instruction size - @return: 1-ok, <=0-the kernel should stop - """ - pass - - - def make_code(self, ea, size): - """ - An instruction is being created - @param ea: Address - @param size: Instruction size - @return: 1-ok, <=0-the kernel should stop - """ - pass - - - def make_data(self, ea, flags, tid, len): - """ - A data item is being created - @param ea: Address - @param tid: type id - @param flags: item flags - @param len: data item size - @return: 1-ok, <=0-the kernel should stop - """ - pass - - - def load_idasgn(self, short_sig_name): - """ - FLIRT signature have been loaded for normal processing - (not for recognition of startup sequences) - @param short_sig_name: signature name - @return: Ignored - """ - pass - - - def add_func(self, func): - """ - The kernel has added a function - @param func: the func_t instance - @return: Ignored - """ - pass - - - def del_func(self, func): - """ - The kernel is about to delete a function - @param func: the func_t instance - @return: 1-ok,<=0-do not delete - """ - pass - - - def is_call_insn(self, ea, func_name): - """ - Is the instruction a "call"? - - @param ea: instruction address - @return: 1-unknown, 0-no, 2-yes - """ - pass - - - def is_ret_insn(self, ea, func_name): - """ - Is the instruction a "return"? - - @param ea: instruction address - @param strict: - True: report only ret instructions - False: include instructions like "leave" which begins the function epilog - @return: 1-unknown, 0-no, 2-yes - """ - pass - - - def assemble(self, ea, cs, ip, use32, line): - """ - Assembles an instruction - - @param ea: linear address of instruction - @param cs: cs of instruction - @param ip: ip of instruction - @param use32: is 32bit segment? - @param line: line to assemble - - @return: - None to let the underlying processor module assemble the line - - or a string containing the assembled buffer - """ - pass - -# -*/ -//--------------------------------------------------------------------------- -// IDP hooks -//--------------------------------------------------------------------------- -int idaapi IDP_Callback(void *ud, int notification_code, va_list va); -class IDP_Hooks -{ -public: - virtual ~IDP_Hooks() - { - unhook(); - } - - bool hook() - { - return hook_to_notification_point(HT_IDP, IDP_Callback, this); - } - - bool unhook() - { - return unhook_from_notification_point(HT_IDP, IDP_Callback, this); - } - - virtual bool custom_ana() - { - return false; - } - - virtual bool custom_out() - { - return false; - } - - virtual bool custom_emu() - { - return false; - } - - virtual bool custom_outop(PyObject *py_op) - { - return false; - } - - virtual PyObject *custom_mnem() - { - Py_RETURN_NONE; - } - - virtual int is_sane_insn(int no_crefs) - { - return 0; - } - - virtual int may_be_func(int state) - { - return 0; - } - - virtual int closebase() - { - return 0; - } - - virtual void savebase() - { - } - - virtual void auto_empty_finally() - { - } - - virtual int rename(ea_t ea, const char *new_name) - { - return 0; - } - - virtual void renamed(ea_t ea, const char *new_name, bool local_name) - { - } - - virtual int undefine(ea_t ea) - { - return 0; - } - - virtual int make_code(ea_t ea, asize_t size) - { - return 0; - } - - virtual int make_data(ea_t ea, flags_t flags, tid_t tid, asize_t len) - { - return 0; - } - - virtual void load_idasgn(const char *short_sig_name) - { - } - - virtual void auto_empty() - { - } - - virtual int auto_queue_empty(atype_t type) - { - return 1; // Keep the queue empty. - } - - virtual void add_func(func_t *func) - { - } - - virtual int del_func(func_t *func) - { - return 0; - } - - virtual int is_call_insn(ea_t /*ea*/) - { - return 0; - } - - virtual int is_ret_insn(ea_t /*ea*/, bool /*strict*/) - { - return 0; - } - - virtual PyObject *assemble( - ea_t /*ea*/, - ea_t /*cs*/, - ea_t /*ip*/, - bool /*use32*/, - const char * /*line*/) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } -}; - -enum areacb_type_t -{ - AREACB_TYPE_UNKNOWN, - AREACB_TYPE_FUNC, - AREACB_TYPE_SEGMENT, - AREACB_TYPE_HIDDEN_AREA, - AREACB_TYPE_SRAREA, -}; - -//--------------------------------------------------------------------------- -// IDB hooks -//--------------------------------------------------------------------------- -int idaapi IDB_Callback(void *ud, int notification_code, va_list va); -class IDB_Hooks -{ -public: - virtual ~IDB_Hooks() { unhook(); }; - - bool hook() - { - return hook_to_notification_point(HT_IDB, IDB_Callback, this); - } - bool unhook() - { - return unhook_from_notification_point(HT_IDB, IDB_Callback, this); - } - // Hook functions to override in Python - virtual int byte_patched(ea_t /*ea*/) { return 0; } - virtual int cmt_changed(ea_t, bool /*repeatable_cmt*/) { return 0; } - virtual int area_cmt_changed(areacb_t * /*areas*/, area_t * /*area*/, const char * /*cmt*/, bool /*repeatable*/) { return 0; } - virtual int ti_changed(ea_t /*ea*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; } - virtual int op_ti_changed(ea_t /*ea*/, int /*n*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; } - virtual int op_type_changed(ea_t /*ea*/, int /*n*/) { return 0; } - virtual int enum_created(enum_t /*id*/) { return 0; } - virtual int enum_deleted(enum_t /*id*/) { return 0; } - virtual int enum_bf_changed(enum_t /*id*/) { return 0; } - virtual int enum_renamed(enum_t /*id*/) { return 0; } - virtual int enum_cmt_changed(enum_t /*id*/) { return 0; } - virtual int enum_member_created(enum_t /*id*/, const_t cid) { return 0; } - virtual int enum_member_deleted(enum_t /*id*/, const_t cid) { return 0; } - virtual int struc_created(tid_t /*struc_id*/) { return 0; } - virtual int struc_deleted(tid_t /*struc_id*/) { return 0; } - virtual int struc_renamed(struc_t * /*sptr*/) { return 0; } - virtual int struc_expanded(struc_t * /*sptr*/) { return 0; } - virtual int struc_cmt_changed(tid_t /*struc_id*/) { return 0; } - virtual int struc_member_created(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; } - virtual int struc_member_deleted(struc_t * /*sptr*/, tid_t /*member_id*/, ea_t /*offset*/) { return 0; } - virtual int struc_member_renamed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; } - virtual int struc_member_changed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; } - virtual int thunk_func_created(func_t * /*pfn*/) { return 0; } - virtual int func_tail_appended(func_t * /*pfn*/, func_t * /*tail*/) { return 0; } - virtual int func_tail_removed(func_t * /*pfn*/, ea_t /*tail_ea*/) { return 0; } - virtual int tail_owner_changed(func_t * /*tail*/, ea_t /*owner_func*/) { return 0; } - virtual int func_noret_changed(func_t * /*pfn*/) { return 0; } - virtual int segm_added(segment_t * /*s*/) { return 0; } - virtual int segm_deleted(ea_t /*startEA*/) { return 0; } - virtual int segm_start_changed(segment_t * /*s*/) { return 0; } - virtual int segm_end_changed(segment_t * /*s*/) { return 0; } - virtual int segm_moved(ea_t /*from*/, ea_t /*to*/, asize_t /*size*/) { return 0; } -}; - -// -%} - -%{ -// -//------------------------------------------------------------------------- -int idaapi IDP_Callback(void *ud, int notification_code, va_list va) -{ - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - IDP_Hooks *proxy = (IDP_Hooks *)ud; - int ret = 0; - try - { - switch ( notification_code ) - { - case processor_t::custom_ana: - ret = proxy->custom_ana() ? 1 + cmd.size : 0; - break; - - case processor_t::custom_out: - ret = proxy->custom_out() ? 2 : 0; - break; - - case processor_t::custom_emu: - ret = proxy->custom_emu() ? 2 : 0; - break; - - case processor_t::custom_outop: - { - op_t *op = va_arg(va, op_t *); - ref_t py_obj(create_idaapi_linked_class_instance(S_PY_OP_T_CLSNAME, op)); - if ( py_obj == NULL ) - break; - ret = proxy->custom_outop(py_obj.o) ? 2 : 0; - break; - } - - case processor_t::custom_mnem: - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyObject *py_ret = proxy->custom_mnem(); - if ( py_ret != NULL && PyString_Check(py_ret) ) - { - char *outbuffer = va_arg(va, char *); - size_t bufsize = va_arg(va, size_t); - - qstrncpy(outbuffer, PyString_AS_STRING(py_ret), bufsize); - ret = 2; - } - else - { - ret = 0; - } - Py_XDECREF(py_ret); - break; - } - - case processor_t::is_sane_insn: - { - int no_crefs = va_arg(va, int); - ret = proxy->is_sane_insn(no_crefs); - break; - } - - case processor_t::may_be_func: - { - int state = va_arg(va, int); - ret = proxy->may_be_func(state); - break; - } - - case processor_t::closebase: - { - proxy->closebase(); - break; - } - - case processor_t::savebase: - { - proxy->savebase(); - break; - } - - case processor_t::auto_empty_finally: - { - proxy->auto_empty_finally(); - break; - } - - case processor_t::rename: - { - ea_t ea = va_arg(va, ea_t); - const char *new_name = va_arg(va, const char *); - ret = proxy->rename(ea, new_name); - break; - } - - case processor_t::renamed: - { - ea_t ea = va_arg(va, ea_t); - const char *new_name = va_arg(va, const char *); - bool local_name = va_argi(va, bool); - proxy->renamed(ea, new_name, local_name); - break; - } - - case processor_t::undefine: - { - ea_t ea = va_arg(va, ea_t); - ret = proxy->undefine(ea); - break; - } - - case processor_t::make_code: - { - ea_t ea = va_arg(va, ea_t); - asize_t size = va_arg(va, asize_t); - ret = proxy->make_code(ea, size); - break; - } - - case processor_t::make_data: - { - ea_t ea = va_arg(va, ea_t); - flags_t flags = va_arg(va, flags_t); - tid_t tid = va_arg(va, tid_t); - asize_t len = va_arg(va, asize_t); - ret = proxy->make_data(ea, flags, tid, len); - break; - } - - case processor_t::load_idasgn: - { - const char *short_sig_name = va_arg(va, const char *); - proxy->load_idasgn(short_sig_name); - break; - } - - case processor_t::auto_empty: - { - proxy->auto_empty(); - break; - } - - case processor_t::auto_queue_empty: - { - atype_t type = va_arg(va, atype_t); - ret = proxy->auto_queue_empty(type); - break; - } - - case processor_t::add_func: - { - func_t *func = va_arg(va, func_t *); - proxy->add_func(func); - break; - } - - case processor_t::del_func: - { - func_t *func = va_arg(va, func_t *); - ret = proxy->del_func(func); - break; - } - - case processor_t::is_call_insn: - { - ea_t ea = va_arg(va, ea_t); - ret = proxy->is_call_insn(ea); - break; - } - - case processor_t::is_ret_insn: - { - ea_t ea = va_arg(va, ea_t); - bool strict = va_argi(va, bool); - ret = proxy->is_ret_insn(ea, strict); - break; - } - - case processor_t::assemble: - { - ea_t ea = va_arg(va, ea_t); - ea_t cs = va_arg(va, ea_t); - ea_t ip = va_arg(va, ea_t); - bool use32 = va_argi(va, bool); - const char *line = va_arg(va, const char *); - // Extract user buffer (we hardcode the MAXSTR size limit) - uchar *bin = va_arg(va, uchar *); - // Call python - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyObject *py_buffer = proxy->assemble(ea, cs, ip, use32, line); - if ( py_buffer != NULL && PyString_Check(py_buffer) ) - { - char *s; - Py_ssize_t len; - if ( PyString_AsStringAndSize(py_buffer, &s, &len) != -1 ) - { - if ( len > MAXSTR ) - len = MAXSTR; - memcpy(bin, s, len); - ret = len; - } - } - // ret = 0 otherwise - Py_XDECREF(py_buffer); - break; - } - // validate_flirt_func, // flirt has recognized a library function - // // this callback can be used by a plugin or proc module - // // to intercept it and validate such a function - // // args: ea_t start_ea - // // const char *funcname - // // returns: -1-do not create a function, - // // 1-function is validated - // // the idp module is allowed to modify 'cmd' - // set_func_start, // Function chunk start address will be changed - // // args: func_t *pfn - // // ea_t new_start - // // Returns: 1-ok,<=0-do not change - // set_func_end, // Function chunk end address will be changed - // // args: func_t *pfn - // // ea_t new_end - // // Returns: 1-ok,<=0-do not change - // outlabel, // The kernel is going to generate an instruction - // // label line or a function header - // // args: - // // ea_t ea - - // // const char *colored_name - - // // If returns value <=0, then the kernel should - // // not generate the label - // may_show_sreg, // The kernel wants to display the segment registers - // // in the messages window. - // // arg - ea_t current_ea - // // if this function returns 0 - // // then the kernel will not show - // // the segment registers. - // // (assuming that the module have done it) - // coagulate, // Try to define some unexplored bytes - // // This notification will be called if the - // // kernel tried all possibilities and could - // // not find anything more useful than to - // // convert to array of bytes. - // // The module can help the kernel and convert - // // the bytes into something more useful. - // // arg: - // // ea_t start_ea - // // returns: number of converted bytes + 1 - // auto_empty, // Info: all analysis queues are empty - // // args: none - // // returns: none - // // This callback is called once when the - // // initial analysis is finished. If the queue is - // // not empty upon the return from this callback, - // // it will be called later again. - // // See also auto_empty_finally. - // auto_queue_empty, // One analysis queue is empty - // // args: atype_t type - // // returns: 1-yes, keep the queue empty - // // <=0-no, the queue is not empty anymore - // // This callback can be called many times, so - // // only the autoMark() functions can be used from it - // // (other functions may work but it is not tested) - // func_bounds, // find_func_bounds() finished its work - // // The module may fine tune the function bounds - // // args: int *possible_return_code - // // func_t *pfn - // // ea_t max_func_end_ea (from the kernel's point of view) - // // returns: none - // is_jump_func, // is the function a trivial "jump" function? - // // args: func_t *pfn - // // ea_t *jump_target - // // ea_t *func_pointer - // // returns: 0-no, 1-don't know, 2-yes, see jump_target - // // and func_pointer - // gen_regvar_def, // generate register variable definition line - // // args: regvar_t *v - // // returns: 0-ok - // setsgr, // The kernel has changed a segment register value - // // args: ea_t startEA - // // ea_t endEA - // // int regnum - // // sel_t value - // // sel_t old_value - // // uchar tag (SR_... values) - // // returns: 1-ok, 0-error - // set_compiler, // The kernel has changed the compiler information - // // (inf.cc structure) - // is_basic_block_end, // Is the current instruction end of a basic block? - // // This function should be defined for processors - // // with delayed jump slots. The current instruction - // // is stored in 'cmd' - // // args: bool call_insn_stops_block - // // returns: 1-unknown, 0-no, 2-yes - // reglink, // IBM PC only, ignore it - // get_vxd_name, // IBM PC only, ignore it - // // Get Vxd function name - // // args: int vxdnum - // // int funcnum - // // char *outbuf - // // returns: nothing - // - // - // moving_segm, // May the kernel move the segment? - // // args: segment_t - segment to move - // // ea_t to - new segment start address - // // returns: 1-yes, <=0-the kernel should stop - // move_segm, // A segment is moved - // // Fix processor dependent address sensitive information - // // args: ea_t from - old segment address - // // segment_t* - moved segment - // // returns: nothing - // - // - // get_stkvar_scale_factor,// Should stack variable references be multiplied by - // // a coefficient before being used in the stack frame? - // // Currently used by TMS320C55 because the references into - // // the stack should be multiplied by 2 - // // Returns: scaling factor - // // Note: PR_SCALE_STKVARS should be set to use this callback - // - // create_flat_group, // Create special segment representing the flat group - // // (to use for PC mainly) - // // args - ea_t image_base, int bitness, sel_t dataseg_sel - // - // kernel_config_loaded, // This callback is called when ida.cfg is parsed - // // args - none, returns - nothing - // - // might_change_sp, // Does the instruction at 'ea' modify the stack pointer? - // // args: ea_t ea - // // returns: 1-yes, 0-false - // // (not used yet) - // - // is_alloca_probe, // Does the function at 'ea' behave as __alloca_probe? - // // args: ea_t ea - // // returns: 2-yes, 1-false - // - // out_3byte, // Generate text representation of 3byte data - // // init_out_buffer() is called before this function - // // and all Out... function can be used. - // // uFlag contains the flags. - // // This callback might be implemented by the processor - // // module to generate custom representation of 3byte data. - // // args: - // // ea_t dataea - address of the data item - // // uint32 value - value to output - // // bool analyze_only - only create xrefs if necessary - // // do not generate text representation - // // returns: 2-yes, 1-false - // - // get_reg_name, // Generate text representation of a register - // // int reg - internal register number as defined in the processor module - // // size_t width - register width in bytes - // // char *buf - output buffer - // // size_t bufsize - size of output buffer - // // int reghi - if not -1 then this function will return the register pair - // // returns: -1 if error, strlen(buf)+2 otherwise - // // Most processor modules do not need to implement this callback - // // It is useful only if ph.regNames[reg] does not provide - // // the correct register names - // // save its local data - // out_src_file_lnnum, // Callback: generate analog of - // // #line "file.c" 123 - // // directive. - // // const char *file - source file (may be NULL) - // // size_t lnnum - line number - // // returns: 2-directive has been generated - // get_autocmt, // Callback: get dynamic auto comment - // // Will be called if the autocomments are enabled - // // and the comment retrieved from ida.int starts with - // // '$!'. 'cmd' is contains valid info. - // // char *buf - output buffer - // // size_t bufsize - output buffer size - // // returns: 2-new comment has been generated - // // 1-callback has not been handled - // // the buffer must not be changed in this case - // is_insn_table_jump, // Callback: determine if instruction is a table jump or call - // // If CF_JUMP bit can not describe all kinds of table - // // jumps, please define this callback. - // // It will be called for insns with CF_JUMP bit set. - // // input: cmd structure contains the current instruction - // // returns: 1-yes, 0-no - // auto_empty_finally, // Info: all analysis queues are empty definitively - // // args: none - // // returns: none - // // This callback is called only once. - // // See also auto_empty. - // loader_finished, // Event: external file loader finished its work - // // linput_t *li - // // uint16 neflags - // // const char *filetypename - // // Use this event to augment the existing loader functionality - // loader_elf_machine, // Event: ELF loader machine type checkpoint - // // linput_t *li - // // int machine_type - // // const char **p_procname - // // proc_def **p_pd (see ldr\elf.h) - // // set_elf_reloc_t *set_reloc - // // A plugin check the machine_type. If it is the desired one, - // // the the plugin fills p_procname with the processor name. - // // p_pd is used to handle relocations, otherwise can be left untouched - // // set_reloc can be later used by the plugin to specify relocations - // // returns: e_machine value (if it is different from the - // // original e_machine value, procname and p_pd will be ignored - // // and the new value will be used) - // // This event occurs for each loaded ELF file - // is_indirect_jump, // Callback: determine if instruction is an indrect jump - // // If CF_JUMP bit can not describe all jump types - // // jumps, please define this callback. - // // input: cmd structure contains the current instruction - // // returns: 1-use CF_JUMP, 2-no, 3-yes - // verify_noreturn, // The kernel wants to set 'noreturn' flags for a function - // // func_t *pfn - // // Returns: 1-ok, any other value-do not set 'noreturn' flag - // verify_sp, // All function instructions have been analyzed - // // Now the processor module can analyze the stack pointer - // // for the whole function - // // input: func_t *pfn - // // Returns: 1-ok, 0-bad stack pointer - // treat_hindering_item, // An item hinders creation of another item - // // args: ea_t hindering_item_ea - // // flags_t new_item_flags (0 for code) - // // ea_t new_item_ea - // // asize_t new_item_length - // // Returns: 1-no reaction, <=0-the kernel may delete the hindering item - // str2reg, // Convert a register name to a register number - // // args: const char *regname - // // Returns: register number + 2 - // // The register number is the register index in the regNames array - // // Most processor modules do not need to implement this callback - // // It is useful only if ph.regNames[reg] does not provide - // // the correct register names - // create_switch_xrefs, // Create xrefs for a custom jump table - // // in: ea_t jumpea; - address of the jump insn - // // switch_info_ex_t *; - switch information - // // returns: must return 2 - // calc_switch_cases, // Calculate case values and targets for a custom jump table - // // in: ea_t insn_ea - address of the 'indirect jump' instruction - // // switch_info_ex_t *si - switch information - // // casevec_t *casevec - vector of case values... - // // evec_t *targets - ...and corresponding target addresses - // // casevec and targets may be NULL - // // returns: 2-ok, 1-failed - // determined_main, // The main() function has been determined - // // in: ea_t main - address of the main() function - // // returns: none - // preprocess_chart, // gui has retrieved a function flow chart - // // in: qflow_chart_t *fc - // // returns: none - // // Plugins may modify the flow chart in this callback - // get_bg_color, // Get item background color - // // in: ea_t ea, bgcolor_t *color - // // Returns: 1-not implemented, 2-color set - // // Plugins can hook this callback to color disassembly lines - // // dynamically - // get_operand_string, // Request text string for operand (cli, java, ...) - // // args: int opnum - // // char *buf - // // size_t buflen - // // (cmd structure must contain info for the desired insn) - // // opnum is the operand number; -1 means any string operand - // // returns: 1 - no string (or empty string) - // // >1 - original string length with terminating zero - // - // // the following 5 events are very low level - // // take care of possible recursion - // add_cref, // a code reference is being created - // // args: ea_t from, ea_t to, cref_t type - // // returns: <0 - cancel cref creation - // add_dref, // a data reference is being created - // // args: ea_t from, ea_t to, dref_t type - // // returns: <0 - cancel dref creation - // del_cref, // a code reference is being deleted - // // args: ea_t from, ea_t to, bool expand - // // returns: <0 - cancel cref deletion - // del_dref, // a data reference is being deleted - // // args: ea_t from, ea_t to - // // returns: <0 - cancel dref deletion - // coagulate_dref, // data reference is being analyzed - // // args: ea_t from, ea_t to, bool may_define, ea_t *code_ea - // // plugin may correct code_ea (e.g. for thumb mode refs, we clear the last bit) - // // returns: <0 - cancel dref analysis - // custom_fixup, // mutipurpose notification for FIXUP_CUSTOM - // // args: cust_fix oper, ea_t ea, const fixup_data_t*, ... (see cust_fix) - // // returns: 1 - no accepted (fixup ignored by ida) - // // >1 - accepted (see cust_fix) - // off_preproc, // called from get_offset_expr, when refinfo_t - // // contain flag REFINFO_PREPROC. Normally this - // // notification used in a combination with custom_fixup - // // args: ea_t ea, int numop, ea_t* opval, const refinfo_t* ri, - // // char* buf, size_t bufsize, ea_t* target, - // // ea_t* fullvalue, ea_t from, int getn_flags - // // returns: 2 - buf filled as simple expression - // // 3 - buf filled as complex expression - // // 4 - apply standard processing (with - possible - changed values) - // // others - can't convert to offset expression - // - // set_proc_options, // called if the user specified an option string in the command line: - // // -p: - // // can be used for e.g. setting a processor subtype - // // also called if option string is passed to set_processor_type() - // // and IDC's SetProcessorType() - // // args: const char * options - // // returns: <0 - bad option string - // - } - } - catch (Swig::DirectorException &e) - { - msg("Exception in IDP Hook function: %s\n", e.getMessage()); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyErr_Occurred() ) - PyErr_Print(); - } - return ret; -} - -//--------------------------------------------------------------------------- -int idaapi IDB_Callback(void *ud, int notification_code, va_list va) -{ - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - class IDB_Hooks *proxy = (class IDB_Hooks *)ud; - ea_t ea, ea2; - bool repeatable_cmt; - type_t *type; - p_list *fnames; - int n; - enum_t id; - const_t cid; - tid_t struc_id; - struc_t *sptr; - member_t *mptr; - tid_t member_id; - func_t *pfn; - func_t *tail; - segment_t *seg; - asize_t size; - - try { - switch (notification_code) - { - case idb_event::byte_patched: - ea = va_arg(va, ea_t); - return proxy->byte_patched(ea); - - case idb_event::cmt_changed: - ea = va_arg(va, ea_t); - repeatable_cmt = va_arg(va, int); - return proxy->cmt_changed(ea, repeatable_cmt); - - case idb_event::area_cmt_changed: - { - areacb_t *cb = va_arg(va, areacb_t*); - area_t *area = va_arg(va, area_t*); - const char *cmt = va_arg(va, char*); - repeatable_cmt = va_arg(va, int); - return proxy->area_cmt_changed(cb, area, cmt, repeatable_cmt); - } - - case idb_event::ti_changed: - ea = va_arg(va, ea_t); - type = va_arg(va, type_t *); - fnames = va_arg(va, p_list *); - return proxy->ti_changed(ea, type, fnames); - - case idb_event::op_ti_changed: - ea = va_arg(va, ea_t); - n = va_arg(va, int); - type = va_arg(va, type_t *); - fnames = va_arg(va, p_list *); - return proxy->op_ti_changed(ea, n, type, fnames); - - case idb_event::op_type_changed: - ea = va_arg(va, ea_t); - n = va_arg(va, int); - return proxy->op_type_changed(ea, n); - - case idb_event::enum_created: - id = va_arg(va, enum_t); - return proxy->enum_created(id); - - case idb_event::enum_deleted: - id = va_arg(va, enum_t); - return proxy->enum_deleted(id); - - case idb_event::enum_bf_changed: - id = va_arg(va, enum_t); - return proxy->enum_bf_changed(id); - - case idb_event::enum_cmt_changed: - id = va_arg(va, enum_t); - return proxy->enum_cmt_changed(id); - -#ifdef NO_OBSOLETE_FUNCS - case idb_event::enum_member_created: -#else - case idb_event::enum_const_created: -#endif - id = va_arg(va, enum_t); - cid = va_arg(va, const_t); - return proxy->enum_member_created(id, cid); - -#ifdef NO_OBSOLETE_FUNCS - case idb_event::enum_member_deleted: -#else - case idb_event::enum_const_deleted: -#endif - id = va_arg(va, enum_t); - cid = va_arg(va, const_t); - return proxy->enum_member_deleted(id, cid); - - case idb_event::struc_created: - struc_id = va_arg(va, tid_t); - return proxy->struc_created(struc_id); - - case idb_event::struc_deleted: - struc_id = va_arg(va, tid_t); - return proxy->struc_deleted(struc_id); - - case idb_event::struc_renamed: - sptr = va_arg(va, struc_t *); - return proxy->struc_renamed(sptr); - - case idb_event::struc_expanded: - sptr = va_arg(va, struc_t *); - return proxy->struc_expanded(sptr); - - case idb_event::struc_cmt_changed: - struc_id = va_arg(va, tid_t); - return proxy->struc_cmt_changed(struc_id); - - case idb_event::struc_member_created: - sptr = va_arg(va, struc_t *); - mptr = va_arg(va, member_t *); - return proxy->struc_member_created(sptr, mptr); - - case idb_event::struc_member_deleted: - sptr = va_arg(va, struc_t *); - member_id = va_arg(va, tid_t); - ea = va_arg(va, ea_t); - return proxy->struc_member_deleted(sptr, member_id, ea); - - case idb_event::struc_member_renamed: - sptr = va_arg(va, struc_t *); - mptr = va_arg(va, member_t *); - return proxy->struc_member_renamed(sptr, mptr); - - case idb_event::struc_member_changed: - sptr = va_arg(va, struc_t *); - mptr = va_arg(va, member_t *); - return proxy->struc_member_changed(sptr, mptr); - - case idb_event::thunk_func_created: - pfn = va_arg(va, func_t *); - return proxy->thunk_func_created(pfn); - - case idb_event::func_tail_appended: - pfn = va_arg(va, func_t *); - tail = va_arg(va, func_t *); - return proxy->func_tail_appended(pfn, tail); - - case idb_event::func_tail_removed: - pfn = va_arg(va, func_t *); - ea = va_arg(va, ea_t); - return proxy->func_tail_removed(pfn, ea); - - case idb_event::tail_owner_changed: - tail = va_arg(va, func_t *); - ea = va_arg(va, ea_t); - return proxy->tail_owner_changed(tail, ea); - - case idb_event::func_noret_changed: - pfn = va_arg(va, func_t *); - return proxy->func_noret_changed(pfn); - - case idb_event::segm_added: - seg = va_arg(va, segment_t *); - return proxy->segm_added(seg); - - case idb_event::segm_deleted: - ea = va_arg(va, ea_t); - return proxy->segm_deleted(ea); - - case idb_event::segm_start_changed: - seg = va_arg(va, segment_t *); - return proxy->segm_start_changed(seg); - - case idb_event::segm_end_changed: - seg = va_arg(va, segment_t *); - return proxy->segm_end_changed(seg); - - case idb_event::segm_moved: - ea = va_arg(va, ea_t); - ea2 = va_arg(va, ea_t); - size = va_arg(va, asize_t); - return proxy->segm_moved(ea, ea2, size); - } - } - catch (Swig::DirectorException &e) - { - msg("Exception in IDB Hook function: %s\n", e.getMessage()); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if (PyErr_Occurred()) - PyErr_Print(); - } - return 0; -} - -//------------------------------------------------------------------------- -static const regval_t *idaapi _py_getreg( - const char *name, - const regval_t *regvals) -{ - for ( int i=dbg->registers_size - 1; i >= 0; i-- ) - { - if ( stricmp(name, dbg->registers[i].name) == 0 ) - return ®vals[i]; - } - static regval_t rv; - return &rv; -} - -//------------------------------------------------------------------------- -// -%} \ No newline at end of file +// Ignore the following symbols +%ignore WorkReg; +%ignore AbstractRegister; +%ignore rginfo; +%ignore insn_t::get_canon_mnem; +%ignore insn_t::get_canon_feature; +%ignore insn_t::is_canon_insn; +%ignore bytes_t; +%ignore IDPOPT_STR; +%ignore IDPOPT_NUM; +%ignore IDPOPT_BIT; +%ignore IDPOPT_FLT; +%ignore IDPOPT_I64; +%ignore IDPOPT_OK; +%ignore IDPOPT_BADKEY; +%ignore IDPOPT_BADTYPE; +%ignore IDPOPT_BADVALUE; +%ignore set_options_t; +%ignore read_user_config_file; + +%ignore s_preline; +%ignore ca_operation_t; +%ignore _chkarg_cmd; +%ignore ENUM_SIZE; + +%ignore asm_t::checkarg_dispatch; +%ignore asm_t::func_header; +%ignore asm_t::func_footer; +%ignore asm_t::get_type_name; +%ignore instruc_t; +%ignore processor_t; +%ignore ph; +%ignore IDB_Callback; +%ignore IDP_Callback; +%ignore _py_getreg; +%ignore free_processor_module; +%ignore read_config_file; +%ignore cfg_compiler_changed; + +%ignore gen_idb_event; + +%include "idp.hpp" +%feature("director") IDB_Hooks; +%feature("director") IDP_Hooks; + +%extend areacb_t { + areacb_type_t get_type() + { + areacb_type_t t = AREACB_TYPE_UNKNOWN; + if ( $self == &funcs ) + t = AREACB_TYPE_FUNC; + else if ( $self == &segs ) + t = AREACB_TYPE_SEGMENT; + else if ( $self == &hidden_areas ) + t = AREACB_TYPE_HIDDEN_AREA; + return t; + } +} + +%inline %{ + +// +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +/* +# +def AssembleLine(ea, cs, ip, use32, line): + """ + Assemble an instruction to a string (display a warning if an error is found) + + @param ea: linear address of instruction + @param cs: cs of instruction + @param ip: ip of instruction + @param use32: is 32bit segment + @param line: line to assemble + @return: + - None on failure + - or a string containing the assembled instruction + """ + pass +# +*/ +static PyObject *AssembleLine( + ea_t ea, + ea_t cs, + ea_t ip, + bool use32, + const char *line) +{ + int inslen; + char buf[MAXSTR]; + bool ok = false; + if (ph.notify != NULL && + (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) + { + ok = true; + } + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return PyString_FromStringAndSize(buf, inslen); + else + Py_RETURN_NONE; +} + +//--------------------------------------------------------------------------- +/* +# +def assemble(ea, cs, ip, use32, line): + """ + Assemble an instruction into the database (display a warning if an error is found) + @param ea: linear address of instruction + @param cs: cs of instruction + @param ip: ip of instruction + @param use32: is 32bit segment? + @param line: line to assemble + + @return: Boolean. True on success. + """ +# +*/ +bool assemble( + ea_t ea, + ea_t cs, + ea_t ip, + bool use32, + const char *line) +{ + int inslen; + char buf[MAXSTR]; + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = false; + Py_BEGIN_ALLOW_THREADS; + if (ph.notify != NULL) + { + inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); + if (inslen > 0) + { + patch_many_bytes(ea, buf, inslen); + rc = true; + } + } + Py_END_ALLOW_THREADS; + return rc; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_id(): + """ + Returns the 'ph.id' field + """ + pass +# +*/ +static size_t ph_get_id() +{ + return ph.id; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_version(): + """ + Returns the 'ph.version' + """ + pass +# +*/ +static size_t ph_get_version() +{ + return ph.version; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_flag(): + """ + Returns the 'ph.flag' + """ + pass +# +*/ +static size_t ph_get_flag() +{ + return ph.flag; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_cnbits(): + """ + Returns the 'ph.cnbits' + """ + pass +# +*/ +static size_t ph_get_cnbits() +{ + return ph.cnbits; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_dnbits(): + """ + Returns the 'ph.dnbits' + """ + pass +# +*/ +static size_t ph_get_dnbits() +{ + return ph.dnbits; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regFirstSreg(): + """ + Returns the 'ph.regFirstSreg' + """ + pass +# +*/ +static size_t ph_get_regFirstSreg() +{ + return ph.regFirstSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regLastSreg(): + """ + Returns the 'ph.regLastSreg' + """ + pass +# +*/ +static size_t ph_get_regLastSreg() +{ + return ph.regLastSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_segreg_size(): + """ + Returns the 'ph.segreg_size' + """ + pass +# +*/ +static size_t ph_get_segreg_size() +{ + return ph.segreg_size; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regCodeSreg(): + """ + Returns the 'ph.regCodeSreg' + """ + pass +# +*/ +static size_t ph_get_regCodeSreg() +{ + return ph.regCodeSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regDataSreg(): + """ + Returns the 'ph.regDataSreg' + """ + pass +# +*/ +static size_t ph_get_regDataSreg() +{ + return ph.regDataSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_high_fixup_bits(): + """ + Returns the 'ph.high_fixup_bits' + """ + pass +# +*/ +static size_t ph_get_high_fixup_bits() +{ + return ph.high_fixup_bits; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_icode_return(): + """ + Returns the 'ph.icode_return' + """ + pass +# +*/ +static size_t ph_get_icode_return() +{ + return ph.icode_return; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_instruc_start(): + """ + Returns the 'ph.instruc_start' + """ + pass +# +*/ +static size_t ph_get_instruc_start() +{ + return ph.instruc_start; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_instruc_end(): + """ + Returns the 'ph.instruc_end' + """ + pass +# +*/ +static size_t ph_get_instruc_end() +{ + return ph.instruc_end; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_tbyte_size(): + """ + Returns the 'ph.tbyte_size' field as defined in he processor module + """ + pass +# +*/ +static size_t ph_get_tbyte_size() +{ + return ph.tbyte_size; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_instruc(): + """ + Returns a list of tuples (instruction_name, instruction_feature) containing the + instructions list as defined in he processor module + """ + pass +# +*/ +static PyObject *ph_get_instruc() +{ + Py_ssize_t i = 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_result = PyTuple_New(ph.instruc_end - ph.instruc_start); + for ( const instruc_t *p = ph.instruc + ph.instruc_start, *end = ph.instruc + ph.instruc_end; + p != end; + ++p ) + { + PyTuple_SetItem(py_result, i++, Py_BuildValue("(sI)", p->name, p->feature)); + } + return py_result; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regnames(): + """ + Returns the list of register names as defined in the processor module + """ + pass +# +*/ +static PyObject *ph_get_regnames() +{ + Py_ssize_t i = 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_result = PyList_New(ph.regsNum); + for ( Py_ssize_t i=0; i +def ph_get_operand_info(): + """ + Returns the operand information given an ea and operand number. + + @param ea: address + @param n: operand number + + @return: Returns an idd_opinfo_t as a tuple: (modified, ea, reg_ival, regidx, value_size). + Please refer to idd_opinfo_t structure in the SDK. + """ + pass +# +*/ +static PyObject *ph_get_operand_info( + ea_t ea, + int n) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool ok = false; + idd_opinfo_t opinf; + Py_BEGIN_ALLOW_THREADS; + do + { + if ( dbg == NULL || n == - 1 ) + break; + + // Allocate register space + thid_t tid = get_current_thread(); + regvals_t regvalues; + regvalues.resize(dbg->registers_size); + // Read registers + if ( get_reg_vals(tid, -1, regvalues.begin()) != 1 ) + break; + + // Call the processor module + if ( ph.notify(ph.get_operand_info, + ea, + n, + tid, + _py_getreg, + regvalues.begin(), + &opinf) > 0 ) + { + break; + } + ok = true; + } while (false); + + Py_END_ALLOW_THREADS; + if ( ok ) + return Py_BuildValue("(i" PY_FMT64 "Kii)", + opinf.modified, + opinf.ea, + opinf.value.ival, + opinf.debregidx, + opinf.value_size); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +class IDP_Hooks(object): + def hook(self): + """ + Creates an IDP hook + + @return: Boolean true on success + """ + pass + + + def unhook(self): + """ + Removes the IDP hook + @return: Boolean true on success + """ + pass + + + def custom_ana(self): + """ + Analyzes and decodes an instruction at idaapi.cmd.ea + - cmd.itype must be set >= idaapi.CUSTOM_CMD_ITYPE + - cmd.size must be set to the instruction length + + @return: Boolean + - False if the instruction is not recognized + - True if the instruction was decoded. idaapi.cmd should be filled in that case. + """ + pass + + + def custom_out(self): + """ + Outputs the instruction defined in idaapi.cmd + + @return: Boolean (whether this instruction can be outputted or not) + """ + pass + + + def custom_emu(self): + """ + Emulate instruction, create cross-references, plan to analyze + subsequent instructions, modify flags etc. Upon entrance to this function + all information about the instruction is in 'cmd' structure. + + @return: Boolean (whether this instruction has been emulated or not) + """ + pass + + + def custom_outop(self, op): + """ + Notification to generate operand text. + If False was returned, then the standard operand output function will be called. + + The output buffer is inited with init_output_buffer() + and this notification may use out_...() functions to form the operand text + + @return: Boolean (whether the operand has been outputted or not) + """ + pass + + def custom_mnem(self): + """ + Prints the mnemonic of the instruction defined in idaapi.cmd + + @return: + - None: No mnemonic. IDA will use the default mnemonic value if present + - String: The desired mnemonic string + """ + pass + + + def is_sane_insn(self, no_crefs): + """ + is the instruction sane for the current file type? + @param no_crefs: + - 1: the instruction has no code refs to it. + ida just tries to convert unexplored bytes + to an instruction (but there is no other + reason to convert them into an instruction) + - 0: the instruction is created because + of some coderef, user request or another + weighty reason. + @return: 1-ok, <=0-no, the instruction isn't likely to appear in the program + """ + pass + + + def may_be_func(self, no_crefs): + """ + Can a function start here? + @param state: autoanalysis phase + 0: creating functions + 1: creating chunks + + @return: integer (probability 0..100) + """ + pass + + + def closebase(self): + """ + The database will be closed now + """ + pass + + + def savebase(self): + """ + The database is being saved. Processor module should + """ + pass + + + def rename(self, ea, new_name): + """ + The kernel is going to rename a byte. + + @param ea: Address + @param new_name: The new name + + @return: + - If returns value <=0, then the kernel should + not rename it. See also the 'renamed' event + """ + pass + + + def renamed(self, ea, new_name, local_name): + """ + The kernel has renamed a byte + + @param ea: Address + @param new_name: The new name + @param local_name: Is local name + + @return: Ignored + """ + pass + + + def undefine(self, ea): + """ + An item in the database (insn or data) is being deleted + @param ea: Address + @return: + - returns: >0-ok, <=0-the kernel should stop + - if the return value is positive: + bit0 - ignored + bit1 - do not delete srareas at the item end + """ + pass + + + def make_code(self, ea, size): + """ + An instruction is being created + @param ea: Address + @param size: Instruction size + @return: 1-ok, <=0-the kernel should stop + """ + pass + + + def make_code(self, ea, size): + """ + An instruction is being created + @param ea: Address + @param size: Instruction size + @return: 1-ok, <=0-the kernel should stop + """ + pass + + + def make_data(self, ea, flags, tid, len): + """ + A data item is being created + @param ea: Address + @param tid: type id + @param flags: item flags + @param len: data item size + @return: 1-ok, <=0-the kernel should stop + """ + pass + + + def load_idasgn(self, short_sig_name): + """ + FLIRT signature have been loaded for normal processing + (not for recognition of startup sequences) + @param short_sig_name: signature name + @return: Ignored + """ + pass + + + def add_func(self, func): + """ + The kernel has added a function + @param func: the func_t instance + @return: Ignored + """ + pass + + + def del_func(self, func): + """ + The kernel is about to delete a function + @param func: the func_t instance + @return: 1-ok,<=0-do not delete + """ + pass + + + def is_call_insn(self, ea, func_name): + """ + Is the instruction a "call"? + + @param ea: instruction address + @return: 1-unknown, 0-no, 2-yes + """ + pass + + + def is_ret_insn(self, ea, func_name): + """ + Is the instruction a "return"? + + @param ea: instruction address + @param strict: - True: report only ret instructions + False: include instructions like "leave" which begins the function epilog + @return: 1-unknown, 0-no, 2-yes + """ + pass + + + def assemble(self, ea, cs, ip, use32, line): + """ + Assembles an instruction + + @param ea: linear address of instruction + @param cs: cs of instruction + @param ip: ip of instruction + @param use32: is 32bit segment? + @param line: line to assemble + + @return: - None to let the underlying processor module assemble the line + - or a string containing the assembled buffer + """ + pass + +# +*/ +//--------------------------------------------------------------------------- +// IDP hooks +//--------------------------------------------------------------------------- +int idaapi IDP_Callback(void *ud, int notification_code, va_list va); +class IDP_Hooks +{ +public: + virtual ~IDP_Hooks() + { + unhook(); + } + + bool hook() + { + return hook_to_notification_point(HT_IDP, IDP_Callback, this); + } + + bool unhook() + { + return unhook_from_notification_point(HT_IDP, IDP_Callback, this); + } + + virtual bool custom_ana() + { + return false; + } + + virtual bool custom_out() + { + return false; + } + + virtual bool custom_emu() + { + return false; + } + + virtual bool custom_outop(PyObject *py_op) + { + return false; + } + + virtual PyObject *custom_mnem() + { + Py_RETURN_NONE; + } + + virtual int is_sane_insn(int no_crefs) + { + return 0; + } + + virtual int may_be_func(int state) + { + return 0; + } + + virtual int closebase() + { + return 0; + } + + virtual void savebase() + { + } + + virtual void auto_empty_finally() + { + } + + virtual int rename(ea_t ea, const char *new_name) + { + return 0; + } + + virtual void renamed(ea_t ea, const char *new_name, bool local_name) + { + } + + virtual int undefine(ea_t ea) + { + return 0; + } + + virtual int make_code(ea_t ea, asize_t size) + { + return 0; + } + + virtual int make_data(ea_t ea, flags_t flags, tid_t tid, asize_t len) + { + return 0; + } + + virtual void load_idasgn(const char *short_sig_name) + { + } + + virtual void auto_empty() + { + } + + virtual int auto_queue_empty(atype_t type) + { + return 1; // Keep the queue empty. + } + + virtual void add_func(func_t *func) + { + } + + virtual int del_func(func_t *func) + { + return 0; + } + + virtual int is_call_insn(ea_t /*ea*/) + { + return 0; + } + + virtual int is_ret_insn(ea_t /*ea*/, bool /*strict*/) + { + return 0; + } + + virtual PyObject *assemble( + ea_t /*ea*/, + ea_t /*cs*/, + ea_t /*ip*/, + bool /*use32*/, + const char * /*line*/) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } +}; + +enum areacb_type_t +{ + AREACB_TYPE_UNKNOWN, + AREACB_TYPE_FUNC, + AREACB_TYPE_SEGMENT, + AREACB_TYPE_HIDDEN_AREA, + AREACB_TYPE_SRAREA, +}; + +//--------------------------------------------------------------------------- +// IDB hooks +//--------------------------------------------------------------------------- +int idaapi IDB_Callback(void *ud, int notification_code, va_list va); +class IDB_Hooks +{ +public: + virtual ~IDB_Hooks() { unhook(); }; + + bool hook() + { + return hook_to_notification_point(HT_IDB, IDB_Callback, this); + } + bool unhook() + { + return unhook_from_notification_point(HT_IDB, IDB_Callback, this); + } + // Hook functions to override in Python + virtual int byte_patched(ea_t /*ea*/) { return 0; } + virtual int cmt_changed(ea_t, bool /*repeatable_cmt*/) { return 0; } + virtual int area_cmt_changed(areacb_t * /*areas*/, area_t * /*area*/, const char * /*cmt*/, bool /*repeatable*/) { return 0; } + virtual int ti_changed(ea_t /*ea*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; } + virtual int op_ti_changed(ea_t /*ea*/, int /*n*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; } + virtual int op_type_changed(ea_t /*ea*/, int /*n*/) { return 0; } + virtual int enum_created(enum_t /*id*/) { return 0; } + virtual int enum_deleted(enum_t /*id*/) { return 0; } + virtual int enum_bf_changed(enum_t /*id*/) { return 0; } + virtual int enum_renamed(enum_t /*id*/) { return 0; } + virtual int enum_cmt_changed(enum_t /*id*/) { return 0; } + virtual int enum_member_created(enum_t /*id*/, const_t cid) { return 0; } + virtual int enum_member_deleted(enum_t /*id*/, const_t cid) { return 0; } + virtual int struc_created(tid_t /*struc_id*/) { return 0; } + virtual int struc_deleted(tid_t /*struc_id*/) { return 0; } + virtual int struc_renamed(struc_t * /*sptr*/) { return 0; } + virtual int struc_expanded(struc_t * /*sptr*/) { return 0; } + virtual int struc_cmt_changed(tid_t /*struc_id*/) { return 0; } + virtual int struc_member_created(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; } + virtual int struc_member_deleted(struc_t * /*sptr*/, tid_t /*member_id*/, ea_t /*offset*/) { return 0; } + virtual int struc_member_renamed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; } + virtual int struc_member_changed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; } + virtual int thunk_func_created(func_t * /*pfn*/) { return 0; } + virtual int func_tail_appended(func_t * /*pfn*/, func_t * /*tail*/) { return 0; } + virtual int func_tail_removed(func_t * /*pfn*/, ea_t /*tail_ea*/) { return 0; } + virtual int tail_owner_changed(func_t * /*tail*/, ea_t /*owner_func*/) { return 0; } + virtual int func_noret_changed(func_t * /*pfn*/) { return 0; } + virtual int segm_added(segment_t * /*s*/) { return 0; } + virtual int segm_deleted(ea_t /*startEA*/) { return 0; } + virtual int segm_start_changed(segment_t * /*s*/) { return 0; } + virtual int segm_end_changed(segment_t * /*s*/) { return 0; } + virtual int segm_moved(ea_t /*from*/, ea_t /*to*/, asize_t /*size*/) { return 0; } +}; + +// +%} + +%{ +// +//------------------------------------------------------------------------- +int idaapi IDP_Callback(void *ud, int notification_code, va_list va) +{ + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + IDP_Hooks *proxy = (IDP_Hooks *)ud; + int ret = 0; + try + { + switch ( notification_code ) + { + case processor_t::custom_ana: + ret = proxy->custom_ana() ? 1 + cmd.size : 0; + break; + + case processor_t::custom_out: + ret = proxy->custom_out() ? 2 : 0; + break; + + case processor_t::custom_emu: + ret = proxy->custom_emu() ? 2 : 0; + break; + + case processor_t::custom_outop: + { + op_t *op = va_arg(va, op_t *); + ref_t py_obj(create_idaapi_linked_class_instance(S_PY_OP_T_CLSNAME, op)); + if ( py_obj == NULL ) + break; + ret = proxy->custom_outop(py_obj.o) ? 2 : 0; + break; + } + + case processor_t::custom_mnem: + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_ret = proxy->custom_mnem(); + if ( py_ret != NULL && PyString_Check(py_ret) ) + { + char *outbuffer = va_arg(va, char *); + size_t bufsize = va_arg(va, size_t); + + qstrncpy(outbuffer, PyString_AS_STRING(py_ret), bufsize); + ret = 2; + } + else + { + ret = 0; + } + Py_XDECREF(py_ret); + break; + } + + case processor_t::is_sane_insn: + { + int no_crefs = va_arg(va, int); + ret = proxy->is_sane_insn(no_crefs); + break; + } + + case processor_t::may_be_func: + { + int state = va_arg(va, int); + ret = proxy->may_be_func(state); + break; + } + + case processor_t::closebase: + { + proxy->closebase(); + break; + } + + case processor_t::savebase: + { + proxy->savebase(); + break; + } + + case processor_t::auto_empty_finally: + { + proxy->auto_empty_finally(); + break; + } + + case processor_t::rename: + { + ea_t ea = va_arg(va, ea_t); + const char *new_name = va_arg(va, const char *); + ret = proxy->rename(ea, new_name); + break; + } + + case processor_t::renamed: + { + ea_t ea = va_arg(va, ea_t); + const char *new_name = va_arg(va, const char *); + bool local_name = va_argi(va, bool); + proxy->renamed(ea, new_name, local_name); + break; + } + + case processor_t::undefine: + { + ea_t ea = va_arg(va, ea_t); + ret = proxy->undefine(ea); + break; + } + + case processor_t::make_code: + { + ea_t ea = va_arg(va, ea_t); + asize_t size = va_arg(va, asize_t); + ret = proxy->make_code(ea, size); + break; + } + + case processor_t::make_data: + { + ea_t ea = va_arg(va, ea_t); + flags_t flags = va_arg(va, flags_t); + tid_t tid = va_arg(va, tid_t); + asize_t len = va_arg(va, asize_t); + ret = proxy->make_data(ea, flags, tid, len); + break; + } + + case processor_t::load_idasgn: + { + const char *short_sig_name = va_arg(va, const char *); + proxy->load_idasgn(short_sig_name); + break; + } + + case processor_t::auto_empty: + { + proxy->auto_empty(); + break; + } + + case processor_t::auto_queue_empty: + { + atype_t type = va_arg(va, atype_t); + ret = proxy->auto_queue_empty(type); + break; + } + + case processor_t::add_func: + { + func_t *func = va_arg(va, func_t *); + proxy->add_func(func); + break; + } + + case processor_t::del_func: + { + func_t *func = va_arg(va, func_t *); + ret = proxy->del_func(func); + break; + } + + case processor_t::is_call_insn: + { + ea_t ea = va_arg(va, ea_t); + ret = proxy->is_call_insn(ea); + break; + } + + case processor_t::is_ret_insn: + { + ea_t ea = va_arg(va, ea_t); + bool strict = va_argi(va, bool); + ret = proxy->is_ret_insn(ea, strict); + break; + } + + case processor_t::assemble: + { + ea_t ea = va_arg(va, ea_t); + ea_t cs = va_arg(va, ea_t); + ea_t ip = va_arg(va, ea_t); + bool use32 = va_argi(va, bool); + const char *line = va_arg(va, const char *); + // Extract user buffer (we hardcode the MAXSTR size limit) + uchar *bin = va_arg(va, uchar *); + // Call python + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_buffer = proxy->assemble(ea, cs, ip, use32, line); + if ( py_buffer != NULL && PyString_Check(py_buffer) ) + { + char *s; + Py_ssize_t len; + if ( PyString_AsStringAndSize(py_buffer, &s, &len) != -1 ) + { + if ( len > MAXSTR ) + len = MAXSTR; + memcpy(bin, s, len); + ret = len; + } + } + // ret = 0 otherwise + Py_XDECREF(py_buffer); + break; + } + // validate_flirt_func, // flirt has recognized a library function + // // this callback can be used by a plugin or proc module + // // to intercept it and validate such a function + // // args: ea_t start_ea + // // const char *funcname + // // returns: -1-do not create a function, + // // 1-function is validated + // // the idp module is allowed to modify 'cmd' + // set_func_start, // Function chunk start address will be changed + // // args: func_t *pfn + // // ea_t new_start + // // Returns: 1-ok,<=0-do not change + // set_func_end, // Function chunk end address will be changed + // // args: func_t *pfn + // // ea_t new_end + // // Returns: 1-ok,<=0-do not change + // outlabel, // The kernel is going to generate an instruction + // // label line or a function header + // // args: + // // ea_t ea - + // // const char *colored_name - + // // If returns value <=0, then the kernel should + // // not generate the label + // may_show_sreg, // The kernel wants to display the segment registers + // // in the messages window. + // // arg - ea_t current_ea + // // if this function returns 0 + // // then the kernel will not show + // // the segment registers. + // // (assuming that the module have done it) + // coagulate, // Try to define some unexplored bytes + // // This notification will be called if the + // // kernel tried all possibilities and could + // // not find anything more useful than to + // // convert to array of bytes. + // // The module can help the kernel and convert + // // the bytes into something more useful. + // // arg: + // // ea_t start_ea + // // returns: number of converted bytes + 1 + // auto_empty, // Info: all analysis queues are empty + // // args: none + // // returns: none + // // This callback is called once when the + // // initial analysis is finished. If the queue is + // // not empty upon the return from this callback, + // // it will be called later again. + // // See also auto_empty_finally. + // auto_queue_empty, // One analysis queue is empty + // // args: atype_t type + // // returns: 1-yes, keep the queue empty + // // <=0-no, the queue is not empty anymore + // // This callback can be called many times, so + // // only the autoMark() functions can be used from it + // // (other functions may work but it is not tested) + // func_bounds, // find_func_bounds() finished its work + // // The module may fine tune the function bounds + // // args: int *possible_return_code + // // func_t *pfn + // // ea_t max_func_end_ea (from the kernel's point of view) + // // returns: none + // is_jump_func, // is the function a trivial "jump" function? + // // args: func_t *pfn + // // ea_t *jump_target + // // ea_t *func_pointer + // // returns: 0-no, 1-don't know, 2-yes, see jump_target + // // and func_pointer + // gen_regvar_def, // generate register variable definition line + // // args: regvar_t *v + // // returns: 0-ok + // setsgr, // The kernel has changed a segment register value + // // args: ea_t startEA + // // ea_t endEA + // // int regnum + // // sel_t value + // // sel_t old_value + // // uchar tag (SR_... values) + // // returns: 1-ok, 0-error + // set_compiler, // The kernel has changed the compiler information + // // (inf.cc structure) + // is_basic_block_end, // Is the current instruction end of a basic block? + // // This function should be defined for processors + // // with delayed jump slots. The current instruction + // // is stored in 'cmd' + // // args: bool call_insn_stops_block + // // returns: 1-unknown, 0-no, 2-yes + // reglink, // IBM PC only, ignore it + // get_vxd_name, // IBM PC only, ignore it + // // Get Vxd function name + // // args: int vxdnum + // // int funcnum + // // char *outbuf + // // returns: nothing + // + // + // moving_segm, // May the kernel move the segment? + // // args: segment_t - segment to move + // // ea_t to - new segment start address + // // returns: 1-yes, <=0-the kernel should stop + // move_segm, // A segment is moved + // // Fix processor dependent address sensitive information + // // args: ea_t from - old segment address + // // segment_t* - moved segment + // // returns: nothing + // + // + // get_stkvar_scale_factor,// Should stack variable references be multiplied by + // // a coefficient before being used in the stack frame? + // // Currently used by TMS320C55 because the references into + // // the stack should be multiplied by 2 + // // Returns: scaling factor + // // Note: PR_SCALE_STKVARS should be set to use this callback + // + // create_flat_group, // Create special segment representing the flat group + // // (to use for PC mainly) + // // args - ea_t image_base, int bitness, sel_t dataseg_sel + // + // kernel_config_loaded, // This callback is called when ida.cfg is parsed + // // args - none, returns - nothing + // + // might_change_sp, // Does the instruction at 'ea' modify the stack pointer? + // // args: ea_t ea + // // returns: 1-yes, 0-false + // // (not used yet) + // + // is_alloca_probe, // Does the function at 'ea' behave as __alloca_probe? + // // args: ea_t ea + // // returns: 2-yes, 1-false + // + // out_3byte, // Generate text representation of 3byte data + // // init_out_buffer() is called before this function + // // and all Out... function can be used. + // // uFlag contains the flags. + // // This callback might be implemented by the processor + // // module to generate custom representation of 3byte data. + // // args: + // // ea_t dataea - address of the data item + // // uint32 value - value to output + // // bool analyze_only - only create xrefs if necessary + // // do not generate text representation + // // returns: 2-yes, 1-false + // + // get_reg_name, // Generate text representation of a register + // // int reg - internal register number as defined in the processor module + // // size_t width - register width in bytes + // // char *buf - output buffer + // // size_t bufsize - size of output buffer + // // int reghi - if not -1 then this function will return the register pair + // // returns: -1 if error, strlen(buf)+2 otherwise + // // Most processor modules do not need to implement this callback + // // It is useful only if ph.regNames[reg] does not provide + // // the correct register names + // // save its local data + // out_src_file_lnnum, // Callback: generate analog of + // // #line "file.c" 123 + // // directive. + // // const char *file - source file (may be NULL) + // // size_t lnnum - line number + // // returns: 2-directive has been generated + // get_autocmt, // Callback: get dynamic auto comment + // // Will be called if the autocomments are enabled + // // and the comment retrieved from ida.int starts with + // // '$!'. 'cmd' is contains valid info. + // // char *buf - output buffer + // // size_t bufsize - output buffer size + // // returns: 2-new comment has been generated + // // 1-callback has not been handled + // // the buffer must not be changed in this case + // is_insn_table_jump, // Callback: determine if instruction is a table jump or call + // // If CF_JUMP bit can not describe all kinds of table + // // jumps, please define this callback. + // // It will be called for insns with CF_JUMP bit set. + // // input: cmd structure contains the current instruction + // // returns: 1-yes, 0-no + // auto_empty_finally, // Info: all analysis queues are empty definitively + // // args: none + // // returns: none + // // This callback is called only once. + // // See also auto_empty. + // loader_finished, // Event: external file loader finished its work + // // linput_t *li + // // uint16 neflags + // // const char *filetypename + // // Use this event to augment the existing loader functionality + // loader_elf_machine, // Event: ELF loader machine type checkpoint + // // linput_t *li + // // int machine_type + // // const char **p_procname + // // proc_def **p_pd (see ldr\elf.h) + // // set_elf_reloc_t *set_reloc + // // A plugin check the machine_type. If it is the desired one, + // // the the plugin fills p_procname with the processor name. + // // p_pd is used to handle relocations, otherwise can be left untouched + // // set_reloc can be later used by the plugin to specify relocations + // // returns: e_machine value (if it is different from the + // // original e_machine value, procname and p_pd will be ignored + // // and the new value will be used) + // // This event occurs for each loaded ELF file + // is_indirect_jump, // Callback: determine if instruction is an indrect jump + // // If CF_JUMP bit can not describe all jump types + // // jumps, please define this callback. + // // input: cmd structure contains the current instruction + // // returns: 1-use CF_JUMP, 2-no, 3-yes + // verify_noreturn, // The kernel wants to set 'noreturn' flags for a function + // // func_t *pfn + // // Returns: 1-ok, any other value-do not set 'noreturn' flag + // verify_sp, // All function instructions have been analyzed + // // Now the processor module can analyze the stack pointer + // // for the whole function + // // input: func_t *pfn + // // Returns: 1-ok, 0-bad stack pointer + // treat_hindering_item, // An item hinders creation of another item + // // args: ea_t hindering_item_ea + // // flags_t new_item_flags (0 for code) + // // ea_t new_item_ea + // // asize_t new_item_length + // // Returns: 1-no reaction, <=0-the kernel may delete the hindering item + // str2reg, // Convert a register name to a register number + // // args: const char *regname + // // Returns: register number + 2 + // // The register number is the register index in the regNames array + // // Most processor modules do not need to implement this callback + // // It is useful only if ph.regNames[reg] does not provide + // // the correct register names + // create_switch_xrefs, // Create xrefs for a custom jump table + // // in: ea_t jumpea; - address of the jump insn + // // switch_info_ex_t *; - switch information + // // returns: must return 2 + // calc_switch_cases, // Calculate case values and targets for a custom jump table + // // in: ea_t insn_ea - address of the 'indirect jump' instruction + // // switch_info_ex_t *si - switch information + // // casevec_t *casevec - vector of case values... + // // evec_t *targets - ...and corresponding target addresses + // // casevec and targets may be NULL + // // returns: 2-ok, 1-failed + // determined_main, // The main() function has been determined + // // in: ea_t main - address of the main() function + // // returns: none + // preprocess_chart, // gui has retrieved a function flow chart + // // in: qflow_chart_t *fc + // // returns: none + // // Plugins may modify the flow chart in this callback + // get_bg_color, // Get item background color + // // in: ea_t ea, bgcolor_t *color + // // Returns: 1-not implemented, 2-color set + // // Plugins can hook this callback to color disassembly lines + // // dynamically + // get_operand_string, // Request text string for operand (cli, java, ...) + // // args: int opnum + // // char *buf + // // size_t buflen + // // (cmd structure must contain info for the desired insn) + // // opnum is the operand number; -1 means any string operand + // // returns: 1 - no string (or empty string) + // // >1 - original string length with terminating zero + // + // // the following 5 events are very low level + // // take care of possible recursion + // add_cref, // a code reference is being created + // // args: ea_t from, ea_t to, cref_t type + // // returns: <0 - cancel cref creation + // add_dref, // a data reference is being created + // // args: ea_t from, ea_t to, dref_t type + // // returns: <0 - cancel dref creation + // del_cref, // a code reference is being deleted + // // args: ea_t from, ea_t to, bool expand + // // returns: <0 - cancel cref deletion + // del_dref, // a data reference is being deleted + // // args: ea_t from, ea_t to + // // returns: <0 - cancel dref deletion + // coagulate_dref, // data reference is being analyzed + // // args: ea_t from, ea_t to, bool may_define, ea_t *code_ea + // // plugin may correct code_ea (e.g. for thumb mode refs, we clear the last bit) + // // returns: <0 - cancel dref analysis + // custom_fixup, // mutipurpose notification for FIXUP_CUSTOM + // // args: cust_fix oper, ea_t ea, const fixup_data_t*, ... (see cust_fix) + // // returns: 1 - no accepted (fixup ignored by ida) + // // >1 - accepted (see cust_fix) + // off_preproc, // called from get_offset_expr, when refinfo_t + // // contain flag REFINFO_PREPROC. Normally this + // // notification used in a combination with custom_fixup + // // args: ea_t ea, int numop, ea_t* opval, const refinfo_t* ri, + // // char* buf, size_t bufsize, ea_t* target, + // // ea_t* fullvalue, ea_t from, int getn_flags + // // returns: 2 - buf filled as simple expression + // // 3 - buf filled as complex expression + // // 4 - apply standard processing (with - possible - changed values) + // // others - can't convert to offset expression + // + // set_proc_options, // called if the user specified an option string in the command line: + // // -p: + // // can be used for e.g. setting a processor subtype + // // also called if option string is passed to set_processor_type() + // // and IDC's SetProcessorType() + // // args: const char * options + // // returns: <0 - bad option string + // + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in IDP Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyErr_Occurred() ) + PyErr_Print(); + } + return ret; +} + +//--------------------------------------------------------------------------- +int idaapi IDB_Callback(void *ud, int notification_code, va_list va) +{ + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + class IDB_Hooks *proxy = (class IDB_Hooks *)ud; + ea_t ea, ea2; + bool repeatable_cmt; + type_t *type; + p_list *fnames; + int n; + enum_t id; + const_t cid; + tid_t struc_id; + struc_t *sptr; + member_t *mptr; + tid_t member_id; + func_t *pfn; + func_t *tail; + segment_t *seg; + asize_t size; + + try { + switch (notification_code) + { + case idb_event::byte_patched: + ea = va_arg(va, ea_t); + return proxy->byte_patched(ea); + + case idb_event::cmt_changed: + ea = va_arg(va, ea_t); + repeatable_cmt = va_arg(va, int); + return proxy->cmt_changed(ea, repeatable_cmt); + + case idb_event::area_cmt_changed: + { + areacb_t *cb = va_arg(va, areacb_t*); + area_t *area = va_arg(va, area_t*); + const char *cmt = va_arg(va, char*); + repeatable_cmt = va_arg(va, int); + return proxy->area_cmt_changed(cb, area, cmt, repeatable_cmt); + } + + case idb_event::ti_changed: + ea = va_arg(va, ea_t); + type = va_arg(va, type_t *); + fnames = va_arg(va, p_list *); + return proxy->ti_changed(ea, type, fnames); + + case idb_event::op_ti_changed: + ea = va_arg(va, ea_t); + n = va_arg(va, int); + type = va_arg(va, type_t *); + fnames = va_arg(va, p_list *); + return proxy->op_ti_changed(ea, n, type, fnames); + + case idb_event::op_type_changed: + ea = va_arg(va, ea_t); + n = va_arg(va, int); + return proxy->op_type_changed(ea, n); + + case idb_event::enum_created: + id = va_arg(va, enum_t); + return proxy->enum_created(id); + + case idb_event::enum_deleted: + id = va_arg(va, enum_t); + return proxy->enum_deleted(id); + + case idb_event::enum_bf_changed: + id = va_arg(va, enum_t); + return proxy->enum_bf_changed(id); + + case idb_event::enum_cmt_changed: + id = va_arg(va, enum_t); + return proxy->enum_cmt_changed(id); + +#ifdef NO_OBSOLETE_FUNCS + case idb_event::enum_member_created: +#else + case idb_event::enum_const_created: +#endif + id = va_arg(va, enum_t); + cid = va_arg(va, const_t); + return proxy->enum_member_created(id, cid); + +#ifdef NO_OBSOLETE_FUNCS + case idb_event::enum_member_deleted: +#else + case idb_event::enum_const_deleted: +#endif + id = va_arg(va, enum_t); + cid = va_arg(va, const_t); + return proxy->enum_member_deleted(id, cid); + + case idb_event::struc_created: + struc_id = va_arg(va, tid_t); + return proxy->struc_created(struc_id); + + case idb_event::struc_deleted: + struc_id = va_arg(va, tid_t); + return proxy->struc_deleted(struc_id); + + case idb_event::struc_renamed: + sptr = va_arg(va, struc_t *); + return proxy->struc_renamed(sptr); + + case idb_event::struc_expanded: + sptr = va_arg(va, struc_t *); + return proxy->struc_expanded(sptr); + + case idb_event::struc_cmt_changed: + struc_id = va_arg(va, tid_t); + return proxy->struc_cmt_changed(struc_id); + + case idb_event::struc_member_created: + sptr = va_arg(va, struc_t *); + mptr = va_arg(va, member_t *); + return proxy->struc_member_created(sptr, mptr); + + case idb_event::struc_member_deleted: + sptr = va_arg(va, struc_t *); + member_id = va_arg(va, tid_t); + ea = va_arg(va, ea_t); + return proxy->struc_member_deleted(sptr, member_id, ea); + + case idb_event::struc_member_renamed: + sptr = va_arg(va, struc_t *); + mptr = va_arg(va, member_t *); + return proxy->struc_member_renamed(sptr, mptr); + + case idb_event::struc_member_changed: + sptr = va_arg(va, struc_t *); + mptr = va_arg(va, member_t *); + return proxy->struc_member_changed(sptr, mptr); + + case idb_event::thunk_func_created: + pfn = va_arg(va, func_t *); + return proxy->thunk_func_created(pfn); + + case idb_event::func_tail_appended: + pfn = va_arg(va, func_t *); + tail = va_arg(va, func_t *); + return proxy->func_tail_appended(pfn, tail); + + case idb_event::func_tail_removed: + pfn = va_arg(va, func_t *); + ea = va_arg(va, ea_t); + return proxy->func_tail_removed(pfn, ea); + + case idb_event::tail_owner_changed: + tail = va_arg(va, func_t *); + ea = va_arg(va, ea_t); + return proxy->tail_owner_changed(tail, ea); + + case idb_event::func_noret_changed: + pfn = va_arg(va, func_t *); + return proxy->func_noret_changed(pfn); + + case idb_event::segm_added: + seg = va_arg(va, segment_t *); + return proxy->segm_added(seg); + + case idb_event::segm_deleted: + ea = va_arg(va, ea_t); + return proxy->segm_deleted(ea); + + case idb_event::segm_start_changed: + seg = va_arg(va, segment_t *); + return proxy->segm_start_changed(seg); + + case idb_event::segm_end_changed: + seg = va_arg(va, segment_t *); + return proxy->segm_end_changed(seg); + + case idb_event::segm_moved: + ea = va_arg(va, ea_t); + ea2 = va_arg(va, ea_t); + size = va_arg(va, asize_t); + return proxy->segm_moved(ea, ea2, size); + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in IDB Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if (PyErr_Occurred()) + PyErr_Print(); + } + return 0; +} + +//------------------------------------------------------------------------- +static const regval_t *idaapi _py_getreg( + const char *name, + const regval_t *regvals) +{ + for ( int i=dbg->registers_size - 1; i >= 0; i-- ) + { + if ( strieq(name, dbg->registers(i).name) ) + return ®vals[i]; + } + static regval_t rv; + return &rv; +} + +//------------------------------------------------------------------------- +// +%} diff --git a/swig/kernwin.i b/swig/kernwin.i index b23bc9d2..f5fe6baf 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -1,6703 +1,6728 @@ -// Ignore the va_list functions -%ignore AskUsingForm_cv; -%ignore AskUsingForm_c; -%ignore OpenForm_cv; -%ignore OpenForm_c; -%ignore close_form; -%ignore vaskstr; -%ignore strvec_t; -%ignore load_custom_icon; -%ignore vasktext; -%ignore add_menu_item; -%rename (add_menu_item) py_add_menu_item; -%ignore del_menu_item; -%rename (del_menu_item) py_del_menu_item; -%ignore vwarning; - -%ignore choose_idasgn; -%rename (choose_idasgn) py_choose_idasgn; - -%rename (del_hotkey) py_del_hotkey; -%rename (add_hotkey) py_add_hotkey; - -%ignore msg; -%rename (msg) py_msg; - -%ignore umsg; -%rename (umsg) py_umsg; - -%ignore textctrl_info_t; -%ignore vinfo; -%ignore UI_Callback; -%ignore vnomem; -%ignore vmsg; -%ignore show_wait_box_v; -%ignore askbuttons_cv; -%ignore askfile_cv; -%ignore askyn_cv; -%ignore askyn_v; -%ignore add_custom_viewer_popup_item; -%ignore create_custom_viewer; -%ignore take_database_snapshot; -%ignore restore_database_snapshot; -%ignore destroy_custom_viewer; -%ignore destroy_custom_viewerdestroy_custom_viewer; -%ignore set_custom_viewer_popup_menu; -%ignore set_custom_viewer_handler; -%ignore set_custom_viewer_range; -%ignore is_idaview; -%ignore refresh_custom_viewer; -%ignore set_custom_viewer_handlers; -%ignore get_viewer_name; -// Ignore these string functions. There are trivial replacements in Python. -%ignore addblanks; -%ignore trim; -%ignore skipSpaces; -%ignore stristr; -%ignore set_nav_colorizer; -%rename (set_nav_colorizer) py_set_nav_colorizer; -%rename (call_nav_colorizer) py_call_nav_colorizer; - -%ignore get_highlighted_identifier; -%rename (get_highlighted_identifier) py_get_highlighted_identifier; - - -// 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; - -%ignore action_desc_t::handler; -%ignore action_handler_t; -%ignore register_action; -%rename (register_action) py_register_action; -%ignore unregister_action; -%rename (unregister_action) py_unregister_action; -%ignore attach_dynamic_action_to_popup; -%rename (attach_dynamic_action_to_popup) py_attach_dynamic_action_to_popup; - -%include "typemaps.i" - -%rename (asktext) py_asktext; -%rename (str2ea) py_str2ea; -%rename (str2user) py_str2user; -%ignore process_ui_action; -%rename (process_ui_action) py_process_ui_action; -%ignore execute_sync; -%ignore exec_request_t; -%rename (execute_sync) py_execute_sync; - -%ignore ui_request_t; -%ignore execute_ui_requests; -%rename (execute_ui_requests) py_execute_ui_requests; - -%ignore timer_t; -%ignore register_timer; -%rename (register_timer) py_register_timer; -%ignore unregister_timer; -%rename (unregister_timer) py_unregister_timer; - -// Make askaddr(), askseg(), and asklong() return a -// tuple: (result, value) -%rename (_asklong) asklong; -%rename (_askaddr) askaddr; -%rename (_askseg) askseg; - -%ignore qvector::operator==; -%ignore qvector::operator!=; -%ignore qvector::find; -%ignore qvector::has; -%ignore qvector::del; -%ignore qvector::add_unique; - -%ignore gen_disasm_text; -%rename (gen_disasm_text) py_gen_disasm_text; - -%feature("director") UI_Hooks; - -//------------------------------------------------------------------------- -%{ -struct py_action_handler_t : public action_handler_t -{ - py_action_handler_t(); // No. - py_action_handler_t(PyObject *_o) - : pyah(borref_t(_o)), has_activate(false), has_update(false) - { - ref_t act(PyW_TryGetAttrString(pyah.o, "activate")); - if ( act != NULL && PyCallable_Check(act.o) > 0 ) - has_activate = true; - - ref_t upd(PyW_TryGetAttrString(pyah.o, "update")); - if ( upd != NULL && PyCallable_Check(upd.o) > 0 ) - has_update = true; - } - virtual idaapi ~py_action_handler_t() - { - PYW_GIL_GET; - // NOTE: We need to do the decref _within_ the PYW_GIL_GET scope, - // and not leave it to the destructor to clean it up, because when - // ~ref_t() gets called, the GIL will have already been released. - pyah = ref_t(); - } - virtual int idaapi activate(action_activation_ctx_t *ctx) - { - if ( !has_activate ) - return 0; - PYW_GIL_GET_AND_REPORT_ERROR; - newref_t pyctx(SWIG_NewPointerObj(SWIG_as_voidptr(ctx), SWIGTYPE_p_action_activation_ctx_t, 0)); - newref_t pyres(PyObject_CallMethod(pyah.o, (char *)"activate", (char *) "O", pyctx.o)); - return PyErr_Occurred() ? 0 : ((pyres != NULL && PyInt_Check(pyres.o)) ? PyInt_AsLong(pyres.o) : 0); - } - virtual action_state_t idaapi update(action_update_ctx_t *ctx) - { - if ( !has_update ) - return AST_DISABLE; - PYW_GIL_GET_AND_REPORT_ERROR; - newref_t pyctx(SWIG_NewPointerObj(SWIG_as_voidptr(ctx), SWIGTYPE_p_action_update_ctx_t, 0)); - newref_t pyres(PyObject_CallMethod(pyah.o, (char *)"update", (char *) "O", pyctx.o)); - return PyErr_Occurred() ? AST_DISABLE_ALWAYS : ((pyres != NULL && PyInt_Check(pyres.o)) ? action_state_t(PyInt_AsLong(pyres.o)) : AST_DISABLE); - } -private: - ref_t pyah; - bool has_activate; - bool has_update; -}; - -typedef std::map py_action_handlers_t; -static py_action_handlers_t py_action_handlers; - -%} - -%inline %{ -void refresh_lists(void) -{ - Py_BEGIN_ALLOW_THREADS; - callui(ui_list); - Py_END_ALLOW_THREADS; -} -%} - -# This is for get_cursor() -%apply int *OUTPUT {int *x, int *y}; - -SWIG_DECLARE_PY_CLINKED_OBJECT(textctrl_info_t) - -%inline %{ -// -//------------------------------------------------------------------------ - -//------------------------------------------------------------------------ -/* -# -def register_timer(interval, callback): - """ - Register a timer - - @param interval: Interval in milliseconds - @param callback: A Python callable that takes no parameters and returns an integer. - The callback may return: - -1 : to unregister the timer - >= 0 : the new or same timer interval - @return: None or a timer object - """ - pass -# -*/ -static PyObject *py_register_timer(int interval, PyObject *py_callback) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( py_callback == NULL || !PyCallable_Check(py_callback) ) - Py_RETURN_NONE; - - // An inner class hosting the callback method - struct tmr_t - { - static int idaapi callback(void *ud) - { - PYW_GIL_GET; - py_timer_ctx_t *ctx = (py_timer_ctx_t *)ud; - newref_t py_result(PyObject_CallFunctionObjArgs(ctx->pycallback, NULL)); - int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result.o); - - // Timer has been unregistered? - if ( ret == -1 ) - { - // Free the context - Py_DECREF(ctx->pycallback); - delete ctx; - } - return ret; - }; - }; - - py_timer_ctx_t *ctx = new py_timer_ctx_t(); - ctx->pycallback = py_callback; - Py_INCREF(py_callback); - ctx->timer_id = register_timer( - interval, - tmr_t::callback, - ctx); - - if ( ctx->timer_id == NULL ) - { - Py_DECREF(py_callback); - delete ctx; - Py_RETURN_NONE; - } - return PyCObject_FromVoidPtr(ctx, NULL); -} - -//------------------------------------------------------------------------ -/* -# -def unregister_timer(timer_obj): - """ - Unregister a timer - - @param timer_obj: a timer object previously returned by a register_timer() - @return: Boolean - @note: After the timer has been deleted, the timer_obj will become invalid. - """ - pass -# -*/ -static PyObject *py_unregister_timer(PyObject *py_timerctx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( py_timerctx == NULL || !PyCObject_Check(py_timerctx) ) - Py_RETURN_FALSE; - - py_timer_ctx_t *ctx = (py_timer_ctx_t *) PyCObject_AsVoidPtr(py_timerctx); - if ( !unregister_timer(ctx->timer_id) ) - Py_RETURN_FALSE; - - Py_DECREF(ctx->pycallback); - delete ctx; - - Py_RETURN_TRUE; -} - -//------------------------------------------------------------------------ -/* -# -def choose_idasgn(): - """ - Opens the signature chooser - - @return: None or the selected signature name - """ - pass -# -*/ -static PyObject *py_choose_idasgn() -{ - char *name = choose_idasgn(); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( name == NULL ) - { - Py_RETURN_NONE; - } - else - { - PyObject *py_str = PyString_FromString(name); - qfree(name); - return py_str; - } -} - -//------------------------------------------------------------------------ -/* -# -def get_highlighted_identifier(flags = 0): - """ - Returns the currently highlighted identifier - - @param flags: reserved (pass 0) - @return: None or the highlighted identifier - """ - pass -# -*/ -static PyObject *py_get_highlighted_identifier(int flags = 0) -{ - char buf[MAXSTR]; - bool ok = get_highlighted_identifier(buf, sizeof(buf), flags); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !ok ) - Py_RETURN_NONE; - else - return PyString_FromString(buf); -} - -//------------------------------------------------------------------------ -static int py_load_custom_icon_fn(const char *filename) -{ - return load_custom_icon(filename); -} - -//------------------------------------------------------------------------ -static int py_load_custom_icon_data(PyObject *data, const char *format) -{ - Py_ssize_t len; - char *s; - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyString_AsStringAndSize(data, &s, &len) == -1 ) - return 0; - else - return load_custom_icon(s, len, format); -} - -//------------------------------------------------------------------------ -/* -# -def free_custom_icon(icon_id): - """ - Frees an icon loaded with load_custom_icon() - """ - pass -# -*/ - -//------------------------------------------------------------------------- -/* -# -def readsel2(view, p0, p1): - """ - Read the user selection, and store its information in p0 (from) and p1 (to). - - This can be used as follows: - - - >>> p0 = idaapi.twinpos_t() - p1 = idaapi.twinpos_t() - view = idaapi.get_current_viewer() - idaapi.readsel2(view, p0, p1) - - - At that point, p0 and p1 hold information for the selection. - But, the 'at' property of p0 and p1 is not properly typed. - To specialize it, call #place() on it, passing it the view - they were retrieved from. Like so: - - - >>> place0 = p0.place(view) - place1 = p1.place(view) - - - This will effectively "cast" the place into a specialized type, - holding proper information, depending on the view type (e.g., - disassembly, structures, enums, ...) - - @param view: The view to retrieve the selection for. - @param p0: Storage for the "from" part of the selection. - @param p1: Storage for the "to" part of the selection. - @return: a bool value indicating success. - """ - pass -# -*/ - -//------------------------------------------------------------------------ -/* -# -def umsg(text): - """ - Prints text into IDA's Output window - - @param text: text to print - Can be Unicode, or string in UTF-8 encoding - @return: number of bytes printed - """ - pass -# -*/ -static PyObject* py_umsg(PyObject *o) -{ - PyObject* utf8 = NULL; - if ( PyUnicode_Check(o) ) - { - utf8 = PyUnicode_AsUTF8String(o); - o = utf8; - } - else if ( !PyString_Check(o) ) - { - PyErr_SetString(PyExc_TypeError, "A unicode or UTF-8 string expected"); - return NULL; - } - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = umsg("%s", PyString_AsString(o)); - Py_END_ALLOW_THREADS; - Py_XDECREF(utf8); - return PyInt_FromLong(rc); -} - -//------------------------------------------------------------------------ -/* -# -def msg(text): - """ - Prints text into IDA's Output window - - @param text: text to print - Can be Unicode, or string in local encoding - @return: number of bytes printed - """ - pass -# -*/ -static PyObject* py_msg(PyObject *o) -{ - if ( PyUnicode_Check(o) ) - return py_umsg(o); - - if ( !PyString_Check(o) ) - { - PyErr_SetString(PyExc_TypeError, "A string expected"); - return NULL; - } - int rc; - Py_BEGIN_ALLOW_THREADS; - rc = msg("%s", PyString_AsString(o)); - Py_END_ALLOW_THREADS; - return PyInt_FromLong(rc); -} - -//------------------------------------------------------------------------ -/* -# -def asktext(max_text, defval, prompt): - """ - Asks for a long text - - @param max_text: Maximum text length - @param defval: The default value - @param prompt: The prompt value - @return: None or the entered string - """ - pass -# -*/ -PyObject *py_asktext(int max_text, const char *defval, const char *prompt) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( max_text <= 0 ) - Py_RETURN_NONE; - - char *buf = new char[max_text]; - if ( buf == NULL ) - Py_RETURN_NONE; - - PyObject *py_ret; - if ( asktext(size_t(max_text), buf, defval, "%s", prompt) != NULL ) - { - py_ret = PyString_FromString(buf); - } - else - { - py_ret = Py_None; - Py_INCREF(py_ret); - } - delete [] buf; - return py_ret; -} - -//------------------------------------------------------------------------ -/* -# -def str2ea(addr): - """ - Converts a string express to EA. The expression evaluator may be called as well. - - @return: BADADDR or address value - """ - pass -# -*/ -ea_t py_str2ea(const char *str, ea_t screenEA = BADADDR) -{ - ea_t ea; - bool ok = str2ea(str, &ea, screenEA); - return ok ? ea : BADADDR; -} - -//------------------------------------------------------------------------ -/* -# -def str2user(str): - """ - Insert C-style escape characters to string - - @return: new string with escape characters inserted - """ - pass -# -*/ -PyObject *py_str2user(const char *str) -{ - qstring qstr(str); - qstring retstr; - qstr2user(&retstr, qstr); - return PyString_FromString(retstr.c_str()); -} - -//------------------------------------------------------------------------ -/* -# -def process_ui_action(name): - """ - Invokes an IDA UI action by name - - @param name: action name - @return: Boolean - """ - pass -# -*/ -static bool py_process_ui_action(const char *name, int flags = 0) -{ - return process_ui_action(name, flags, NULL); -} - -//------------------------------------------------------------------------ -/* -# -def del_menu_item(menu_ctx): - """Deprecated. Use detach_menu_item()/unregister_action() instead.""" - pass -# -*/ -static bool py_del_menu_item(PyObject *py_ctx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_ctx) ) - return false; - - py_add_del_menu_item_ctx *ctx = (py_add_del_menu_item_ctx *)PyCObject_AsVoidPtr(py_ctx); - - bool ok = del_menu_item(ctx->menupath.c_str()); - if ( ok ) - { - Py_DECREF(ctx->cb_data); - delete ctx; - } - - return ok; -} - -//------------------------------------------------------------------------ -/* -# -def del_hotkey(ctx): - """ - Deletes a previously registered function hotkey - - @param ctx: Hotkey context previously returned by add_hotkey() - - @return: Boolean. - """ - pass -# -*/ -bool py_del_hotkey(PyObject *pyctx) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(pyctx) ) - return false; - - py_idchotkey_ctx_t *ctx = (py_idchotkey_ctx_t *) PyCObject_AsVoidPtr(pyctx); - if ( !del_idc_hotkey(ctx->hotkey.c_str()) ) - return false; - - Py_DECREF(ctx->pyfunc); - delete ctx; - return true; -} - -//------------------------------------------------------------------------ -/* -# -def add_hotkey(hotkey, pyfunc): - """ - Associates a function call with a hotkey. - Callable pyfunc will be called each time the hotkey is pressed - - @param hotkey: The hotkey - @param pyfunc: Callable - - @return: Context object on success or None on failure. - """ - pass -# -*/ -PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - // Make sure a callable was passed - if ( !PyCallable_Check(pyfunc) ) - return NULL; - - // Form the function name - qstring idc_func_name; - idc_func_name.sprnt("py_hotkeycb_%p", pyfunc); - - // Can add the hotkey? - if ( add_idc_hotkey(hotkey, idc_func_name.c_str()) == IDCHK_OK ) do - { - // Generate global variable name - qstring idc_gvarname; - idc_gvarname.sprnt("_g_pyhotkey_ref_%p", pyfunc); - - // Now add the global variable - idc_value_t *gvar = add_idc_gvar(idc_gvarname.c_str()); - if ( gvar == NULL ) - break; - - // The function body will call a registered IDC function that - // will take a global variable that wraps a PyCallable as a pvoid - qstring idc_func; - idc_func.sprnt("static %s() { %s(%s); }", - idc_func_name.c_str(), - S_PYINVOKE0, - idc_gvarname.c_str()); - - // Compile the IDC condition - char errbuf[MAXSTR]; - if ( !CompileLineEx(idc_func.c_str(), errbuf, sizeof(errbuf)) ) - break; - - // Create new context - // Define context - py_idchotkey_ctx_t *ctx = new py_idchotkey_ctx_t(); - - // Remember the hotkey - ctx->hotkey = hotkey; - - // Take reference to the callable - ctx->pyfunc = pyfunc; - Py_INCREF(pyfunc); - - // Bind IDC variable w/ the PyCallable - gvar->set_pvoid(pyfunc); - - // Return the context - return PyCObject_FromVoidPtr(ctx, NULL); - } while (false); - - // Cleanup - del_idc_hotkey(hotkey); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------ -/* -# -def add_menu_item(menupath, name, hotkey, flags, callback, args): - """Deprecated. Use register_action()/attach_menu_item() instead.""" - pass -# -*/ -bool idaapi py_menu_item_callback(void *userdata); -static PyObject *py_add_menu_item( - const char *menupath, - const char *name, - const char *hotkey, - int flags, - PyObject *pyfunc, - PyObject *args) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bool no_args; - - // No slash in the menu path? - const char *p = strrchr(menupath, '/'); - if ( p == NULL ) - Py_RETURN_NONE; - - if ( args == Py_None ) - { - no_args = true; - args = PyTuple_New(0); - if ( args == NULL ) - return NULL; - } - else if ( !PyTuple_Check(args) ) - { - PyErr_SetString(PyExc_TypeError, "args must be a tuple or None"); - return NULL; - } - else - { - no_args = false; - } - - // Form a tuple holding the function to be called and its arguments - PyObject *cb_data = Py_BuildValue("(OO)", pyfunc, args); - - // If we created an empty tuple, then we must free it - if ( no_args ) - Py_DECREF(args); - - // Add the menu item - bool b = add_menu_item(menupath, name, hotkey, flags, py_menu_item_callback, (void *)cb_data); - - if ( !b ) - { - Py_XDECREF(cb_data); - Py_RETURN_NONE; - } - // Create a context (for the delete_menu_item()) - py_add_del_menu_item_ctx *ctx = new py_add_del_menu_item_ctx(); - - // Form the complete menu path - ctx->menupath.append(menupath, p - menupath + 1); - ctx->menupath.append(name); - // Save callback data - ctx->cb_data = cb_data; - - // Return context to user - return PyCObject_FromVoidPtr(ctx, NULL); -} - -//------------------------------------------------------------------------ -/* -# - -MFF_FAST = 0x0000 -"""execute code as soon as possible -this mode is ok call ui related functions -that do not query the database.""" - -MFF_READ = 0x0001 -"""execute code only when ida is idle and it is safe to query the database. -this mode is recommended only for code that does not modify the database. -(nb: ida may be in the middle of executing another user request, for example it may be waiting for him to enter values into a modal dialog box)""" - -MFF_WRITE = 0x0002 -"""execute code only when ida is idle and it is safe to modify the database. in particular, this flag will suspend execution if there is -a modal dialog box on the screen this mode can be used to call any ida api function. MFF_WRITE implies MFF_READ""" - -MFF_NOWAIT = 0x0004 -"""Do not wait for the request to be executed. -he caller should ensure that the request is not -destroyed until the execution completes. -if not, the request will be ignored. -the return code of execute_sync() is meaningless -in this case. -This flag can be used to delay the code execution -until the next UI loop run even from the main thread""" - -def execute_sync(callable, reqf): - """ - Executes a function in the context of the main thread. - If the current thread not the main thread, then the call is queued and - executed afterwards. - - @note: The Python version of execute_sync() cannot be called from a different thread - for the time being. - @param callable: A python callable object - @param reqf: one of MFF_ flags - @return: -1 or the return value of the callable - """ - pass -# -*/ -//------------------------------------------------------------------------ -static int py_execute_sync(PyObject *py_callable, int reqf) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - int rc = -1; - // Callable? - if ( PyCallable_Check(py_callable) ) - { - struct py_exec_request_t : exec_request_t - { - ref_t py_callable; - virtual int idaapi execute() - { - PYW_GIL_GET; - newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); - int ret = py_result == NULL || !PyInt_Check(py_result.o) - ? -1 - : PyInt_AsLong(py_result.o); - // if the requesting thread decided not to wait for the request to - // complete, we have to self-destroy, nobody else will do it - if ( (code & MFF_NOWAIT) != 0 ) - delete this; - return ret; - } - py_exec_request_t(PyObject *pyc) - { - // No need to GIL-ensure here, since this is created - // within the py_execute_sync() scope. - py_callable = borref_t(pyc); - } - virtual ~py_exec_request_t() - { - // Need to GIL-ensure here, since this might be called - // from the main thread. - PYW_GIL_GET; - py_callable = ref_t(); // Release callable - } - }; - py_exec_request_t *req = new py_exec_request_t(py_callable); - - // Release GIL before executing, or if this is running in the - // non-main thread, this will wait on the req.sem, while the main - // thread might be waiting for the GIL to be available. - Py_BEGIN_ALLOW_THREADS; - rc = execute_sync(*req, reqf); - Py_END_ALLOW_THREADS; - // destroy the request once it is finished. exception: NOWAIT requests - // will be handled in the future, so do not destroy them yet! - if ( (reqf & MFF_NOWAIT) == 0 ) - delete req; - } - return rc; -} - -//------------------------------------------------------------------------ -/* -# - -def execute_ui_requests(callable_list): - """ - Inserts a list of callables into the UI message processing queue. - When the UI is ready it will call one callable. - A callable can request to be called more than once if it returns True. - - @param callable_list: A list of python callable objects. - @note: A callable should return True if it wants to be called more than once. - @return: Boolean. False if the list contains a non callabale item - """ - pass -# -*/ -static bool py_execute_ui_requests(PyObject *py_list) -{ - struct py_ui_request_t: public ui_request_t - { - private: - ref_vec_t py_callables; - size_t py_callable_idx; - - static int idaapi s_py_list_walk_cb( - const ref_t &py_item, - Py_ssize_t index, - void *ud) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - // Not callable? Terminate iteration - if ( !PyCallable_Check(py_item.o) ) - return CIP_FAILED; - - // Append this callable and increment its reference - py_ui_request_t *_this = (py_ui_request_t *)ud; - _this->py_callables.push_back(py_item); - return CIP_OK; - } - public: - py_ui_request_t(): py_callable_idx(0) - { - } - - virtual bool idaapi run() - { - PYW_GIL_GET; - - // Get callable - ref_t py_callable = py_callables.at(py_callable_idx); - bool reschedule; - newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); - reschedule = py_result != NULL && PyObject_IsTrue(py_result.o); - - // No rescheduling? Then advance to the next callable - if ( !reschedule ) - ++py_callable_idx; - - // Reschedule this C callback only if there are more callables - return py_callable_idx < py_callables.size(); - } - - // Walk the list and extract all callables - bool init(PyObject *py_list) - { - Py_ssize_t count = pyvar_walk_list( - py_list, - s_py_list_walk_cb, - this); - return count > 0; - } - - virtual idaapi ~py_ui_request_t() - { - py_callables.clear(); - } - }; - - py_ui_request_t *req = new py_ui_request_t(); - if ( !req->init(py_list) ) - { - delete req; - return false; - } - execute_ui_requests(req, NULL); - return true; -} - -//------------------------------------------------------------------------ -/* -# -def set_dock_pos(src, dest, orient, left = 0, top = 0, right = 0, bottom = 0): - """ - Sets the dock orientation of a window relatively to another window. - - @param src: Source docking control - @param dest: Destination docking control - @param orient: One of DOR_XXXX constants - @param left, top, right, bottom: These parameter if DOR_FLOATING is used, or if you want to specify the width of docked windows - @return: Boolean - - Example: - set_dock_pos('Structures', 'Enums', DOR_RIGHT) <- docks the Structures window to the right of Enums window - """ - pass -# -*/ - -//------------------------------------------------------------------------ -/* -# -def is_idaq(): - """ - Returns True or False depending if IDAPython is hosted by IDAQ - """ -# -*/ - -//--------------------------------------------------------------------------- -// UI hooks -//--------------------------------------------------------------------------- -int idaapi UI_Callback(void *ud, int notification_code, va_list va); -/* -# -class UI_Hooks(object): - def hook(self): - """ - Creates an UI hook - - @return: Boolean true on success - """ - pass - - def unhook(self): - """ - Removes the UI hook - @return: Boolean true on success - """ - pass - - def preprocess(self, name): - """ - IDA ui is about to handle a user command - - @param name: ui command name - (these names can be looked up in ida[tg]ui.cfg) - @return: 0-ok, nonzero - a plugin has handled the command - """ - pass - - def postprocess(self): - """ - An ida ui command has been handled - - @return: Ignored - """ - pass - - def saving(self): - """ - The kernel is saving the database. - - @return: Ignored - """ - pass - - def saved(self): - """ - The kernel has saved the database. - - @return: Ignored - """ - pass - - def get_ea_hint(self, ea): - """ - The UI wants to display a simple hint for an address in the navigation band - - @param ea: The address - @return: String with the hint or None - """ - pass - - def updating_actions(self, ctx): - """ - The UI is about to batch-update some actions. - - @param ctx: The action_update_ctx_t instance - @return: Ignored - """ - pass - - def updated_actions(self): - """ - The UI is done updating actions. - - @return: Ignored - """ - pass - - def populating_tform_popup(self, form, popup): - """ - The UI is populating the TForm's popup menu. - Now is a good time to call idaapi.attach_action_to_popup() - - @param form: The form - @param popup: The popup menu. - @return: Ignored - """ - pass - - def finish_populating_tform_popup(self, form, popup): - """ - The UI is about to be done populating the TForm's popup menu. - Now is a good time to call idaapi.attach_action_to_popup() - - @param form: The form - @param popup: The popup menu. - @return: Ignored - """ - pass - - def term(self): - """ - IDA is terminated and the database is already closed. - The UI may close its windows in this callback. - """ - # if the user forgot to call unhook, do it for him - self.unhook() - - def __term__(self): - self.term() - -# -*/ -class UI_Hooks -{ -public: - virtual ~UI_Hooks() - { - unhook(); - } - - bool hook() - { - return hook_to_notification_point(HT_UI, UI_Callback, this); - } - - bool unhook() - { - return unhook_from_notification_point(HT_UI, UI_Callback, this); - } - - virtual int preprocess(const char * /*name*/) - { - return 0; - } - - virtual void postprocess() - { - } - - virtual void saving() - { - } - - virtual void saved() - { - } - - virtual void term() - { - } - - virtual PyObject *get_ea_hint(ea_t /*ea*/) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - }; - - virtual void current_tform_changed(TForm * /*form*/, TForm * /*previous_form*/) - { - } - - virtual void updating_actions(action_update_ctx_t *ctx) - { - } - - virtual void updated_actions() - { - } - - virtual void populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/) - { - } - - virtual void finish_populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/) - { - } -}; - -//------------------------------------------------------------------------- -bool py_register_action(action_desc_t *desc) -{ - bool ok = register_action(*desc); - if ( ok ) - { - // Success. We are managing this handler from now on, - // and must prevent it from being destroyed. - py_action_handlers[desc->name] = desc->handler; - // Let's set this to NULL, so when the wrapping Python action_desc_t - // instance is deleted, it doesn't try to delete the handler (See - // kernwin.i's action_desc_t::~action_desc_t()). - desc->handler = NULL; - } - return ok; -} - -//------------------------------------------------------------------------- -bool py_unregister_action(const char *name) -{ - bool ok = unregister_action(name); - if ( ok ) - { - py_action_handler_t *handler = - (py_action_handler_t *) py_action_handlers[name]; - delete handler; - py_action_handlers.erase(name); - } - return ok; -} - -//------------------------------------------------------------------------- -bool py_attach_dynamic_action_to_popup( - TForm *form, - TPopupMenu *popup_handle, - action_desc_t *desc, - const char *popuppath = NULL, - int flags = 0) -{ - bool ok = attach_dynamic_action_to_popup( - form, popup_handle, *desc, popuppath, flags); - if ( ok ) - // Set the handler to null, so the desc won't destroy - // it, as it noticed ownership was taken by IDA. - // In addition, we don't need to register into the - // 'py_action_handlers', because IDA will destroy the - // handler as soon as the popup menu is closed. - desc->handler = NULL; - return ok; -} - -// This is similar to a twinline_t, with improved memory management: -// twinline_t has a dummy destructor, that performs no cleanup. -struct disasm_line_t -{ - disasm_line_t() : at(NULL) {} - ~disasm_line_t() { qfree(at); } - disasm_line_t(const disasm_line_t &other) { *this = other; } - disasm_line_t &operator=(const disasm_line_t &other) - { - qfree(at); - at = other.at == NULL ? NULL : other.at->clone(); - return *this; - } - place_t *at; - qstring line; - color_t prefix_color; - bgcolor_t bg_color; - bool is_default; -}; -DECLARE_TYPE_AS_MOVABLE(disasm_line_t); -typedef qvector disasm_text_t; - -//------------------------------------------------------------------------- -void py_gen_disasm_text(ea_t ea1, ea_t ea2, disasm_text_t &text, bool truncate_lines) -{ - text_t _text; - gen_disasm_text(ea1, ea2, _text, truncate_lines); - for ( size_t i = 0, n = _text.size(); i < n; ++i ) - { - const twinline_t &tl = _text[i]; - disasm_line_t &dl = text.push_back(); - dl.at = tl.at; // Transfer ownership - dl.line.inject(tl.line); // Transfer ownership - } -} - -//------------------------------------------------------------------------- -// Although 'TCustomControl*' and 'TForm*' instances can both be used -// for attach_action_to_popup() at a binary-level, IDAPython SWIG bindings -// require that a 'TForm *' wrapper be passed to wrap_attach_action_to_popup(). -// Thus, we provide another attach_action_to_popup() version, that -// accepts a 'TCustomControl' as first argument. -// -// Since user-created GraphViewer are created like so: -// +-------- PluginForm ----------+ -// |+----- TCustomControl -------+| -// || || -// || || -// || || -// || || -// || || -// |+----------------------------+| -// +------------------------------+ -// The user cannot use GetTForm(), and use that to attach -// an action to, because that'll attach the action to the PluginForm -// instance. -// Instead, the user must use GetTCustomControl(), and call -// this function below with it. -bool attach_action_to_popup( - TCustomControl *tcc, - TPopupMenu *popup_handle, - const char *name, - const char *popuppath = NULL, - int flags = 0) -{ - return attach_action_to_popup((TForm*) tcc, popup_handle, name, popuppath, flags); -} - -//------------------------------------------------------------------------- -/* -# -def set_nav_colorizer(callback): - """ - Set a new colorizer for the navigation band. - - The 'callback' is a function of 2 arguments: - - ea (the EA to colorize for) - - nbytes (the number of bytes at that EA) - and must return a 'long' value. - - The previous colorizer is returned, allowing - the new 'callback' to use 'call_nav_colorizer' - with it. - - Note that the previous colorizer is returned - only the first time set_nav_colorizer() is called: - due to the way the colorizers API is defined in C, - it is impossible to chain more than 2 colorizers - in IDAPython: the original, IDA-provided colorizer, - and a user-provided one. - - Example: colorizer inverting the color provided by the IDA colorizer: - def my_colorizer(ea, nbytes): - global ida_colorizer - orig = idaapi.call_nav_colorizer(ida_colorizer, ea, nbytes) - return long(~orig) - - ida_colorizer = idaapi.set_nav_colorizer(my_colorizer) - """ - pass -# -*/ -nav_colorizer_t *py_set_nav_colorizer(PyObject *new_py_colorizer) -{ - static ref_t py_colorizer; - struct ida_local lambda_t - { - static uint32 idaapi call_py_colorizer(ea_t ea, asize_t nbytes) - { - PYW_GIL_GET; - - if ( py_colorizer == NULL ) // Shouldn't happen. - return 0; - newref_t pyres = PyObject_CallFunction( - py_colorizer.o, "KK", - (unsigned long long) ea, - (unsigned long long) nbytes); - PyW_ShowCbErr("nav_colorizer"); - if ( pyres.o == NULL ) - return 0; - if ( !PyLong_Check(pyres.o) ) - { - static bool warned = false; - if ( !warned ) - { - msg("WARNING: set_nav_colorizer() callback must return a 'long'.\n"); - warned = true; - } - return 0; - } - return PyLong_AsLong(pyres.o); - } - }; - - bool need_install = py_colorizer == NULL; - py_colorizer = borref_t(new_py_colorizer); - return need_install - ? set_nav_colorizer(lambda_t::call_py_colorizer) - : NULL; -} - -//------------------------------------------------------------------------- -/* -# -def call_nav_colorizer(colorizer, ea, nbytes): - """ - To be used with the IDA-provided colorizer, that is - returned as result of the first call to set_nav_colorizer(). - - This is a trivial trampoline, so that SWIG can generate a - wrapper that will do the types checking. - """ - pass -# -*/ -uint32 py_call_nav_colorizer( - nav_colorizer_t *col, - ea_t ea, - asize_t nbytes) -{ - return col(ea, nbytes); -} - - - -//--------------------------------------------------------------------------- -uint32 idaapi choose_sizer(void *self) -{ - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod((PyObject *)self, "sizer", "")); - return PyInt_AsLong(pyres.o); -} - -//--------------------------------------------------------------------------- -char *idaapi choose_getl(void *self, uint32 n, char *buf) -{ - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - (PyObject *)self, - "getl", - "l", - n)); - - const char *res; - if (pyres == NULL || (res = PyString_AsString(pyres.o)) == NULL ) - qstrncpy(buf, "", MAXSTR); - else - qstrncpy(buf, res, MAXSTR); - return buf; -} - -//--------------------------------------------------------------------------- -void idaapi choose_enter(void *self, uint32 n) -{ - PYW_GIL_GET; - newref_t res(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); -} - -//--------------------------------------------------------------------------- -uint32 choose_choose( - void *self, - int flags, - int x0,int y0, - int x1,int y1, - int width, - int deflt, - int icon) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); - const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; - - int r = choose( - flags, - x0, y0, - x1, y1, - self, - width, - choose_sizer, - choose_getl, - title, - icon, - deflt, - NULL, /* del */ - NULL, /* inst */ - NULL, /* update */ - NULL, /* edit */ - choose_enter, - NULL, /* destroy */ - NULL, /* popup_names */ - NULL);/* get_icon */ - - return r; -} - - -PyObject *choose2_find(const char *title); -int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon); -void choose2_refresh(PyObject *self); -void choose2_close(PyObject *self); -int choose2_create(PyObject *self, bool embedded); -void choose2_activate(PyObject *self); -PyObject *choose2_get_embedded(PyObject *self); -PyObject *choose2_get_embedded_selection(PyObject *self); - - -#define DECLARE_FORM_ACTIONS form_actions_t *fa = (form_actions_t *)p_fa; - -//--------------------------------------------------------------------------- -static bool textctrl_info_t_assign(PyObject *self, PyObject *other) -{ - textctrl_info_t *lhs = textctrl_info_t_get_clink(self); - textctrl_info_t *rhs = textctrl_info_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -static bool textctrl_info_t_set_text(PyObject *self, const char *s) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - if ( ti == NULL ) - return false; - ti->text = s; - return true; -} - -//------------------------------------------------------------------------- -static const char *textctrl_info_t_get_text(PyObject *self) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - return ti == NULL ? "" : ti->text.c_str(); -} - -//------------------------------------------------------------------------- -static bool textctrl_info_t_set_flags(PyObject *self, unsigned int flags) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - if ( ti == NULL ) - return false; - ti->flags = flags; - return true; -} - -//------------------------------------------------------------------------- -static unsigned int textctrl_info_t_get_flags( - PyObject *self, - unsigned int flags) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - return ti == NULL ? 0 : ti->flags; -} - -//------------------------------------------------------------------------- -static bool textctrl_info_t_set_tabsize( - PyObject *self, - unsigned int tabsize) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - if ( ti == NULL ) - return false; - ti->tabsize = tabsize; - return true; -} - -//------------------------------------------------------------------------- -static unsigned int textctrl_info_t_get_tabsize( - PyObject *self, - unsigned int tabsize) -{ - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); - return ti == NULL ? 0 : ti->tabsize; -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_enable_field(size_t p_fa, int fid, bool enable) -{ - DECLARE_FORM_ACTIONS; - return fa->enable_field(fid, enable); -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_show_field(size_t p_fa, int fid, bool show) -{ - DECLARE_FORM_ACTIONS; - return fa->show_field(fid, show); -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_move_field( - size_t p_fa, - int fid, - int x, - int y, - int w, - int h) -{ - DECLARE_FORM_ACTIONS; - return fa->move_field(fid, x, y, w, h); -} - -//--------------------------------------------------------------------------- -static int formchgcbfa_get_focused_field(size_t p_fa) -{ - DECLARE_FORM_ACTIONS; - return fa->get_focused_field(); -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_set_focused_field(size_t p_fa, int fid) -{ - DECLARE_FORM_ACTIONS; - return fa->set_focused_field(fid); -} - -//--------------------------------------------------------------------------- -static void formchgcbfa_refresh_field(size_t p_fa, int fid) -{ - DECLARE_FORM_ACTIONS; - return fa->refresh_field(fid); -} - -//--------------------------------------------------------------------------- -static void formchgcbfa_close(size_t p_fa, int close_normally) -{ - DECLARE_FORM_ACTIONS; - fa->close(close_normally); -} - -//--------------------------------------------------------------------------- -static PyObject *formchgcbfa_get_field_value( - size_t p_fa, - int fid, - int ft, - size_t sz) -{ - DECLARE_FORM_ACTIONS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - switch ( ft ) - { - // dropdown list - case 8: - { - // Readonly? Then return the selected index - if ( sz == 1 ) - { - int sel_idx; - if ( fa->get_combobox_value(fid, &sel_idx) ) - return PyLong_FromLong(sel_idx); - } - // Not readonly? Then return the qstring - else - { - qstring val; - if ( fa->get_combobox_value(fid, &val) ) - return PyString_FromString(val.c_str()); - } - break; - } - // multilinetext - tuple representing textctrl_info_t - case 7: - { - textctrl_info_t ti; - if ( fa->get_text_value(fid, &ti) ) - return Py_BuildValue("(sII)", ti.text.c_str(), ti.flags, ti.tabsize); - break; - } - // button - uint32 - case 4: - { - uval_t val; - if ( fa->get_unsigned_value(fid, &val) ) - return PyLong_FromUnsignedLong(val); - break; - } - // ushort - case 2: - { - ushort val; - if ( fa->_get_field_value(fid, &val) ) - return PyLong_FromUnsignedLong(val); - break; - } - // string label - case 1: - { - char val[MAXSTR]; - if ( fa->get_ascii_value(fid, val, sizeof(val)) ) - return PyString_FromString(val); - break; - } - // string input - case 3: - { - qstring val; - val.resize(sz + 1); - if ( fa->get_ascii_value(fid, val.begin(), val.size()) ) - return PyString_FromString(val.begin()); - break; - } - case 5: - { - intvec_t intvec; - // Returned as 1-base - if (fa->get_chooser_value(fid, &intvec)) - { - // Make 0-based - for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) - (*it)--; - ref_t l(PyW_IntVecToPyList(intvec)); - l.incref(); - return l.o; - } - break; - } - // Numeric control - case 6: - { - union - { - sel_t sel; - sval_t sval; - uval_t uval; - ulonglong ull; - } u; - switch ( sz ) - { - case 'S': // sel_t - { - if ( fa->get_segment_value(fid, &u.sel) ) - return Py_BuildValue(PY_FMT64, u.sel); - break; - } - // sval_t - case 'n': - case 'D': - case 'O': - case 'Y': - case 'H': - { - if ( fa->get_signed_value(fid, &u.sval) ) - return Py_BuildValue(PY_SFMT64, u.sval); - break; - } - case 'L': // uint64 - case 'l': // int64 - { - if ( fa->_get_field_value(fid, &u.ull) ) - return Py_BuildValue("K", u.ull); - break; - } - case 'N': - case 'M': // uval_t - { - if ( fa->get_unsigned_value(fid, &u.uval) ) - return Py_BuildValue(PY_FMT64, u.uval); - break; - } - case '$': // ea_t - { - if ( fa->get_ea_value(fid, &u.uval) ) - return Py_BuildValue(PY_FMT64, u.uval); - break; - } - } - break; - } - } - Py_RETURN_NONE; -} - -//--------------------------------------------------------------------------- -static bool formchgcbfa_set_field_value( - size_t p_fa, - int fid, - int ft, - PyObject *py_val) -{ - DECLARE_FORM_ACTIONS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - - switch ( ft ) - { - // dropdown list - case 8: - { - // Editable dropdown list - if ( PyString_Check(py_val) ) - { - qstring val(PyString_AsString(py_val)); - return fa->set_combobox_value(fid, &val); - } - // Readonly dropdown list - else - { - int sel_idx = PyLong_AsLong(py_val); - return fa->set_combobox_value(fid, &sel_idx); - } - break; - } - // multilinetext - textctrl_info_t - case 7: - { - textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(py_val); - return ti == NULL ? false : fa->set_text_value(fid, ti); - } - // button - uint32 - case 4: - { - uval_t val = PyLong_AsUnsignedLong(py_val); - return fa->set_unsigned_value(fid, &val); - } - // ushort - case 2: - { - ushort val = PyLong_AsUnsignedLong(py_val) & 0xffff; - return fa->_set_field_value(fid, &val); - } - // strings - case 3: - case 1: - return fa->set_ascii_value(fid, PyString_AsString(py_val)); - // intvec_t - case 5: - { - intvec_t intvec; - // Passed as 0-based - if ( !PyW_PyListToIntVec(py_val, intvec) ) - break; - - // Make 1-based - for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) - (*it)++; - - return fa->set_chooser_value(fid, &intvec); - } - // Numeric - case 6: - { - uint64 num; - if ( PyW_GetNumber(py_val, &num) ) - return fa->_set_field_value(fid, &num); - } - } - return false; -} - -#undef DECLARE_FORM_ACTIONS - -static size_t py_get_AskUsingForm() -{ - // Return a pointer to the function. Note that, although - // the C implementation of AskUsingForm_cv will do some - // Qt/txt widgets generation, the Python's ctypes - // implementation through which the call well go will first - // unblock other threads. No need to do it ourselves. - return (size_t)AskUsingForm_c; -} - -static size_t py_get_OpenForm() -{ - // See comments above. - return (size_t)OpenForm_c; -} - -// -%} - -%{ -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -int idaapi UI_Callback(void *ud, int notification_code, va_list va) -{ - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - UI_Hooks *proxy = (UI_Hooks *)ud; - int ret = 0; - try - { - switch (notification_code) - { - case ui_preprocess: - { - const char *name = va_arg(va, const char *); - return proxy->preprocess(name); - } - - case ui_postprocess: - proxy->postprocess(); - break; - - case ui_saving: - proxy->saving(); - break; - - case ui_saved: - proxy->saved(); - break; - - case ui_term: - proxy->term(); - break; - - case ui_get_ea_hint: - { - ea_t ea = va_arg(va, ea_t); - char *buf = va_arg(va, char *); - size_t sz = va_arg(va, size_t); - char *_buf; - Py_ssize_t _len; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyObject *py_str = proxy->get_ea_hint(ea); - if ( py_str != NULL - && PyString_Check(py_str) - && PyString_AsStringAndSize(py_str, &_buf, &_len) != - 1 ) - { - qstrncpy(buf, _buf, qmin(_len, sz)); - ret = 1; - } - break; - } - - case ui_current_tform_changed: - { - TForm *form = va_arg(va, TForm *); - TForm *prev_form = va_arg(va, TForm *); - proxy->current_tform_changed(form, prev_form); - } - break; - - case ui_updating_actions: - { - action_update_ctx_t *ctx = va_arg(va, action_update_ctx_t *); - proxy->updating_actions(ctx); - } - break; - - - case ui_updated_actions: - { - proxy->updated_actions(); - } - break; - - case ui_populating_tform_popup: - { - TForm *form = va_arg(va, TForm *); - TPopupMenu *popup = va_arg(va, TPopupMenu *); - proxy->populating_tform_popup(form, popup); - } - break; - - case ui_finish_populating_tform_popup: - { - TForm *form = va_arg(va, TForm *); - TPopupMenu *popup = va_arg(va, TPopupMenu *); - proxy->finish_populating_tform_popup(form, popup); - } - break; - } - } - catch (Swig::DirectorException &e) - { - msg("Exception in UI Hook function: %s\n", e.getMessage()); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyErr_Occurred() ) - PyErr_Print(); - } - return ret; -} - -//------------------------------------------------------------------------ -bool idaapi py_menu_item_callback(void *userdata) -{ - PYW_GIL_GET; - - // userdata is a tuple of ( func, args ) - // func and args are borrowed references from userdata - PyObject *func = PyTuple_GET_ITEM(userdata, 0); - PyObject *args = PyTuple_GET_ITEM(userdata, 1); - - // Call the python function - newref_t result(PyEval_CallObject(func, args)); - - // We cannot raise an exception in the callback, just print it. - if ( result == NULL ) - { - PyErr_Print(); - return false; - } - - return PyObject_IsTrue(result.o) != 0; -} - - - -//------------------------------------------------------------------------ -// Some defines -#define POPUP_NAMES_COUNT 4 -#define MAX_CHOOSER_MENU_COMMANDS 20 -#define thisobj ((py_choose2_t *) obj) -#define thisdecl py_choose2_t *_this = thisobj -#define MENU_COMMAND_CB(id) \ - static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) \ - { \ - return thisobj->on_command(id, int(n)); \ - } - -//------------------------------------------------------------------------ -// Helper functions -class py_choose2_t; -typedef std::map pychoose2_to_choose2_map_t; -static pychoose2_to_choose2_map_t choosers; - -py_choose2_t *choose2_find_instance(PyObject *self) -{ - pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - return it == choosers.end() ? NULL : it->second; -} - -void choose2_add_instance(PyObject *self, py_choose2_t *c2) -{ - choosers[self] = c2; -} - -void choose2_del_instance(PyObject *self) -{ - pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - if ( it != choosers.end() ) - choosers.erase(it); -} - -//------------------------------------------------------------------------ -class py_choose2_t -{ -private: - enum - { - CHOOSE2_HAVE_DEL = 0x0001, - CHOOSE2_HAVE_INS = 0x0002, - CHOOSE2_HAVE_UPDATE = 0x0004, - CHOOSE2_HAVE_EDIT = 0x0008, - CHOOSE2_HAVE_ENTER = 0x0010, - CHOOSE2_HAVE_GETICON = 0x0020, - CHOOSE2_HAVE_GETATTR = 0x0040, - CHOOSE2_HAVE_COMMAND = 0x0080, - CHOOSE2_HAVE_ONCLOSE = 0x0100, - CHOOSE2_HAVE_SELECT = 0x0200, - CHOOSE2_HAVE_REFRESHED = 0x0400, - }; - // Chooser flags - int flags; - - // Callback flags (to tell which callback exists and which not) - // One of CHOOSE2_HAVE_xxxx - unsigned int cb_flags; - chooser_info_t *embedded; - intvec_t embedded_sel; - - // Menu callback index (in the menu_cbs array) - int menu_cb_idx; - - // Chooser title - qstring title; - - // Column widths - intvec_t widths; - - // Python object link - PyObject *self; - // Chooser columns - qstrvec_t cols; - const char **popup_names; - bool ui_cb_hooked; - - // The number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value - MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) - MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) - MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) - MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) - MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) - MENU_COMMAND_CB(10) MENU_COMMAND_CB(11) - MENU_COMMAND_CB(12) MENU_COMMAND_CB(13) - MENU_COMMAND_CB(14) MENU_COMMAND_CB(15) - MENU_COMMAND_CB(16) MENU_COMMAND_CB(17) - MENU_COMMAND_CB(18) MENU_COMMAND_CB(19) - static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; - - //------------------------------------------------------------------------ - // Static methods to dispatch to member functions - //------------------------------------------------------------------------ - static int idaapi ui_cb(void *obj, int notification_code, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - // UI callback to handle chooser items with attributes - if ( notification_code != ui_get_chooser_item_attrs ) - return 0; - - // Pass events that belong to our chooser only - void *chooser_obj = va_arg(va, void *); - if ( obj != chooser_obj ) - return 0; - - int n = int(va_arg(va, uint32)); - chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); - thisobj->on_get_line_attr(n, attr); - return 1; - } - - static void idaapi s_select(void *obj, const intvec_t &sel) - { - thisobj->on_select(sel); - } - - static void idaapi s_refreshed(void *obj) - { - thisobj->on_refreshed(); - } - - static uint32 idaapi s_sizer(void *obj) - { - return (uint32)thisobj->on_get_size(); - } - - static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) - { - thisobj->on_get_line(int(n), arrptr); - } - - static uint32 idaapi s_del(void *obj, uint32 n) - { - return uint32(thisobj->on_delete_line(int(n))); - } - - static void idaapi s_ins(void *obj) - { - thisobj->on_insert_line(); - } - - static uint32 idaapi s_update(void *obj, uint32 n) - { - return uint32(thisobj->on_refresh(int(n))); - } - - static void idaapi s_edit(void *obj, uint32 n) - { - thisobj->on_edit_line(int(n)); - } - - static void idaapi s_enter(void * obj, uint32 n) - { - thisobj->on_enter(int(n)); - } - - static int idaapi s_get_icon(void *obj, uint32 n) - { - return thisobj->on_get_icon(int(n)); - } - - static void idaapi s_destroy(void *obj) - { - thisobj->on_close(); - } - - //------------------------------------------------------------------------ - // Member functions corresponding to each chooser2() callback - //------------------------------------------------------------------------ - void clear_popup_names() - { - if ( popup_names == NULL ) - return; - - for ( int i=0; i=0; i-- ) - line_arr[i][0] = '\0'; - - // Call Python - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1)); - if ( list == NULL ) - return; - - // Go over the List returned by Python and convert to C strings - for ( int i=ncols-1; i>=0; i-- ) - { - borref_t item(PyList_GetItem(list.o, Py_ssize_t(i))); - if ( item == NULL ) - continue; - - const char *str = PyString_AsString(item.o); - if ( str != NULL ) - qstrncpy(line_arr[i], str, MAXSTR); - } - } - - size_t on_get_size() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL)); - if ( pyres == NULL ) - return 0; - - return PyInt_AsLong(pyres.o); - } - - void on_refreshed() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL)); - } - - void on_select(const intvec_t &intvec) - { - PYW_GIL_GET; - ref_t py_list(PyW_IntVecToPyList(intvec)); - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list.o)); - } - - void on_close() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL)); - - // Delete this instance if none modal and not embedded - if ( !is_modal() && get_embedded() == NULL ) - delete this; - } - - int on_delete_line(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_DELETE_LINE, - "i", - IS_CHOOSER_EVENT(lineno) ? lineno : lineno-1)); - return pyres == NULL ? 1 : PyInt_AsLong(pyres.o); - } - - int on_refresh(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_REFRESH, - "i", - lineno - 1)); - return pyres == NULL ? lineno : PyInt_AsLong(pyres.o) + 1; - } - - void on_insert_line() - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL)); - } - - void on_enter(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_SELECT_LINE, - "i", - lineno - 1)); - } - - void on_edit_line(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_EDIT_LINE, - "i", - lineno - 1)); - } - - int on_command(int cmd_id, int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_COMMAND, - "ii", - lineno - 1, - cmd_id)); - return pyres == NULL ? lineno : PyInt_AsLong(pyres.o); - } - - int on_get_icon(int lineno) - { - PYW_GIL_GET; - newref_t pyres( - PyObject_CallMethod( - self, - (char *)S_ON_GET_ICON, - "i", - lineno - 1)); - return PyInt_AsLong(pyres.o); - } - - void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) - { - PYW_GIL_GET; - newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1)); - if ( pyres != NULL ) - { - if ( PyList_Check(pyres.o) ) - { - PyObject *item; - if ( (item = PyList_GetItem(pyres.o, 0)) != NULL ) - attr->color = PyInt_AsLong(item); - if ( (item = PyList_GetItem(pyres.o, 1)) != NULL ) - attr->flags = PyInt_AsLong(item); - } - } - } - - bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const - { - if ( get_embedded() != NULL ) - { - // For embedded chooser, the "caption" will be overloaded to encode - // the AskUsingForm's title, caption and embedded chooser id - // Title:EmbeddedChooserID:Caption - - char title_buf[MAXSTR]; - const char *ptitle; - - static const char delimiter[] = ":"; - char temp[MAXSTR]; - qstrncpy(temp, caption, sizeof(temp)); - - char *ctx; - char *p = qstrtok(temp, delimiter, &ctx); - if ( p == NULL ) - return false; - - // Copy the title - char title_str[MAXSTR]; - qstrncpy(title_str, p, sizeof(title_str)); - - // Copy the echooser ID - p = qstrtok(NULL, delimiter, &ctx); - if ( p == NULL ) - return false; - - char id_str[10]; - qstrncpy(id_str, p, sizeof(id_str)); - - // Form the new title of the form: "AskUsingFormTitle:EchooserId" - qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str); - - // Adjust the title - *out_title = title_buf; - - // Adjust the caption - p = qstrtok(NULL, delimiter, &ctx); - *out_caption = caption + (p - temp); - } - else - { - *out_title = title; - *out_caption = caption; - } - return true; - } - -public: - //------------------------------------------------------------------------ - // Public methods - //------------------------------------------------------------------------ - py_choose2_t(): flags(0), cb_flags(0), - embedded(NULL), menu_cb_idx(0), - self(NULL), popup_names(NULL), ui_cb_hooked(false) - { - } - - ~py_choose2_t() - { - // Remove from list - choose2_del_instance(self); - - // Uninstall hooks - install_hooks(false); - - delete embedded; - Py_XDECREF(self); - clear_popup_names(); - } - - static py_choose2_t *find_chooser(const char *title) - { - return (py_choose2_t *) get_chooser_obj(title); - } - - void close() - { - // Will trigger on_close() - close_chooser(title.c_str()); - } - - bool activate() - { - TForm *frm = find_tform(title.c_str()); - if ( frm == NULL ) - return false; - - switchto_tform(frm, true); - return true; - } - - int add_command( - const char *_caption, - int flags=0, - int menu_index=-1, - int icon=-1) - { - if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) - return -1; - - qstring title, caption; - if ( !split_chooser_caption(&title, &caption, _caption) - || !add_chooser_command( - title.c_str(), - caption.c_str(), - menu_cbs[menu_cb_idx], - menu_index, - icon, - flags) ) - return -1; - - return menu_cb_idx++; - } - - // Create a chooser. - // If it detects the "embedded" attribute, then it will create a chooser_info_t structure - // Otherwise the chooser window is created and displayed - int create(PyObject *self) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Get flags - ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS)); - if ( flags_attr == NULL ) - return -1; - flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0; - - // Get the title - if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) - return -1; - - // Get columns - ref_t cols_attr(PyW_TryGetAttrString(self, "cols")); - if ( cols_attr == NULL ) - return -1; - - // Get col count - int ncols = int(PyList_Size(cols_attr.o)); - - // Get cols caption and widthes - cols.qclear(); - for ( int i=0; iself = self; - - // Hook to notification point (to handle chooser item attributes) - install_hooks(true); - - // Check if *embedded - ref_t emb_attr(PyW_TryGetAttrString(self, S_EMBEDDED)); - if ( emb_attr != NULL && PyObject_IsTrue(emb_attr.o) == 1 ) - { - // Create an embedded chooser structure - embedded = new chooser_info_t(); - embedded->obj = this; - embedded->cb = sizeof(chooser_info_t); - embedded->title = title.c_str(); - embedded->columns = ncols; - embedded->deflt = deflt; - embedded->flags = flags; - embedded->width = pts[0]; // Take x1 - embedded->height = pts[1]; // Take y1 - embedded->icon = icon; - embedded->popup_names = popup_names; - embedded->widths = widths.begin(); - embedded->destroyer = s_destroy; - embedded->getl = s_getl; - embedded->sizer = s_sizer; - embedded->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL; - embedded->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL; - embedded->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL; - embedded->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL; - embedded->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL; - embedded->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL; - embedded->get_attrs = NULL; - // Fill callbacks that are only present in idaq - if ( is_idaq() ) - { - embedded->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL; - embedded->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL; - } - else - { - embedded->select = NULL; - embedded->refresh = NULL; - } - } - - // Create the chooser (if not embedded) - int r; - if ( embedded == NULL ) - { - r = ::choose2( - flags, - pts[0], pts[1], pts[2], pts[3], - this, - ncols, - &widths[0], - s_sizer, - s_getl, - title.c_str(), - icon, - deflt, - (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL, - (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL, - (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL, - (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL, - (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL, - s_destroy, - popup_names, - (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL); - - clear_popup_names(); - - // Modal chooser return the index of the selected item - if ( is_modal() ) - r--; - } - // Embedded chooser? - else - { - // Return success - r = 1; - } - - return r; - } - - inline PyObject *get_self() - { - return self; - } - - void refresh() - { - refresh_chooser(title.c_str()); - } - - bool is_modal() - { - return (flags & CH_MODAL) != 0; - } - - intvec_t *get_sel_vec() - { - return &embedded_sel; - } - - chooser_info_t *get_embedded() const - { - return embedded; - } -}; - -//------------------------------------------------------------------------ -// Initialize the callback pointers -#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id -chooser_cb_t *py_choose2_t::menu_cbs[MAX_CHOOSER_MENU_COMMANDS] = -{ - DECL_MENU_COMMAND_CB(0), DECL_MENU_COMMAND_CB(1), - DECL_MENU_COMMAND_CB(2), DECL_MENU_COMMAND_CB(3), - DECL_MENU_COMMAND_CB(4), DECL_MENU_COMMAND_CB(5), - DECL_MENU_COMMAND_CB(6), DECL_MENU_COMMAND_CB(7), - DECL_MENU_COMMAND_CB(8), DECL_MENU_COMMAND_CB(9), - DECL_MENU_COMMAND_CB(10), DECL_MENU_COMMAND_CB(11), - DECL_MENU_COMMAND_CB(12), DECL_MENU_COMMAND_CB(13), - DECL_MENU_COMMAND_CB(14), DECL_MENU_COMMAND_CB(15), - DECL_MENU_COMMAND_CB(16), DECL_MENU_COMMAND_CB(17), - DECL_MENU_COMMAND_CB(18), DECL_MENU_COMMAND_CB(19) -}; -#undef DECL_MENU_COMMAND_CB - -#undef POPUP_NAMES_COUNT -#undef MAX_CHOOSER_MENU_COMMANDS -#undef thisobj -#undef thisdecl -#undef MENU_COMMAND_CB - -//------------------------------------------------------------------------ -int choose2_create(PyObject *self, bool embedded) -{ - py_choose2_t *c2; - - c2 = choose2_find_instance(self); - if ( c2 != NULL ) - { - if ( !embedded ) - c2->activate(); - return 1; - } - - c2 = new py_choose2_t(); - - choose2_add_instance(self, c2); - - int r = c2->create(self); - // Non embedded chooser? Return immediately - if ( !embedded ) - return r; - - // Embedded chooser was not created? - if ( c2->get_embedded() == NULL || r != 1 ) - { - delete c2; - r = 0; - } - return r; -} - -//------------------------------------------------------------------------ -void choose2_close(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 == NULL ) - return; - - // Modal or embedded chooser? - if ( c2->get_embedded() != NULL || c2->is_modal() ) - { - // Then simply delete the instance - delete c2; - } - else - { - // Close the chooser. - // In turn this will lead to the deletion of the object - c2->close(); - } -} - -//------------------------------------------------------------------------ -void choose2_refresh(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->refresh(); -} - -//------------------------------------------------------------------------ -void choose2_activate(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->activate(); -} - -//------------------------------------------------------------------------ -PyObject *choose2_get_embedded_selection(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - py_choose2_t *c2 = choose2_find_instance(self); - chooser_info_t *embedded; - - if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) - Py_RETURN_NONE; - - // Returned as 1-based - intvec_t &intvec = *c2->get_sel_vec(); - - // Make 0-based - for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) - (*it)--; - - ref_t ret(PyW_IntVecToPyList(intvec)); - ret.incref(); - return ret.o; -} - -//------------------------------------------------------------------------ -// Return the C instances as 64bit numbers -PyObject *choose2_get_embedded(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - py_choose2_t *c2 = choose2_find_instance(self); - chooser_info_t *embedded; - - if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) - Py_RETURN_NONE; - else - return Py_BuildValue("(KK)", - PY_ULONG_LONG(embedded), - PY_ULONG_LONG(c2->get_sel_vec())); -} - -//------------------------------------------------------------------------ -int choose2_add_command( - PyObject *self, - const char *caption, - int flags=0, - int menu_index=-1, - int icon=-1) -{ - py_choose2_t *c2 = choose2_find_instance(self); - return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon); -} - -//------------------------------------------------------------------------ -PyObject *choose2_find(const char *title) -{ - py_choose2_t *c2 = py_choose2_t::find_chooser(title); - return c2 == NULL ? NULL : c2->get_self(); -} - - -// - -%} - -%{ -// -//-------------------------------------------------------------------------- -#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, - int *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, int *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) - { - PYW_GIL_GET; - newref_t result( - PyObject_CallMethod( - self, - (char *)S_ON_EXECUTE_LINE, - "s", - line)); - PyW_ShowCbErr(S_ON_EXECUTE_LINE); - return result != NULL && PyObject_IsTrue(result.o); - } - - //-------------------------------------------------------------------------- - // 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, - int *vk_key, - int shift) - { - PYW_GIL_GET; - newref_t 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.o); - - PyW_ShowCbErr(S_ON_KEYDOWN); - - if ( ok ) - { - Py_ssize_t sz = PyTuple_Size(result.o); - PyObject *item; - -#define GET_TUPLE_ENTRY(col, PyThingy, AsThingy, out) \ - do \ - { \ - if ( sz > col ) \ - { \ - borref_t _r(PyTuple_GetItem(result.o, col)); \ - if ( _r != NULL && PyThingy##_Check(_r.o) ) \ - *out = PyThingy##_##AsThingy(_r.o); \ - } \ - } while ( false ) - - GET_TUPLE_ENTRY(0, PyString, AsString, line); - GET_TUPLE_ENTRY(1, PyInt, AsLong, p_x); - GET_TUPLE_ENTRY(2, PyInt, AsLong, p_sellen); - GET_TUPLE_ENTRY(3, PyInt, AsLong, vk_key); - *vk_key &= 0xffff; -#undef GET_TUPLE_ENTRY - } - 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) - { - PYW_GIL_GET; - newref_t result( - PyObject_CallMethod( - self, - (char *)S_ON_COMPLETE_LINE, - "sisi", - prefix, - n, - line, - x)); - - bool ok = result != NULL && PyString_Check(result.o); - PyW_ShowCbErr(S_ON_COMPLETE_LINE); - if ( ok ) - *completion = PyString_AsString(result.o); - return ok; - } - - // Private ctor (use bind()) - py_cli_t() - { - } - -public: - //--------------------------------------------------------------------------- - static int bind(PyObject *py_obj) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - 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' - { - ref_t flags_attr(PyW_TryGetAttrString(py_obj, S_FLAGS)); - if ( flags_attr == NULL ) - py_cli->cli.flags = 0; - else - py_cli->cli.flags = PyLong_AsLong(flags_attr.o); - } - - // 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); - - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - 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 -// - -// -//--------------------------------------------------------------------------- -class plgform_t -{ -private: - ref_t py_obj; - TForm *form; - - static int idaapi s_callback(void *ud, int notification_code, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - - plgform_t *_this = (plgform_t *)ud; - if ( notification_code == ui_tform_visible ) - { - TForm *form = va_arg(va, TForm *); - if ( form == _this->form ) - { - // Qt: QWidget* - // G: HWND - // We wrap and pass as a CObject in the hope that a Python UI framework - // can unwrap a CObject and get the hwnd/widget back - newref_t py_result( - PyObject_CallMethod( - _this->py_obj.o, - (char *)S_ON_CREATE, "O", - PyCObject_FromVoidPtr(form, NULL))); - PyW_ShowCbErr(S_ON_CREATE); - } - } - else if ( notification_code == ui_tform_invisible ) - { - TForm *form = va_arg(va, TForm *); - if ( form == _this->form ) - { - { - newref_t py_result( - PyObject_CallMethod( - _this->py_obj.o, - (char *)S_ON_CLOSE, "O", - PyCObject_FromVoidPtr(form, NULL))); - PyW_ShowCbErr(S_ON_CLOSE); - } - _this->unhook(); - } - } - return 0; - } - - void unhook() - { - unhook_from_notification_point(HT_UI, s_callback, this); - form = NULL; - - // Call DECREF at last, since it may trigger __del__ - PYW_GIL_CHECK_LOCKED_SCOPE(); - py_obj = ref_t(); - } - -public: - plgform_t(): form(NULL) - { - } - - bool show( - PyObject *obj, - const char *caption, - int options) - { - // Already displayed? - TForm *f = find_tform(caption); - if ( f != NULL ) - { - // Our form? - if ( f == form ) - { - // Switch to it - switchto_tform(form, true); - return true; - } - // Fail to create - return false; - } - - // Create a form - form = create_tform(caption, NULL); - if ( form == NULL ) - return false; - - if ( !hook_to_notification_point(HT_UI, s_callback, this) ) - { - form = NULL; - return false; - } - - py_obj = borref_t(obj); - - if ( is_idaq() ) - options |= FORM_QWIDGET; - - this->form = form; - open_tform(form, options); - return true; - } - - void close(int options = 0) - { - if ( form != NULL ) - close_tform(form, options); - } - - static PyObject *create() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new plgform_t(), destroy); - } - - static void destroy(void *obj) - { - delete (plgform_t *)obj; - } -}; -// -%} - -%inline %{ -// -//--------------------------------------------------------------------------- -#define DECL_PLGFORM PYW_GIL_CHECK_LOCKED_SCOPE(); plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); -static PyObject *plgform_new() -{ - return plgform_t::create(); -} - -static bool plgform_show( - PyObject *py_link, - PyObject *py_obj, - const char *caption, - int options = FORM_TAB|FORM_MENU|FORM_RESTORE) -{ - DECL_PLGFORM; - return plgform->show(py_obj, caption, options); -} - -static void plgform_close( - PyObject *py_link, - int options) -{ - DECL_PLGFORM; - plgform->close(options); -} -#undef DECL_PLGFORM -// -%} - -%{ -// -//--------------------------------------------------------------------------- -// Base class for all custviewer place_t providers -class custviewer_data_t -{ -public: - virtual void *get_ud() = 0; - virtual place_t *get_min() = 0; - virtual place_t *get_max() = 0; -}; - -//--------------------------------------------------------------------------- -class cvdata_simpleline_t: public custviewer_data_t -{ -private: - strvec_t lines; - simpleline_place_t pl_min, pl_max; -public: - - void *get_ud() - { - return &lines; - } - - place_t *get_min() - { - return &pl_min; - } - - place_t *get_max() - { - return &pl_max; - } - - strvec_t &get_lines() - { - return lines; - } - - void set_minmax(size_t start=0, size_t end=size_t(-1)) - { - if ( start == 0 && end == size_t(-1) ) - { - end = lines.size(); - pl_min.n = 0; - pl_max.n = end == 0 ? 0 : end - 1; - } - else - { - pl_min.n = start; - pl_max.n = end; - } - } - - bool set_line(size_t nline, simpleline_t &sl) - { - if ( nline >= lines.size() ) - return false; - lines[nline] = sl; - return true; - } - - bool del_line(size_t nline) - { - if ( nline >= lines.size() ) - return false; - lines.erase(lines.begin()+nline); - return true; - } - - void add_line(simpleline_t &line) - { - lines.push_back(line); - } - - void add_line(const char *str) - { - lines.push_back(simpleline_t(str)); - } - - bool insert_line(size_t nline, simpleline_t &line) - { - if ( nline >= lines.size() ) - return false; - lines.insert(lines.begin()+nline, line); - return true; - } - - bool patch_line(size_t nline, size_t offs, int value) - { - if ( nline >= lines.size() ) - return false; - qstring &L = lines[nline].line; - L[offs] = (uchar) value & 0xFF; - return true; - } - - const size_t to_lineno(place_t *pl) const - { - return ((simpleline_place_t *)pl)->n; - } - - bool curline(place_t *pl, size_t *n) - { - if ( pl == NULL ) - return false; - - *n = to_lineno(pl); - return true; - } - - simpleline_t *get_line(size_t nline) - { - return nline >= lines.size() ? NULL : &lines[nline]; - } - - simpleline_t *get_line(place_t *pl) - { - return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); - } - - const size_t count() const - { - return lines.size(); - } - - void clear_lines() - { - lines.clear(); - set_minmax(); - } -}; - -//--------------------------------------------------------------------------- -// FIXME: This should inherit py_view_base.hpp's py_customidamemo_t, -// just like py_graph.hpp's py_graph_t does. -// There should be a way to "merge" the two mechanisms; they are similar. -class customviewer_t -{ -protected: - qstring _title; - TForm *_form; - TCustomControl *_cv; - custviewer_data_t *_data; - int _features; - enum - { - HAVE_HINT = 0x0001, - HAVE_KEYDOWN = 0x0002, - HAVE_POPUP = 0x0004, - HAVE_DBLCLICK = 0x0008, - HAVE_CURPOS = 0x0010, - HAVE_CLICK = 0x0020, - HAVE_CLOSE = 0x0040 - }; -private: - struct cvw_popupctx_t - { - size_t menu_id; - customviewer_t *cv; - cvw_popupctx_t(): menu_id(0), cv(NULL) { } - cvw_popupctx_t(size_t mid, customviewer_t *v): menu_id(mid), cv(v) { } - }; - typedef std::map cvw_popupmap_t; - static cvw_popupmap_t _global_popup_map; - static size_t _global_popup_id; - qstring _curline; - intvec_t _installed_popups; - - static bool idaapi s_popup_menu_cb(void *ud) - { - size_t mid = (size_t)ud; - cvw_popupmap_t::iterator it = _global_popup_map.find(mid); - if ( it == _global_popup_map.end() ) - return false; - - PYW_GIL_GET; - return it->second.cv->on_popup_menu(it->second.menu_id); - } - - static bool idaapi s_cv_keydown( - TCustomControl * /*cv*/, - int vk_key, - int shift, - void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_keydown(vk_key, shift); - } - - // The popup menu is being constructed - static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - _this->on_popup(); - } - - // The user clicked - static bool idaapi s_cv_click(TCustomControl * /*cv*/, int shift, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_click(shift); - } - - // The user double clicked - static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_dblclick(shift); - } - - // Cursor position has been changed - static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) - { - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - _this->on_curpos_changed(); - } - - //-------------------------------------------------------------------------- - static int idaapi s_ui_cb(void *ud, int code, va_list va) - { - // This hook gets called from the kernel. Ensure we hold the GIL. - PYW_GIL_GET; - customviewer_t *_this = (customviewer_t *)ud; - switch ( code ) - { - case ui_get_custom_viewer_hint: - { - TCustomControl *viewer = va_arg(va, TCustomControl *); - place_t *place = va_arg(va, place_t *); - int *important_lines = va_arg(va, int *); - qstring &hint = *va_arg(va, qstring *); - if ( (_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer ) - return 0; - else - return _this->on_hint(place, important_lines, hint) ? 1 : 0; - } - - case ui_tform_invisible: - { - TForm *form = va_arg(va, TForm *); - if ( _this->_form != form ) - break; - } - // fallthrough... - case ui_term: - unhook_from_notification_point(HT_UI, s_ui_cb, _this); - _this->on_close(); - _this->on_post_close(); - break; - } - - return 0; - } - - void on_post_close() - { - init_vars(); - clear_popup_menu(); - } - -public: - - inline TForm *get_tform() { return _form; } - inline TCustomControl *get_tcustom_control() { return _cv; } - - // - // All the overridable callbacks - // - - // OnClick - virtual bool on_click(int /*shift*/) { return false; } - - // OnDblClick - virtual bool on_dblclick(int /*shift*/) { return false; } - - // OnCurorPositionChanged - virtual void on_curpos_changed() { } - - // OnHostFormClose - virtual void on_close() { } - - // OnKeyDown - virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } - - // OnPopupShow - virtual bool on_popup() { return false; } - - // OnHint - virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } - - // OnPopupMenuClick - virtual bool on_popup_menu(size_t menu_id) { return false; } - - void init_vars() - { - _data = NULL; - _features = 0; - _curline.clear(); - _cv = NULL; - _form = NULL; - } - - customviewer_t() - { - init_vars(); - } - - ~customviewer_t() - { - } - - //-------------------------------------------------------------------------- - void close() - { - if ( _form != NULL ) - close_tform(_form, FORM_SAVE | FORM_CLOSE_LATER); - } - - //-------------------------------------------------------------------------- - bool set_range( - const place_t *minplace = NULL, - const place_t *maxplace = NULL) - { - if ( _cv == NULL ) - return false; - - set_custom_viewer_range( - _cv, - minplace == NULL ? _data->get_min() : minplace, - maxplace == NULL ? _data->get_max() : maxplace); - return true; - } - - place_t *get_place( - bool mouse = false, - int *x = 0, - int *y = 0) - { - return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); - } - - //-------------------------------------------------------------------------- - bool refresh() - { - if ( _cv == NULL ) - return false; - - refresh_custom_viewer(_cv); - return true; - } - - //-------------------------------------------------------------------------- - bool refresh_current() - { - int x, y; - place_t *pl = get_place(false, &x, &y); - if ( pl == NULL ) - return false; - - return jumpto(pl, x, y); - } - - //-------------------------------------------------------------------------- - bool get_current_word(bool mouse, qstring &word) - { - // query the cursor position - int x, y; - if ( get_place(mouse, &x, &y) == NULL ) - return false; - - // query the line at the cursor - const char *line = get_current_line(mouse, true); - if ( line == NULL ) - return false; - - if ( x >= (int)strlen(line) ) - return false; - - // find the beginning of the word - const char *ptr = line + x; - while ( ptr > line && !qisspace(ptr[-1]) ) - ptr--; - - // find the end of the word - const char *begin = ptr; - ptr = line + x; - while ( !qisspace(*ptr) && *ptr != '\0' ) - ptr++; - - word.qclear(); - word.append(begin, ptr-begin); - return true; - } - - //-------------------------------------------------------------------------- - const char *get_current_line(bool mouse, bool notags) - { - const char *r = get_custom_viewer_curline(_cv, mouse); - if ( r == NULL || !notags ) - return r; - - size_t sz = strlen(r); - if ( sz == 0 ) - return r; - - _curline.resize(sz + 5, '\0'); - tag_remove(r, &_curline[0], sz + 1); - return _curline.c_str(); - } - - //-------------------------------------------------------------------------- - bool is_focused() - { - return get_current_viewer() == _cv; - } - - //-------------------------------------------------------------------------- - bool jumpto(place_t *place, int x, int y) - { - return ::jumpto(_cv, place, x, y); - } - - //-------------------------------------------------------------------------- - void clear_popup_menu() - { - if ( _cv != NULL ) - set_custom_viewer_popup_menu(_cv, NULL); - - for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); - it != it_end; - ++it) - { - _global_popup_map.erase(*it); - } - _installed_popups.clear(); - } - - //-------------------------------------------------------------------------- - size_t add_popup_menu( - const char *title, - const char *hotkey) - { - size_t menu_id = _global_popup_id + 1; - - // Overlap / already exists? - if (_cv == NULL || // No custviewer? - // Overlap? - menu_id == 0 || - // Already exists? - _global_popup_map.find(menu_id) != _global_popup_map.end()) - { - return 0; - } - add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); - - // Save global association - _global_popup_map[menu_id] = cvw_popupctx_t(menu_id, this); - _global_popup_id = menu_id; - - // Remember what menu IDs are set with this form - _installed_popups.push_back(menu_id); - return menu_id; - } - - //-------------------------------------------------------------------------- - bool create(const char *title, int features, custviewer_data_t *data) - { - // Already created? (in the instance) - if ( _form != NULL ) - return true; - - // Already created? (in IDA windows list) - HWND hwnd(NULL); - TForm *form = create_tform(title, &hwnd); - if ( hwnd == NULL ) - return false; - - _title = title; - _data = data; - _form = form; - _features = features; - - // Create the viewer - _cv = create_custom_viewer( - title, - (TWinControl *)_form, - _data->get_min(), - _data->get_max(), - _data->get_min(), - 0, - _data->get_ud()); - - // Set user-data - set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); - - // - // Set other optional callbacks - // - if ( (features & HAVE_KEYDOWN) != 0 ) - set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); - - if ( (features & HAVE_POPUP) != 0 ) - set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); - - if ( (features & HAVE_DBLCLICK) != 0 ) - set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); - - if ( (features & HAVE_CURPOS) != 0 ) - set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); - - if ( (features & HAVE_CLICK) != 0 ) - set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); - - // Hook to UI notifications (for TForm close event) - hook_to_notification_point(HT_UI, s_ui_cb, this); - - return true; - } - - //-------------------------------------------------------------------------- - bool show() - { - // Closed already? - if ( _form == NULL ) - return false; - - open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE|FORM_QWIDGET); - return true; - } -}; - -customviewer_t::cvw_popupmap_t customviewer_t::_global_popup_map; -size_t customviewer_t::_global_popup_id = 0; -//--------------------------------------------------------------------------- -class py_simplecustview_t: public customviewer_t -{ -private: - cvdata_simpleline_t data; - PyObject *py_self, *py_this, *py_last_link; - int features; - - //-------------------------------------------------------------------------- - // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t - static bool py_to_simpleline(PyObject *py, simpleline_t &sl) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( PyString_Check(py) ) - { - sl.line = PyString_AsString(py); - return true; - } - Py_ssize_t sz; - if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) - return false; - - PyObject *py_val = PyTuple_GetItem(py, 0); - if ( !PyString_Check(py_val) ) - return false; - - sl.line = PyString_AsString(py_val); - - if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) - sl.color = color_t(PyLong_AsUnsignedLong(py_val)); - - if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) - sl.bgcolor = PyLong_AsUnsignedLong(py_val); - - return true; - } - - // - // Callbacks - // - virtual bool on_click(int shift) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift)); - PyW_ShowCbErr(S_ON_CLICK); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - // OnDblClick - virtual bool on_dblclick(int shift) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift)); - PyW_ShowCbErr(S_ON_DBL_CLICK); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - // OnCurorPositionChanged - virtual void on_curpos_changed() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL)); - PyW_ShowCbErr(S_ON_CURSOR_POS_CHANGED); - } - - //-------------------------------------------------------------------------- - // OnHostFormClose - virtual void on_close() - { - // Call the close method if it is there and the object is still bound - if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL)); - PyW_ShowCbErr(S_ON_CLOSE); - - // Cleanup - Py_DECREF(py_self); - py_self = NULL; - } - } - - //-------------------------------------------------------------------------- - // OnKeyDown - virtual bool on_keydown(int vk_key, int shift) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_KEYDOWN, - "ii", - vk_key, - shift)); - - PyW_ShowCbErr(S_ON_KEYDOWN); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- -// OnPopupShow - virtual bool on_popup() - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP, - NULL)); - PyW_ShowCbErr(S_ON_POPUP); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - // OnHint - virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) - { - size_t ln = data.to_lineno(place); - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_HINT, - PY_FMT64, - pyul_t(ln))); - - PyW_ShowCbErr(S_ON_HINT); - bool ok = py_result != NULL && PyTuple_Check(py_result.o) && PyTuple_Size(py_result.o) == 2; - if ( ok ) - { - if ( important_lines != NULL ) - *important_lines = PyInt_AsLong(PyTuple_GetItem(py_result.o, 0)); - hint = PyString_AsString(PyTuple_GetItem(py_result.o, 1)); - } - return ok; - } - - //-------------------------------------------------------------------------- - // OnPopupMenuClick - virtual bool on_popup_menu(size_t menu_id) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_result( - PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP_MENU, - PY_FMT64, - pyul_t(menu_id))); - PyW_ShowCbErr(S_ON_POPUP_MENU); - return py_result != NULL && PyObject_IsTrue(py_result.o); - } - - //-------------------------------------------------------------------------- - void refresh_range() - { - data.set_minmax(); - set_range(); - } - -public: - py_simplecustview_t() - { - py_this = py_self = py_last_link = NULL; - } - ~py_simplecustview_t() - { - } - - //-------------------------------------------------------------------------- - // Edits an existing line - bool edit_line(size_t nline, PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - - return data.set_line(nline, sl); - } - - // Low level: patches a line string directly - bool patch_line(size_t nline, size_t offs, int value) - { - return data.patch_line(nline, offs, value); - } - - // Insert a line - bool insert_line(size_t nline, PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - return data.insert_line(nline, sl); - } - - // Adds a line tuple - bool add_line(PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - data.add_line(sl); - refresh_range(); - return true; - } - - //-------------------------------------------------------------------------- - bool del_line(size_t nline) - { - bool ok = data.del_line(nline); - if ( ok ) - refresh_range(); - return ok; - } - - //-------------------------------------------------------------------------- - // Gets the position and returns a tuple (lineno, x, y) - PyObject *get_pos(bool mouse) - { - place_t *pl; - int x, y; - pl = get_place(mouse, &x, &y); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( pl == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); - } - - //-------------------------------------------------------------------------- - // Returns the line tuple - PyObject *get_line(size_t nline) - { - simpleline_t *r = data.get_line(nline); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( r == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); - } - - // Returns the count of lines - const size_t count() const - { - return data.count(); - } - - // Clears lines - void clear() - { - data.clear_lines(); - refresh_range(); - } - - //-------------------------------------------------------------------------- - bool jumpto(size_t ln, int x, int y) - { - simpleline_place_t l(ln); - return customviewer_t::jumpto(&l, x, y); - } - - //-------------------------------------------------------------------------- - // Initializes and links the Python object to this class - bool init(PyObject *py_link, const char *title) - { - // Already created? - if ( _form != NULL ) - return true; - - // Probe callbacks - features = 0; - static struct - { - const char *cb_name; - int feature; - } const cbtable[] = - { - {S_ON_CLICK, HAVE_CLICK}, - {S_ON_CLOSE, HAVE_CLOSE}, - {S_ON_HINT, HAVE_HINT}, - {S_ON_KEYDOWN, HAVE_KEYDOWN}, - {S_ON_POPUP, HAVE_POPUP}, - {S_ON_DBL_CLICK, HAVE_DBLCLICK}, - {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} - }; - - PYW_GIL_CHECK_LOCKED_SCOPE(); - for ( size_t i=0; i -%} - -%inline %{ -// -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); -} -// - -// -// -// Pywraps Simple Custom Viewer functions -// -PyObject *pyscv_init(PyObject *py_link, const char *title) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - py_simplecustview_t *_this = new py_simplecustview_t(); - bool ok = _this->init(py_link, title); - if ( !ok ) - { - delete _this; - Py_RETURN_NONE; - } - return _this->get_pythis(); -} -#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) - -//-------------------------------------------------------------------------- -bool pyscv_refresh(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->refresh(); -} - -//-------------------------------------------------------------------------- -bool pyscv_delete(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - _this->close(); - delete _this; - return true; -} - -//-------------------------------------------------------------------------- -bool pyscv_refresh_current(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->refresh_current(); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) -{ - DECL_THIS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - const char *line; - if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) - Py_RETURN_NONE; - return PyString_FromString(line); -} - -//-------------------------------------------------------------------------- -bool pyscv_is_focused(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->is_focused(); -} - -void pyscv_clear_popup_menu(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->clear_popup_menu(); -} - -size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) -{ - DECL_THIS; - return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); -} - -size_t pyscv_count(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? 0 : _this->count(); -} - -bool pyscv_show(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? false : _this->show(); -} - -void pyscv_close(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->close(); -} - -bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->jumpto(ln, x, y); -} - -// Returns the line tuple -PyObject *pyscv_get_line(PyObject *py_this, size_t nline) -{ - DECL_THIS; - if ( _this == NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } - return _this->get_line(nline); -} - -//-------------------------------------------------------------------------- -// Gets the position and returns a tuple (lineno, x, y) -PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) -{ - DECL_THIS; - if ( _this == NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } - return _this->get_pos(mouse); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_clear_lines(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->clear(); - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; -} - -//-------------------------------------------------------------------------- -// Adds a line tuple -bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->add_line(py_sl); -} - -//-------------------------------------------------------------------------- -bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->insert_line(nline, py_sl); -} - -//-------------------------------------------------------------------------- -bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) -{ - DECL_THIS; - return _this == NULL ? false : _this->patch_line(nline, offs, value); -} - -//-------------------------------------------------------------------------- -bool pyscv_del_line(PyObject *py_this, size_t nline) -{ - DECL_THIS; - return _this == NULL ? false : _this->del_line(nline); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_get_selection(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_RETURN_NONE; - } - return _this->py_get_selection(); -} - -//-------------------------------------------------------------------------- -PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) -{ - DECL_THIS; - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( _this != NULL ) - { - qstring word; - if ( _this->get_current_word(mouse, word) ) - return PyString_FromString(word.c_str()); - } - Py_RETURN_NONE; -} - -//-------------------------------------------------------------------------- -// Edits an existing line -bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->edit_line(nline, py_sl); -} - -//------------------------------------------------------------------------- -TForm *pyscv_get_tform(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? NULL : _this->get_tform(); -} - -//------------------------------------------------------------------------- -TCustomControl *pyscv_get_tcustom_control(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? NULL : _this->get_tcustom_control(); -} - - -#undef DECL_THIS -// -%} - -%include "kernwin.hpp" - -%template(disasm_text_t) qvector; - -%extend action_desc_t { - action_desc_t( - const char *name, - const char *label, - PyObject *handler, - const char *shortcut = NULL, - const char *tooltip = NULL, - int icon = -1) - { - action_desc_t *ad = new action_desc_t(); -#define DUPSTR(Prop) ad->Prop = Prop == NULL ? NULL : qstrdup(Prop) - DUPSTR(name); - DUPSTR(label); - DUPSTR(shortcut); - DUPSTR(tooltip); -#undef DUPSTR - ad->icon = icon; - ad->handler = new py_action_handler_t(handler); - ad->owner = &PLUGIN; - return ad; - } - - ~action_desc_t() - { - if ( $self->handler != NULL ) // Ownership not taken? - delete $self->handler; -#define FREESTR(Prop) qfree((char *) $self->Prop) - FREESTR(name); - FREESTR(label); - FREESTR(shortcut); - FREESTR(tooltip); -#undef FREESTR - delete $self; - } -} - -//------------------------------------------------------------------------- -uint32 choose_choose(PyObject *self, - int flags, - int x0,int y0, - int x1,int y1, - int width, - int deflt, - int icon); - -%extend place_t { - static idaplace_t *as_idaplace_t(place_t *p) { return (idaplace_t *) p; } - static enumplace_t *as_enumplace_t(place_t *p) { return (enumplace_t *) p; } - static structplace_t *as_structplace_t(place_t *p) { return (structplace_t *) p; } - static simpleline_place_t *as_simpleline_place_t(place_t *p) { return (simpleline_place_t *) p; } -} - -%extend twinpos_t { - - %pythoncode { - def place_as_idaplace_t(self): - return place_t.as_idaplace_t(self.at) - def place_as_enumplace_t(self): - return place_t.as_enumplace_t(self.at) - def place_as_structplace_t(self): - return place_t.as_structplace_t(self.at) - def place_as_simpleline_place_t(self): - return place_t.as_simpleline_place_t(self.at) - - def place(self, view): - ptype = get_viewer_place_type(view) - if ptype == TCCPT_IDAPLACE: - return self.place_as_idaplace_t() - elif ptype == TCCPT_ENUMPLACE: - return self.place_as_enumplace_t() - elif ptype == TCCPT_STRUCTPLACE: - return self.place_as_structplace_t() - elif ptype == TCCPT_SIMPLELINE_PLACE: - return self.place_as_simpleline_place_t() - else: - return self.at - } -} - -%pythoncode %{ - -# -DP_LEFT = 0x0001 -DP_TOP = 0x0002 -DP_RIGHT = 0x0004 -DP_BOTTOM = 0x0008 -DP_INSIDE = 0x0010 -# if not before, then it is after -# (use DP_INSIDE | DP_BEFORE to insert a tab before a given tab) -# this flag alone cannot be used to determine orientation -DP_BEFORE = 0x0020 -# used with combination of other flags -DP_TAB = 0x0040 -DP_FLOATING = 0x0080 - -# ---------------------------------------------------------------------- -def load_custom_icon(file_name=None, data=None, format=None): - """ - Loads a custom icon and returns an identifier that can be used with other APIs - - If file_name is passed then the other two arguments are ignored. - - @param file_name: The icon file name - @param data: The icon data - @param format: The icon data format - - @return: Icon id or 0 on failure. - Use free_custom_icon() to free it - """ - if file_name is not None: - return _idaapi.py_load_custom_icon_fn(file_name) - elif not (data is None and format is None): - return _idaapi.py_load_custom_icon_data(data, format) - else: - return 0 - -# ---------------------------------------------------------------------- -def asklong(defval, format): - res, val = _idaapi._asklong(defval, format) - - if res == 1: - return val - else: - return None - -# ---------------------------------------------------------------------- -def askaddr(defval, format): - res, ea = _idaapi._askaddr(defval, format) - - if res == 1: - return ea - else: - return None - -# ---------------------------------------------------------------------- -def askseg(defval, format): - res, sel = _idaapi._askseg(defval, format) - - if res == 1: - return sel - else: - return None - -# ---------------------------------------------------------------------- -class action_handler_t: - def __init__(self): - pass - - def activate(self, ctx): - return 0 - - def update(self, ctx): - pass - - - -class Choose2(object): - """ - Choose2 wrapper class. - - Some constants are defined in this class. Please refer to kernwin.hpp for more information. - """ - - CH_MODAL = 0x01 - """Modal chooser""" - - CH_MULTI = 0x02 - """Allow multi selection""" - - CH_MULTI_EDIT = 0x04 - CH_NOBTNS = 0x08 - CH_ATTRS = 0x10 - CH_NOIDB = 0x20 - """use the chooser even without an open database, same as x0=-2""" - CH_UTF8 = 0x40 - """string encoding is utf-8""" - - CH_BUILTIN_MASK = 0xF80000 - - # column flags (are specified in the widths array) - CHCOL_PLAIN = 0x00000000 - CHCOL_PATH = 0x00010000 - CHCOL_HEX = 0x00020000 - CHCOL_DEC = 0x00030000 - CHCOL_FORMAT = 0x00070000 - - - def __init__(self, title, cols, flags=0, popup_names=None, - icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, - embedded=False, width=None, height=None): - """ - Constructs a chooser window. - @param title: The chooser title - @param cols: a list of colums; each list item is a list of two items - example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ] - @param flags: One of CH_XXXX constants - @param deflt: Default starting item - @param popup_names: list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] - @param icon: Icon index (the icon should exist in ida resources or an index to a custom loaded icon) - @param x1, y1, x2, y2: The default location - @param embedded: Create as embedded chooser - @param width: Embedded chooser width - @param height: Embedded chooser height - """ - self.title = title - self.flags = flags - self.cols = cols - self.deflt = deflt - self.popup_names = popup_names - self.icon = icon - self.x1 = x1 - self.y1 = y1 - self.x2 = x2 - self.y2 = y2 - self.embedded = embedded - if embedded: - self.x1 = width - self.y1 = height - - - def Embedded(self): - """ - Creates an embedded chooser (as opposed to Show()) - @return: Returns 1 on success - """ - return _idaapi.choose2_create(self, True) - - - def GetEmbSelection(self): - """ - Returns the selection associated with an embedded chooser - - @return: - - None if chooser is not embedded - - A list with selection indices (0-based) - """ - return _idaapi.choose2_get_embedded_selection(self) - - - def Show(self, modal=False): - """ - Activates or creates a chooser window - @param modal: Display as modal dialog - @return: For modal choosers it will return the selected item index (0-based) - """ - if modal: - self.flags |= Choose2.CH_MODAL - - # Disable the timeout - old = _idaapi.set_script_timeout(0) - n = _idaapi.choose2_create(self, False) - _idaapi.set_script_timeout(old) - - # Delete the modal chooser instance - self.Close() - - return n - else: - self.flags &= ~Choose2.CH_MODAL - return _idaapi.choose2_create(self, False) - - - def Activate(self): - """Activates a visible chooser""" - return _idaapi.choose2_activate(self) - - - def Refresh(self): - """Causes the refresh callback to trigger""" - return _idaapi.choose2_refresh(self) - - - def Close(self): - """Closes the chooser""" - return _idaapi.choose2_close(self) - - - def AddCommand(self, - caption, - flags = _idaapi.CHOOSER_POPUP_MENU, - menu_index = -1, - icon = -1, - emb=None): - """ - Deprecated: Use - - register_action() - - attach_action_to_menu() - - attach_action_to_popup() - """ - # Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl - if self.embedded and ((emb is None) or (emb != 2002)): - raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()") - return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) - - # - # Implement these methods in the subclass: - # -# -# def OnClose(self): -# """ -# Called when the window is being closed. -# This callback is mandatory. -# @return: nothing -# """ -# pass -# -# def OnGetLine(self, n): -# """Called when the chooser window requires lines. -# This callback is mandatory. -# @param n: Line number (0-based) -# @return: The user should return a list with ncols elements. -# example: a list [col1, col2, col3, ...] describing the n-th line -# """ -# return ["col1 val", "col2 val"] -# -# def OnGetSize(self): -# """Returns the element count. -# This callback is mandatory. -# @return: Number of elements -# """ -# return len(self.the_list) -# -# def OnEditLine(self, n): -# """ -# Called when an item is being edited. -# @param n: Line number (0-based) -# @return: Nothing -# """ -# pass -# -# def OnInsertLine(self): -# """ -# Called when 'Insert' is selected either via the hotkey or popup menu. -# @return: Nothing -# """ -# pass -# -# def OnSelectLine(self, n): -# """ -# Called when a line is selected and then Ok or double click was pressed -# @param n: Line number (0-based) -# """ -# pass -# -# def OnSelectionChange(self, sel_list): -# """ -# Called when the selection changes -# @param sel_list: A list of selected item indices -# """ -# pass -# -# def OnDeleteLine(self, n): -# """ -# Called when a line is about to be deleted -# @param n: Line number (0-based) -# """ -# return self.n -# -# def OnRefresh(self, n): -# """ -# Triggered when the 'Refresh' is called from the popup menu item. -# -# @param n: The currently selected line (0-based) at the time of the refresh call -# @return: Return the number of elements -# """ -# return self.n -# -# def OnRefreshed(self): -# """ -# Triggered when a refresh happens (for example due to column sorting) -# @param n: Line number (0-based) -# @return: Return the number of elements -# """ -# return self.n -# -# def OnCommand(self, n, cmd_id): -# """Return int ; check add_chooser_command()""" -# return 0 -# -# def OnGetIcon(self, n): -# """ -# Return icon number for a given item (or -1 if no icon is avail) -# @param n: Line number (0-based) -# """ -# return -1 -# -# def OnGetLineAttr(self, n): -# """ -# Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t -# @param n: Line number (0-based) -# """ -# return [0x0, CHITEM_BOLD] -# - - -#ICON WARNING|QUESTION|INFO|NONE -#AUTOHIDE NONE|DATABASE|REGISTRY|SESSION -#HIDECANCEL -#BUTTON YES|NO|CANCEL "Value|NONE" -#STARTITEM {id:ItemName} -#HELP / ENDHELP -try: - import types - from ctypes import * - # On Windows, we use stdcall - - # Callback for buttons - # typedef void (idaapi *formcb_t)(TView *fields[], int code); - - _FORMCB_T = WINFUNCTYPE(None, c_void_p, c_int) - - # Callback for form change - # typedef int (idaapi *formchgcb_t)(int field_id, form_actions_t &fa); - _FORMCHGCB_T = WINFUNCTYPE(c_int, c_int, c_void_p) -except: - try: - _FORMCB_T = CFUNCTYPE(None, c_void_p, c_int) - _FORMCHGCB_T = CFUNCTYPE(c_int, c_int, c_void_p) - except: - _FORMCHGCB_T = _FORMCB_T = None - - -# ----------------------------------------------------------------------- -# textctrl_info_t clinked object -class textctrl_info_t(py_clinked_object_t): - """Class representing textctrl_info_t""" - - # Some constants - TXTF_AUTOINDENT = 0x0001 - """Auto-indent on new line""" - TXTF_ACCEPTTABS = 0x0002 - """Tab key inserts 'tabsize' spaces""" - TXTF_READONLY = 0x0004 - """Text cannot be edited (but can be selected and copied)""" - TXTF_SELECTED = 0x0008 - """Shows the field with its text selected""" - TXTF_MODIFIED = 0x0010 - """Gets/sets the modified status""" - TXTF_FIXEDFONT = 0x0020 - """The control uses IDA's fixed font""" - - def __init__(self, text="", flags=0, tabsize=0): - py_clinked_object_t.__init__(self) - if text: - self.text = text - if flags: - self.flags = flags - if tabsize: - self.tabsize = tabsize - - def _create_clink(self): - return _idaapi.textctrl_info_t_create() - - def _del_clink(self, lnk): - return _idaapi.textctrl_info_t_destroy(lnk) - - def _get_clink_ptr(self): - return _idaapi.textctrl_info_t_get_clink_ptr(self) - - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.textctrl_info_t_assign(self, other) - - def __set_text(self, s): - """Sets the text value""" - return _idaapi.textctrl_info_t_set_text(self, s) - - def __get_text(self): - """Sets the text value""" - return _idaapi.textctrl_info_t_get_text(self) - - def __set_flags__(self, flags): - """Sets the flags value""" - return _idaapi.textctrl_info_t_set_flags(self, flags) - - def __get_flags__(self): - """Returns the flags value""" - return _idaapi.textctrl_info_t_get_flags(self) - - def __set_tabsize__(self, tabsize): - """Sets the tabsize value""" - return _idaapi.textctrl_info_t_set_tabsize(self, tabsize) - - def __get_tabsize__(self): - """Returns the tabsize value""" - return _idaapi.textctrl_info_t_get_tabsize(self) - - value = property(__get_text, __set_text) - """Alias for the text property""" - text = property(__get_text, __set_text) - """Text value""" - flags = property(__get_flags__, __set_flags__) - """Flags value""" - tabsize = property(__get_tabsize__, __set_tabsize__) - -# ----------------------------------------------------------------------- -class Form(object): - - FT_ASCII = 'A' - """Ascii string - char *""" - FT_SEG = 'S' - """Segment - sel_t *""" - FT_HEX = 'N' - """Hex number - uval_t *""" - FT_SHEX = 'n' - """Signed hex number - sval_t *""" - FT_COLOR = 'K' - """Color button - bgcolor_t *""" - FT_ADDR = '$' - """Address - ea_t *""" - FT_UINT64 = 'L' - """default base uint64 - uint64""" - FT_INT64 = 'l' - """default base int64 - int64""" - FT_RAWHEX = 'M' - """Hex number, no 0x prefix - uval_t *""" - FT_FILE = 'f' - """File browse - char * at least QMAXPATH""" - FT_DEC = 'D' - """Decimal number - sval_t *""" - FT_OCT = 'O' - """Octal number, C notation - sval_t *""" - FT_BIN = 'Y' - """Binary number, 0b prefix - sval_t *""" - FT_CHAR = 'H' - """Char value -- sval_t *""" - FT_IDENT = 'I' - """Identifier - char * at least MAXNAMELEN""" - FT_BUTTON = 'B' - """Button - def handler(code)""" - FT_DIR = 'F' - """Path to directory - char * at least QMAXPATH""" - FT_TYPE = 'T' - """Type declaration - char * at least MAXSTR""" - _FT_USHORT = '_US' - """Unsigned short""" - FT_FORMCHG = '%/' - """Form change callback - formchgcb_t""" - FT_ECHOOSER = 'E' - """Embedded chooser - idaapi.Choose2""" - FT_MULTI_LINE_TEXT = 't' - """Multi text control - textctrl_info_t""" - FT_DROPDOWN_LIST = 'b' - """Dropdown list control - Form.DropdownControl""" - - FT_CHKGRP = 'C' - FT_CHKGRP2= 'c' - FT_RADGRP = 'R' - FT_RADGRP2= 'r' - - @staticmethod - def fieldtype_to_ctype(tp, i64 = False): - """ - Factory method returning a ctype class corresponding to the field type string - """ - if tp in (Form.FT_SEG, Form.FT_HEX, Form.FT_RAWHEX, Form.FT_ADDR): - return c_ulonglong if i64 else c_ulong - elif tp in (Form.FT_SHEX, Form.FT_DEC, Form.FT_OCT, Form.FT_BIN, Form.FT_CHAR): - return c_longlong if i64 else c_long - elif tp == Form.FT_UINT64: - return c_ulonglong - elif tp == Form.FT_INT64: - return c_longlong - elif tp == Form.FT_COLOR: - return c_ulong - elif tp == Form._FT_USHORT: - return c_ushort - elif tp in (Form.FT_FORMCHG, Form.FT_ECHOOSER): - return c_void_p - else: - return None - - - # - # Generic argument helper classes - # - class NumericArgument(object): - """ - Argument representing various integer arguments (ushort, uint32, uint64, etc...) - @param tp: One of Form.FT_XXX - """ - DefI64 = False - def __init__(self, tp, value): - cls = Form.fieldtype_to_ctype(tp, self.DefI64) - if cls is None: - raise TypeError("Invalid numeric field type: %s" % tp) - # Get a pointer type to the ctype type - self.arg = pointer(cls(value)) - - def __set_value(self, v): - self.arg.contents.value = v - value = property(lambda self: self.arg.contents.value, __set_value) - - - class StringArgument(object): - """ - Argument representing a character buffer - """ - def __init__(self, size=None, value=None): - if size is None: - raise SyntaxError("The string size must be passed") - - if value is None: - self.arg = create_string_buffer(size) - else: - self.arg = create_string_buffer(value, size) - self.size = size - - def __set_value(self, v): - self.arg.value = v - value = property(lambda self: self.arg.value, __set_value) - - - # - # Base control class - # - class Control(object): - def __init__(self): - self.id = 0 - """Automatically assigned control ID""" - - self.arg = None - """Control argument value. This could be one element or a list/tuple (for multiple args per control)""" - - self.form = None - """Reference to the parent form. It is filled by Form.Add()""" - - - def get_tag(self): - """ - Control tag character. One of Form.FT_XXXX. - The form class will expand the {} notation and replace them with the tags - """ - pass - - def get_arg(self): - """ - Control returns the parameter to be pushed on the stack - (Of AskUsingForm()) - """ - return self.arg - - def free(self): - """ - Free the control - """ - # Release the parent form reference - self.form = None - - - # - # Label controls - # - class LabelControl(Control): - """ - Base class for static label control - """ - def __init__(self, tp): - Form.Control.__init__(self) - self.tp = tp - - def get_tag(self): - return '%%%d%s' % (self.id, self.tp) - - - class StringLabel(LabelControl): - """ - String label control - """ - def __init__(self, value, tp=None, sz=1024): - """ - Type field can be one of: - A - ascii string - T - type declaration - I - ident - F - folder - f - file - X - command - """ - if tp is None: - tp = Form.FT_ASCII - Form.LabelControl.__init__(self, tp) - self.size = sz - self.arg = create_string_buffer(value, sz) - - - class NumericLabel(LabelControl, NumericArgument): - """ - Numeric label control - """ - def __init__(self, value, tp=None): - if tp is None: - tp = Form.FT_HEX - Form.LabelControl.__init__(self, tp) - Form.NumericArgument.__init__(self, tp, value) - - - # - # Group controls - # - class GroupItemControl(Control): - """ - Base class for group control items - """ - def __init__(self, tag, parent): - Form.Control.__init__(self) - self.tag = tag - self.parent = parent - # Item position (filled when form is compiled) - self.pos = 0 - - def assign_pos(self): - self.pos = self.parent.next_child_pos() - - def get_tag(self): - return "%s%d" % (self.tag, self.id) - - - class ChkGroupItemControl(GroupItemControl): - """ - Checkbox group item control - """ - def __init__(self, tag, parent): - Form.GroupItemControl.__init__(self, tag, parent) - - def __get_value(self): - return (self.parent.value & (1 << self.pos)) != 0 - - def __set_value(self, v): - pv = self.parent.value - if v: - pv = pv | (1 << self.pos) - else: - pv = pv & ~(1 << self.pos) - - self.parent.value = pv - - checked = property(__get_value, __set_value) - """Get/Sets checkbox item check status""" - - - class RadGroupItemControl(GroupItemControl): - """ - Radiobox group item control - """ - def __init__(self, tag, parent): - Form.GroupItemControl.__init__(self, tag, parent) - - def __get_value(self): - return self.parent.value == self.pos - - def __set_value(self, v): - self.parent.value = self.pos - - selected = property(__get_value, __set_value) - """Get/Sets radiobox item selection status""" - - - class GroupControl(Control, NumericArgument): - """ - Base class for group controls - """ - def __init__(self, children_names, tag, value=0): - Form.Control.__init__(self) - self.children_names = children_names - self.tag = tag - self._reset() - Form.NumericArgument.__init__(self, Form._FT_USHORT, value) - - def _reset(self): - self.childpos = 0 - - def next_child_pos(self): - v = self.childpos - self.childpos += 1 - return v - - def get_tag(self): - return "%d" % self.id - - - class ChkGroupControl(GroupControl): - """ - Checkbox group control class. - It holds a set of checkbox controls - """ - ItemClass = None - """ - Group control item factory class instance - We need this because later we won't be treating ChkGroupControl or RadGroupControl - individually, instead we will be working with GroupControl in general. - """ - def __init__(self, children_names, value=0, secondary=False): - # Assign group item factory class - if Form.ChkGroupControl.ItemClass is None: - Form.ChkGroupControl.ItemClass = Form.ChkGroupItemControl - - Form.GroupControl.__init__( - self, - children_names, - Form.FT_CHKGRP2 if secondary else Form.FT_CHKGRP, - value) - - - class RadGroupControl(GroupControl): - """ - Radiobox group control class. - It holds a set of radiobox controls - """ - ItemClass = None - def __init__(self, children_names, value=0, secondary=False): - """ - Creates a radiogroup control. - @param children_names: A tuple containing group item names - @param value: Initial selected radio item - @param secondory: Allows rendering one the same line as the previous group control. - Use this if you have another group control on the same line. - """ - # Assign group item factory class - if Form.RadGroupControl.ItemClass is None: - Form.RadGroupControl.ItemClass = Form.RadGroupItemControl - - Form.GroupControl.__init__( - self, - children_names, - Form.FT_RADGRP2 if secondary else Form.FT_RADGRP, - value) - - - # - # Input controls - # - class InputControl(Control): - """ - Generic form input control. - It could be numeric control, string control, directory/file browsing, etc... - """ - def __init__(self, tp, width, swidth, hlp = None): - """ - @param width: Display width - @param swidth: String width - """ - Form.Control.__init__(self) - self.tp = tp - self.width = width - self.switdh = swidth - self.hlp = hlp - - def get_tag(self): - return "%s%d:%s:%s:%s" % ( - self.tp, self.id, - self.width, - self.switdh, - ":" if self.hlp is None else self.hlp) - - - class NumericInput(InputControl, NumericArgument): - """ - A composite class serving as a base numeric input control class - """ - def __init__(self, tp=None, value=0, width=50, swidth=10, hlp=None): - if tp is None: - tp = Form.FT_HEX - Form.InputControl.__init__(self, tp, width, swidth, hlp) - Form.NumericArgument.__init__(self, self.tp, value) - - - class ColorInput(NumericInput): - """ - Color button input control - """ - def __init__(self, value = 0): - """ - @param value: Initial color value in RGB - """ - Form.NumericInput.__init__(self, tp=Form.FT_COLOR, value=value) - - - class StringInput(InputControl, StringArgument): - """ - Base string input control class. - This class also constructs a StringArgument - """ - def __init__(self, - tp=None, - width=1024, - swidth=40, - hlp=None, - value=None, - size=None): - """ - @param width: String size. But in some cases it has special meaning. For example in FileInput control. - If you want to define the string buffer size then pass the 'size' argument - @param swidth: Control width - @param value: Initial value - @param size: String size - """ - if tp is None: - tp = Form.FT_ASCII - if not size: - size = width - Form.InputControl.__init__(self, tp, width, swidth, hlp) - Form.StringArgument.__init__(self, size=size, value=value) - - - class FileInput(StringInput): - """ - File Open/Save input control - """ - def __init__(self, - width=512, - swidth=80, - save=False, open=False, - hlp=None, value=None): - - if save == open: - raise ValueError("Invalid mode. Choose either open or save") - if width < 512: - raise ValueError("Invalid width. Must be greater than 512.") - - # The width field is overloaded in this control and is used - # to denote the type of the FileInput dialog (save or load) - # On the other hand it is passed as is to the StringArgument part - Form.StringInput.__init__( - self, - tp=Form.FT_FILE, - width="1" if save else "0", - swidth=swidth, - hlp=hlp, - size=width, - value=value) - - - class DirInput(StringInput): - """ - Directory browsing control - """ - def __init__(self, - width=512, - swidth=80, - hlp=None, - value=None): - - if width < 512: - raise ValueError("Invalid width. Must be greater than 512.") - - Form.StringInput.__init__( - self, - tp=Form.FT_DIR, - width=width, - swidth=swidth, - hlp=hlp, - size=width, - value=value) - - - class ButtonInput(InputControl): - """ - Button control. - A handler along with a 'code' (numeric value) can be associated with the button. - This way one handler can handle many buttons based on the button code (or in other terms id or tag) - """ - def __init__(self, handler, code="", swidth="", hlp=None): - """ - @param handler: Button handler. A callback taking one argument which is the code. - @param code: A code associated with the button and that is later passed to the handler. - """ - Form.InputControl.__init__( - self, - Form.FT_BUTTON, - code, - swidth, - hlp) - self.arg = _FORMCB_T(lambda view, code, h=handler: h(code)) - - - class FormChangeCb(Control): - """ - Form change handler. - This can be thought of like a dialog procedure. - Everytime a form action occurs, this handler will be called along with the control id. - The programmer can then call various form actions accordingly: - - EnableField - - ShowField - - MoveField - - GetFieldValue - - etc... - - Special control IDs: -1 (The form is initialized) and -2 (Ok has been clicked) - - """ - def __init__(self, handler): - """ - Constructs the handler. - @param handler: The handler (preferrably a member function of a class derived from the Form class). - """ - Form.Control.__init__(self) - - # Save the handler - self.handler = handler - - # Create a callback stub - # We use this mechanism to create an intermediate step - # where we can create an 'fa' adapter for use by Python - self.arg = _FORMCHGCB_T(self.helper_cb) - - def helper_cb(self, fid, p_fa): - # Remember the pointer to the forms_action in the parent form - self.form.p_fa = p_fa - - # Call user's handler - r = self.handler(fid) - return 0 if r is None else r - - def get_tag(self): - return Form.FT_FORMCHG - - def free(self): - Form.Control.free(self) - # Remove reference to the handler - # (Normally the handler is a member function in the parent form) - self.handler = None - - - class EmbeddedChooserControl(InputControl): - """ - Embedded chooser control. - This control links to a Chooser2 control created with the 'embedded=True' - """ - def __init__(self, - chooser=None, - swidth=40, - hlp=None): - """ - Embedded chooser control - - @param chooser: A chooser2 instance (must be constructed with 'embedded=True') - """ - - # !! Make sure a chooser instance is passed !! - if chooser is None or not isinstance(chooser, Choose2): - raise ValueError("Invalid chooser passed.") - - # Create an embedded chooser structure from the Choose2 instance - if chooser.Embedded() != 1: - raise ValueError("Failed to create embedded chooser instance.") - - # Construct input control - Form.InputControl.__init__(self, Form.FT_ECHOOSER, "", swidth) - - # Get a pointer to the chooser_info_t and the selection vector - # (These two parameters are the needed arguments for the AskUsingForm()) - emb, sel = _idaapi.choose2_get_embedded(chooser) - - # Get a pointer to a c_void_p constructed from an address - p_embedded = pointer(c_void_p.from_address(emb)) - p_sel = pointer(c_void_p.from_address(sel)) - - # - Create the embedded chooser info on control creation - # - Do not free the embeded chooser because after we get the args - # via Compile() the user can still call Execute() which relies - # on the already computed args - self.arg = (p_embedded, p_sel) - - # Save chooser instance - self.chooser = chooser - - # Add a bogus 'size' attribute - self.size = 0 - - - value = property(lambda self: self.chooser) - """Returns the embedded chooser instance""" - - - def AddCommand(self, - caption, - flags = _idaapi.CHOOSER_POPUP_MENU, - menu_index = -1, - icon = -1): - """ - Adds a new embedded chooser command - Save the returned value and later use it in the OnCommand handler - - @return: Returns a negative value on failure or the command index - """ - if not self.form.title: - raise ValueError("Form title is not set!") - - # Encode all information for the AddCommand() in the 'caption' parameter - caption = "%s:%d:%s" % (self.form.title, self.id, caption) - return self.chooser.AddCommand(caption, flags=flags, menu_index=menu_index, icon=icon, emb=2002) - - - def free(self): - """ - Frees the embedded chooser data - """ - self.chooser.Close() - self.chooser = None - Form.Control.free(self) - - - class DropdownListControl(InputControl, _qstrvec_t): - """ - Dropdown control - This control allows manipulating a dropdown control - """ - def __init__(self, items=[], readonly=True, selval=0, width=50, swidth=50, hlp = None): - """ - @param items: A string list of items used to prepopulate the control - @param readonly: Specifies whether the dropdown list is editable or not - @param selval: The preselected item index (when readonly) or text value (when editable) - @param width: the control width (n/a if the dropdown list is readonly) - @param swidth: string width - """ - - # Ignore the width if readonly was set - if readonly: - width = 0 - - # Init the input control base class - Form.InputControl.__init__( - self, - Form.FT_DROPDOWN_LIST, - width, - swidth, - hlp) - - # Init the associated qstrvec - _qstrvec_t.__init__(self, items) - - # Remember if readonly or not - self.readonly = readonly - - if readonly: - # Create a C integer and remember it - self.__selval = c_int(selval) - val_addr = addressof(self.__selval) - else: - # Create an strvec with one qstring - self.__selval = _qstrvec_t([selval]) - # Get address of the first element - val_addr = self.__selval.addressof(0) - - # Two arguments: - # - argument #1: a pointer to the qstrvec containing the items - # - argument #2: an integer to hold the selection - # or - # #2: a qstring to hold the dropdown text control value - self.arg = ( - pointer(c_void_p.from_address(self.clink_ptr)), - pointer(c_void_p.from_address(val_addr)) - ) - - - def __set_selval(self, val): - if self.readonly: - self.__selval.value = val - else: - self.__selval[0] = val - - def __get_selval(self): - # Return the selection index - # or the entered text value - return self.__selval.value if self.readonly else self.__selval[0] - - value = property(__get_selval, __set_selval) - selval = property(__get_selval, __set_selval) - """ - Read/write the selection value. - The value is used as an item index in readonly mode or text value in editable mode - This value can be used only after the form has been closed. - """ - - def free(self): - self._free() - - - def set_items(self, items): - """Sets the dropdown list items""" - self.from_list(items) - - - class MultiLineTextControl(InputControl, textctrl_info_t): - """ - Multi line text control. - This class inherits from textctrl_info_t. Thus the attributes are also inherited - This control allows manipulating a multilinetext control - """ - def __init__(self, text="", flags=0, tabsize=0, width=50, swidth=50, hlp = None): - """ - @param text: Initial text value - @param flags: One of textctrl_info_t.TXTF_.... values - @param tabsize: Tab size - @param width: Display width - @param swidth: String width - """ - # Init the input control base class - Form.InputControl.__init__(self, Form.FT_MULTI_LINE_TEXT, width, swidth, hlp) - - # Init the associated textctrl_info base class - textctrl_info_t.__init__(self, text=text, flags=flags, tabsize=tabsize) - - # Get the argument as a pointer from the embedded ti - self.arg = pointer(c_void_p.from_address(self.clink_ptr)) - - - def free(self): - self._free() - - - # - # Form class - # - def __init__(self, form, controls): - """ - Contruct a Form class. - This class wraps around AskUsingForm() or OpenForm() and provides an easier / alternative syntax for describing forms. - The form control names are wrapped inside the opening and closing curly braces and the control themselves are - defined and instantiated via various form controls (subclasses of Form). - - @param form: The form string - @param controls: A dictionary containing the control name as a _key_ and control object as _value_ - """ - self._reset() - self.form = form - """Form string""" - self.controls = controls - """Dictionary of controls""" - self.__args = None - - self.title = None - """The Form title. It will be filled when the form is compiled""" - - self.modal = True - """By default, forms are modal""" - - self.openform_flags = 0 - """ - If non-modal, these flags will be passed to OpenForm. - This is an OR'ed combination of the PluginForm.FORM_* values. - """ - - - def Free(self): - """ - Frees all resources associated with a compiled form. - Make sure you call this function when you finish using the form. - """ - - # Free all the controls - for ctrl in self.__controls.values(): - ctrl.free() - - # Reset the controls - # (Note that we are not removing the form control attributes, no need) - self._reset() - - - def _reset(self): - """ - Resets the Form class state variables - """ - self.__controls = {} - self.__ctrl_id = 1 - - - def __getitem__(self, name): - """Returns a control object by name""" - return self.__controls[name] - - - def Add(self, name, ctrl, mkattr = True): - """ - Low level function. Prefer AddControls() to this function. - This function adds one control to the form. - - @param name: Control name - @param ctrl: Control object - @param mkattr: Create control name / control object as a form attribute - """ - # Assign a unique ID - ctrl.id = self.__ctrl_id - self.__ctrl_id += 1 - - # Create attribute with control name - if mkattr: - setattr(self, name, ctrl) - - # Remember the control - self.__controls[name] = ctrl - - # Link the form to the control via its form attribute - ctrl.form = self - - # Is it a group? Add each child - if isinstance(ctrl, Form.GroupControl): - self._AddGroup(ctrl, mkattr) - - - def FindControlById(self, id): - """ - Finds a control instance given its id - """ - for ctrl in self.__controls.values(): - if ctrl.id == id: - return ctrl - return None - - - @staticmethod - def _ParseFormTitle(form): - """ - Parses the form's title from the form text - """ - help_state = 0 - for i, line in enumerate(form.split("\n")): - if line.startswith("STARTITEM ") or line.startswith("BUTTON "): - continue - # Skip "HELP" and remember state - elif help_state == 0 and line == "HELP": - help_state = 1 # Mark inside HELP - continue - elif help_state == 1 and line == "ENDHELP": - help_state = 2 # Mark end of HELP - continue - return line.strip() - - return None - - - def _AddGroup(self, Group, mkattr=True): - """ - Internal function. - This function expands the group item names and creates individual group item controls - - @param Group: The group class (checkbox or radio group class) - """ - - # Create group item controls for each child - for child_name in Group.children_names: - self.Add( - child_name, - # Use the class factory - Group.ItemClass(Group.tag, Group), - mkattr) - - - def AddControls(self, controls, mkattr=True): - """ - Adds controls from a dictionary. - The dictionary key is the control name and the value is a Form.Control object - @param controls: The control dictionary - """ - for name, ctrl in controls.items(): - # Add the control - self.Add(name, ctrl, mkattr) - - - def CompileEx(self, form): - """ - Low level function. - Compiles (parses the form syntax and adds the control) the form string and - returns the argument list to be passed the argument list to AskUsingForm(). - - The form controls are wrapped inside curly braces: {ControlName}. - - A special operator can be used to return the ID of a given control by its name: {id:ControlName}. - This is useful when you use the STARTITEM form keyword to set the initially focused control. - - @param form: Compiles the form and returns the arguments needed to be passed to AskUsingForm() - """ - # First argument is the form string - args = [None] - - # Second argument, if form is not modal, is the set of flags - if not self.modal: - args.append(self.openform_flags | 0x80) # Add FORM_QWIDGET - - ctrlcnt = 1 - - # Reset all group control internal flags - for ctrl in self.__controls.values(): - if isinstance(ctrl, Form.GroupControl): - ctrl._reset() - - p = 0 - while True: - i1 = form.find("{", p) - # No more items? - if i1 == -1: - break - - # Check if escaped - if (i1 != 0) and form[i1-1] == "\\": - # Remove escape sequence and restart search - form = form[:i1-1] + form[i1:] - - # Skip current marker - p = i1 - - # Continue search - continue - - i2 = form.find("}", i1) - if i2 == -1: - raise SyntaxError("No matching closing brace '}'") - - # Parse control name - ctrlname = form[i1+1:i2] - if not ctrlname: - raise ValueError("Control %d has an invalid name!" % ctrlcnt) - - # Is it the IDOF operator? - if ctrlname.startswith("id:"): - idfunc = True - # Take actual ctrlname - ctrlname = ctrlname[3:] - else: - idfunc = False - - # Find the control - ctrl = self.__controls.get(ctrlname, None) - if ctrl is None: - raise ValueError("No matching control '%s'" % ctrlname) - - # Replace control name by tag - if idfunc: - tag = str(ctrl.id) - else: - tag = ctrl.get_tag() - taglen = len(tag) - form = form[:i1] + tag + form[i2+1:] - - # Set new position - p = i1 + taglen - - # Was it an IDOF() ? No need to push parameters - # Just ID substitution is fine - if idfunc: - continue - - - # For GroupItem controls, there are no individual arguments - # The argument is assigned for the group itself - if isinstance(ctrl, Form.GroupItemControl): - # GroupItem controls will have their position dynamically set - ctrl.assign_pos() - else: - # Push argument(s) - # (Some controls need more than one argument) - arg = ctrl.get_arg() - if isinstance(arg, (types.ListType, types.TupleType)): - # Push all args - args.extend(arg) - else: - # Push one arg - args.append(arg) - - ctrlcnt += 1 - - # If no FormChangeCb instance was passed, and thus there's no '%/' - # in the resulting form string, let's provide a minimal one, so that - # we will retrieve 'p_fa', and thus actions that rely on it will work. - if form.find(Form.FT_FORMCHG) < 0: - form = form + Form.FT_FORMCHG - fccb = Form.FormChangeCb(lambda *args: 1) - self.Add("___dummyfchgcb", fccb) - # Regardless of the actual position of '%/' in the form - # string, a formchange callback _must_ be right after - # the form string. - if self.modal: - inspos = 1 - else: - inspos = 2 - args.insert(inspos, fccb.get_arg()) - - # Patch in the final form string - args[0] = form - - self.title = self._ParseFormTitle(form) - return args - - - def Compile(self): - """ - Compiles a form and returns the form object (self) and the argument list. - The form object will contain object names corresponding to the form elements - - @return: It will raise an exception on failure. Otherwise the return value is ignored - """ - - # Reset controls - self._reset() - - # Insert controls - self.AddControls(self.controls) - - # Compile form and get args - self.__args = self.CompileEx(self.form) - - return (self, self.__args) - - - def Compiled(self): - """ - Checks if the form has already been compiled - - @return: Boolean - """ - return self.__args is not None - - - def _ChkCompiled(self): - if not self.Compiled(): - raise SyntaxError("Form is not compiled") - - - def Execute(self): - """ - Displays a modal dialog containing the compiled form. - @return: 1 - ok ; 0 - cancel - """ - self._ChkCompiled() - if not self.modal: - raise SyntaxError("Form is not modal. Open() should be instead") - - return AskUsingForm(*self.__args) - - - def Open(self): - """ - Opens a widget containing the compiled form. - """ - self._ChkCompiled() - if self.modal: - raise SyntaxError("Form is modal. Execute() should be instead") - - OpenForm(*self.__args) - - - def EnableField(self, ctrl, enable): - """ - Enable or disable an input field - @return: False - no such control - """ - return _idaapi.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) - - - def ShowField(self, ctrl, show): - """ - Show or hide an input field - @return: False - no such control - """ - return _idaapi.formchgcbfa_show_field(self.p_fa, ctrl.id, show) - - - def MoveField(self, ctrl, x, y, w, h): - """ - Move/resize an input field - - @return: False - no such fiel - """ - return _idaapi.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) - - - def GetFocusedField(self): - """ - Get currently focused input field. - @return: None if no field is selected otherwise the control ID - """ - id = _idaapi.formchgcbfa_get_focused_field(self.p_fa) - return self.FindControlById(id) - - - def SetFocusedField(self, ctrl): - """ - Set currently focused input field - @return: False - no such control - """ - return _idaapi.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) - - - def RefreshField(self, ctrl): - """ - Refresh a field - @return: False - no such control - """ - return _idaapi.formchgcbfa_refresh_field(self.p_fa, ctrl.id) - - - def Close(self, close_normally): - """ - Close the form - @param close_normally: - 1: form is closed normally as if the user pressed Enter - 0: form is closed abnormally as if the user pressed Esc - @return: None - """ - return _idaapi.formchgcbfa_close(self.p_fa, close_normally) - - - def GetControlValue(self, ctrl): - """ - Returns the control's value depending on its type - @param ctrl: Form control instance - @return: - - color button, radio controls: integer - - file/dir input, string input and string label: string - - embedded chooser control (0-based indices of selected items): integer list - - for multilinetext control: textctrl_info_t - - dropdown list controls: string (when editable) or index (when readonly) - - None: on failure - """ - tid, sz = self.ControlToFieldTypeIdAndSize(ctrl) - r = _idaapi.formchgcbfa_get_field_value( - self.p_fa, - ctrl.id, - tid, - sz) - # Multilinetext? Unpack the tuple into a new textctrl_info_t instance - if r is not None and tid == 7: - return textctrl_info_t(text=r[0], flags=r[1], tabsize=r[2]) - else: - return r - - - def SetControlValue(self, ctrl, value): - """ - Set the control's value depending on its type - @param ctrl: Form control instance - @param value: - - embedded chooser: a 0-base indices list to select embedded chooser items - - multilinetext: a textctrl_info_t - - dropdown list: an integer designating the selection index if readonly - a string designating the edit control value if not readonly - @return: Boolean true on success - """ - tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) - return _idaapi.formchgcbfa_set_field_value( - self.p_fa, - ctrl.id, - tid, - value) - - - @staticmethod - def ControlToFieldTypeIdAndSize(ctrl): - """ - Converts a control object to a tuple containing the field id - and the associated buffer size - """ - # Input control depend on the associated buffer size (supplied by the user) - - # Make sure you check instances types taking into account inheritance - if isinstance(ctrl, Form.DropdownListControl): - return (8, 1 if ctrl.readonly else 0) - elif isinstance(ctrl, Form.MultiLineTextControl): - return (7, 0) - elif isinstance(ctrl, Form.EmbeddedChooserControl): - return (5, 0) - # Group items or controls - elif isinstance(ctrl, (Form.GroupItemControl, Form.GroupControl)): - return (2, 0) - elif isinstance(ctrl, Form.StringLabel): - return (3, min(_idaapi.MAXSTR, ctrl.size)) - elif isinstance(ctrl, Form.ColorInput): - return (4, 0) - elif isinstance(ctrl, Form.NumericInput): - # Pass the numeric control type - return (6, ord(ctrl.tp[0])) - elif isinstance(ctrl, Form.InputControl): - return (1, ctrl.size) - else: - raise NotImplementedError, "Not yet implemented" - -# -------------------------------------------------------------------------- -# Instantiate AskUsingForm function pointer -try: - import ctypes - # Setup the numeric argument size - Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL - AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm()) - OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_OpenForm()) -except: - def AskUsingForm__(*args): - warning("AskUsingForm() needs ctypes library in order to work") - return 0 - def OpenForm__(*args): - warning("OpenForm() needs ctypes library in order to work") - - -def AskUsingForm(*args): - """ - Calls AskUsingForm() - @param: Compiled Arguments obtain through the Form.Compile() function - @return: 1 = ok, 0 = cancel - """ - old = set_script_timeout(0) - r = AskUsingForm__(*args) - set_script_timeout(old) - return r - -def OpenForm(*args): - """ - Calls OpenForm() - @param: Compiled Arguments obtain through the Form.Compile() function - """ - old = set_script_timeout(0) - r = OpenForm__(*args) - set_script_timeout(old) - - -# - -# -class PluginForm(object): - """ - PluginForm class. - - This form can be used to host additional controls. Please check the PyQt example. - """ - - FORM_MDI = 0x01 - """start by default as MDI (obsolete)""" - FORM_TAB = 0x02 - """attached by default to a tab""" - FORM_RESTORE = 0x04 - """restore state from desktop config""" - FORM_ONTOP = 0x08 - """form should be "ontop""" - FORM_MENU = 0x10 - """form must be listed in the windows menu (automatically set for all plugins)""" - FORM_CENTERED = 0x20 - """form will be centered on the screen""" - FORM_PERSIST = 0x40 - """form will persist until explicitly closed with Close()""" - - - def __init__(self): - """ - """ - self.__clink__ = _idaapi.plgform_new() - - - - def Show(self, caption, options = 0): - """ - Creates the form if not was not created or brings to front if it was already created - - @param caption: The form caption - @param options: One of PluginForm.FORM_ constants - """ - options |= PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE - return _idaapi.plgform_show(self.__clink__, self, caption, options) - - - @staticmethod - def FormToPyQtWidget(form, ctx = sys.modules['__main__']): - """ - Use this method to convert a TForm* to a QWidget to be used by PyQt - - @param ctx: Context. Reference to a module that already imported SIP and QtGui modules - """ - return ctx.sip.wrapinstance(ctx.sip.voidptr(form).__int__(), ctx.QtGui.QWidget) - - - @staticmethod - def FormToPySideWidget(form, ctx = sys.modules['__main__']): - """ - Use this method to convert a TForm* to a QWidget to be used by PySide - - @param ctx: Context. Reference to a module that already imported QtGui module - """ - if form is None: - return None - if type(form).__name__ == "SwigPyObject": - # Since 'form' is a SwigPyObject, we first need to convert it to a PyCObject. - # However, there's no easy way of doing it, so we'll use a rather brutal approach: - # converting the SwigPyObject to a 'long' (will go through 'SwigPyObject_long', - # that will return the pointer's value as a long), and then convert that value - # back to a pointer into a PyCObject. - ptr_l = long(form) - from ctypes import pythonapi, c_void_p, py_object - pythonapi.PyCObject_FromVoidPtr.restype = py_object - pythonapi.PyCObject_AsVoidPtr.argtypes = [c_void_p, c_void_p] - form = pythonapi.PyCObject_FromVoidPtr(ptr_l, 0) - return ctx.QtGui.QWidget.FromCObject(form) - - - def OnCreate(self, form): - """ - This event is called when the plugin form is created. - The programmer should populate the form when this event is triggered. - - @return: None - """ - pass - - - def OnClose(self, form): - """ - Called when the plugin form is closed - - @return: None - """ - pass - - - def Close(self, options): - """ - Closes the form. - - @param options: Close options (FORM_SAVE, FORM_NO_CONTEXT, ...) - - @return: None - """ - return _idaapi.plgform_close(self.__clink__, options) - - FORM_SAVE = 0x1 - """Save state in desktop config""" - - FORM_NO_CONTEXT = 0x2 - """Don't change the current context (useful for toolbars)""" - - FORM_DONT_SAVE_SIZE = 0x4 - """Don't save size of the window""" - - FORM_CLOSE_LATER = 0x8 - """This flag should be used when Close() is called from an event handler""" -# - -class Choose: - """ - Choose - class for choose() with callbacks - """ - def __init__(self, list, title, flags=0, deflt=1, icon=37): - self.list = list - self.title = title - - self.flags = flags - self.x0 = -1 - self.x1 = -1 - self.y0 = -1 - self.y1 = -1 - - self.width = -1 - self.deflt = deflt - self.icon = icon - - # HACK: Add a circular reference for non-modal choosers. This prevents the GC - # from collecting the class object the callbacks need. Unfortunately this means - # that the class will never be collected, unless refhack is set to None explicitly. - if (flags & Choose2.CH_MODAL) == 0: - self.refhack = self - - def sizer(self): - """ - Callback: sizer - returns the length of the list - """ - return len(self.list) - - def getl(self, n): - """ - Callback: getl - get one item from the list - """ - if n == 0: - return self.title - if n <= self.sizer(): - return str(self.list[n-1]) - else: - return "" - - - def ins(self): - pass - - - def update(self, n): - pass - - - def edit(self, n): - pass - - - def enter(self, n): - print "enter(%d) called" % n - - - def destroy(self): - pass - - - def get_icon(self, n): - pass - - - def choose(self): - """ - choose - Display the choose dialogue - """ - old = set_script_timeout(0) - n = _idaapi.choose_choose( - self, - self.flags, - self.x0, - self.y0, - self.x1, - self.y1, - self.width, - self.deflt, - self.icon) - set_script_timeout(old) - return n -%} - -%pythoncode %{ -# -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: - # -# -# 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 prefix_start (string) -# @param n: completion number (int) -# @param line: the current line (string) -# @param prefix_start: the index where PREFIX starts in LINE (int) -# -# @return: None if no completion could be generated otherwise a String with the completion suggestion -# """ -# return None -# - -# -# -class simplecustviewer_t(object): - """The base class for implementing simple custom viewers""" - def __init__(self): - self.__this = None - - def __del__(self): - """Destructor. It also frees the associated C++ object""" - try: - _idaapi.pyscv_delete(self.__this) - except: - pass - - @staticmethod - def __make_sl_arg(line, fgcolor=None, bgcolor=None): - return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) - - def Create(self, title): - """ - Creates the custom view. This should be the first method called after instantiation - - @param title: The title of the view - @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. - In this case better close existing windows - """ - self.title = title - self.__this = _idaapi.pyscv_init(self, title) - return True if self.__this else False - - def Close(self): - """ - Destroys the view. - One has to call Create() afterwards. - Show() can be called and it will call Create() internally. - @return: Boolean - """ - return _idaapi.pyscv_close(self.__this) - - def Show(self): - """ - Shows an already created view. It the view was close, then it will call Create() for you - @return: Boolean - """ - return _idaapi.pyscv_show(self.__this) - - def Refresh(self): - return _idaapi.pyscv_refresh(self.__this) - - def RefreshCurrent(self): - """Refreshes the current line only""" - return _idaapi.pyscv_refresh_current(self.__this) - - def Count(self): - """Returns the number of lines in the view""" - return _idaapi.pyscv_count(self.__this) - - def GetSelection(self): - """ - Returns the selected area or None - @return: - - tuple(x1, y1, x2, y2) - - None if no selection - """ - return _idaapi.pyscv_get_selection(self.__this) - - def ClearLines(self): - """Clears all the lines""" - _idaapi.pyscv_clear_lines(self.__this) - - def AddLine(self, line, fgcolor=None, bgcolor=None): - """ - Adds a colored line to the view - @return: Boolean - """ - return _idaapi.pyscv_add_line(self.__this, self.__make_sl_arg(line, fgcolor, bgcolor)) - - def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): - """ - Inserts a line in the given position - @return: Boolean - """ - return _idaapi.pyscv_insert_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) - - def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): - """ - Edits an existing line. - @return: Boolean - """ - return _idaapi.pyscv_edit_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) - - def PatchLine(self, lineno, offs, value): - """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" - return _idaapi.pyscv_patch_line(self.__this, lineno, offs, value) - - def DelLine(self, lineno): - """ - Deletes an existing line - @return: Boolean - """ - return _idaapi.pyscv_del_line(self.__this, lineno) - - def GetLine(self, lineno): - """ - Returns a line - @param lineno: The line number - @return: - Returns a tuple (colored_line, fgcolor, bgcolor) or None - """ - return _idaapi.pyscv_get_line(self.__this, lineno) - - def GetCurrentWord(self, mouse = 0): - """ - Returns the current word - @param mouse: Use mouse position or cursor position - @return: None if failed or a String containing the current word at mouse or cursor - """ - return _idaapi.pyscv_get_current_word(self.__this, mouse) - - def GetCurrentLine(self, mouse = 0, notags = 0): - """ - Returns the current line. - @param mouse: Current line at mouse pos - @param notags: If True then tag_remove() will be called before returning the line - @return: Returns the current line (colored or uncolored) or None on failure - """ - return _idaapi.pyscv_get_current_line(self.__this, mouse, notags) - - def GetPos(self, mouse = 0): - """ - Returns the current cursor or mouse position. - @param mouse: return mouse position - @return: Returns a tuple (lineno, x, y) - """ - return _idaapi.pyscv_get_pos(self.__this, mouse) - - def GetLineNo(self, mouse = 0): - """Calls GetPos() and returns the current line number or -1 on failure""" - r = self.GetPos(mouse) - return -1 if not r else r[0] - - def Jump(self, lineno, x=0, y=0): - return _idaapi.pyscv_jumpto(self.__this, lineno, x, y) - - def AddPopupMenu(self, title, hotkey=""): - """ - Adds a popup menu item - @param title: The name of the menu item - @param hotkey: Hotkey of the item or just empty - @return: Returns the - """ - return _idaapi.pyscv_add_popup_menu(self.__this, title, hotkey) - - def ClearPopupMenu(self): - """ - Clears all previously installed popup menu items. - Use this function if you're generating menu items on the fly (in the OnPopup() callback), - and before adding new items - """ - _idaapi.pyscv_clear_popup_menu(self.__this) - - def IsFocused(self): - """Returns True if the current view is the focused view""" - return _idaapi.pyscv_is_focused(self.__this) - - def GetTForm(self): - """ - Return the TForm hosting this view. - - @return: The TForm that hosts this view, or None. - """ - return _idaapi.pyscv_get_tform(self.__this) - - def GetTCustomControl(self): - """ - Return the TCustomControl underlying this view. - - @return: The TCustomControl underlying this view, or None. - """ - return _idaapi.pyscv_get_tcustom_control(self.__this) - - - - # Here are all the supported events -# -# def OnClick(self, shift): -# """ -# User clicked in the view -# @param shift: Shift flag -# @return: Boolean. True if you handled the event -# """ -# print "OnClick, shift=%d" % shift -# return True -# -# def OnDblClick(self, shift): -# """ -# User dbl-clicked in the view -# @param shift: Shift flag -# @return: Boolean. True if you handled the event -# """ -# print "OnDblClick, shift=%d" % shift -# return True -# -# def OnCursorPosChanged(self): -# """ -# Cursor position changed. -# @return: Nothing -# """ -# print "OnCurposChanged" -# -# def OnClose(self): -# """ -# The view is closing. Use this event to cleanup. -# @return: Nothing -# """ -# print "OnClose" -# -# def OnKeydown(self, vkey, shift): -# """ -# User pressed a key -# @param vkey: Virtual key code -# @param shift: Shift flag -# @return: Boolean. True if you handled the event -# """ -# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) -# return False -# -# def OnPopup(self): -# """ -# Context menu popup is about to be shown. Create items dynamically if you wish -# @return: Boolean. True if you handled the event -# """ -# print "OnPopup" -# -# def OnHint(self, lineno): -# """ -# Hint requested for the given line number. -# @param lineno: The line number (zero based) -# @return: -# - tuple(number of important lines, hint string) -# - None: if no hint available -# """ -# return (1, "OnHint, line=%d" % lineno) -# -# def OnPopupMenu(self, menu_id): -# """ -# A context (or popup) menu item was executed. -# @param menu_id: ID previously registered with add_popup_menu() -# @return: Boolean -# """ -# print "OnPopupMenu, menu_id=" % menu_id -# return True -# -# -%} +// Ignore the va_list functions +%ignore AskUsingForm_cv; +%ignore AskUsingForm_c; +%ignore OpenForm_cv; +%ignore OpenForm_c; +%ignore close_form; +%ignore vaskstr; +%ignore strvec_t; +%ignore load_custom_icon; +%ignore vasktext; +%ignore add_menu_item; +%rename (add_menu_item) py_add_menu_item; +%ignore del_menu_item; +%rename (del_menu_item) py_del_menu_item; +%ignore vwarning; + +%ignore choose_idasgn; +%rename (choose_idasgn) py_choose_idasgn; + +%rename (del_hotkey) py_del_hotkey; +%rename (add_hotkey) py_add_hotkey; + +%ignore msg; +%rename (msg) py_msg; + +%ignore umsg; +%rename (umsg) py_umsg; + +%ignore textctrl_info_t; +%ignore vinfo; +%ignore UI_Callback; +%ignore vnomem; +%ignore vmsg; +%ignore show_wait_box_v; +%ignore askbuttons_cv; +%ignore askfile_cv; +%ignore askyn_cv; +%ignore askyn_v; +%ignore add_custom_viewer_popup_item; +%ignore create_custom_viewer; +%ignore take_database_snapshot; +%ignore restore_database_snapshot; +%ignore destroy_custom_viewer; +%ignore destroy_custom_viewerdestroy_custom_viewer; +%ignore set_custom_viewer_popup_menu; +%ignore set_custom_viewer_handler; +%ignore set_custom_viewer_range; +%ignore is_idaview; +%ignore refresh_custom_viewer; +%ignore set_custom_viewer_handlers; +%ignore get_viewer_name; +// Ignore these string functions. There are trivial replacements in Python. +%ignore addblanks; +%ignore trim; +%ignore skipSpaces; +%ignore stristr; +%ignore set_nav_colorizer; +%rename (set_nav_colorizer) py_set_nav_colorizer; +%rename (call_nav_colorizer) py_call_nav_colorizer; + +%ignore get_highlighted_identifier; +%rename (get_highlighted_identifier) py_get_highlighted_identifier; + + +// 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; + +%ignore action_desc_t::handler; +%ignore action_handler_t; +%ignore register_action; +%rename (register_action) py_register_action; +%ignore unregister_action; +%rename (unregister_action) py_unregister_action; +%ignore attach_dynamic_action_to_popup; +%rename (attach_dynamic_action_to_popup) py_attach_dynamic_action_to_popup; + +%include "typemaps.i" + +%rename (asktext) py_asktext; +%rename (str2ea) py_str2ea; +%rename (str2user) py_str2user; +%ignore process_ui_action; +%rename (process_ui_action) py_process_ui_action; +%ignore execute_sync; +%ignore exec_request_t; +%rename (execute_sync) py_execute_sync; + +%ignore ui_request_t; +%ignore execute_ui_requests; +%rename (execute_ui_requests) py_execute_ui_requests; + +%ignore timer_t; +%ignore register_timer; +%rename (register_timer) py_register_timer; +%ignore unregister_timer; +%rename (unregister_timer) py_unregister_timer; + +// Make askaddr(), askseg(), and asklong() return a +// tuple: (result, value) +%rename (_asklong) asklong; +%rename (_askaddr) askaddr; +%rename (_askseg) askseg; + +%ignore qvector::operator==; +%ignore qvector::operator!=; +%ignore qvector::find; +%ignore qvector::has; +%ignore qvector::del; +%ignore qvector::add_unique; + +%ignore gen_disasm_text; +%rename (gen_disasm_text) py_gen_disasm_text; + +%feature("director") UI_Hooks; + +//------------------------------------------------------------------------- +%{ +struct py_action_handler_t : public action_handler_t +{ + py_action_handler_t(); // No. + py_action_handler_t(PyObject *_o) + : pyah(borref_t(_o)), has_activate(false), has_update(false) + { + ref_t act(PyW_TryGetAttrString(pyah.o, "activate")); + if ( act != NULL && PyCallable_Check(act.o) > 0 ) + has_activate = true; + + ref_t upd(PyW_TryGetAttrString(pyah.o, "update")); + if ( upd != NULL && PyCallable_Check(upd.o) > 0 ) + has_update = true; + } + virtual idaapi ~py_action_handler_t() + { + PYW_GIL_GET; + // NOTE: We need to do the decref _within_ the PYW_GIL_GET scope, + // and not leave it to the destructor to clean it up, because when + // ~ref_t() gets called, the GIL will have already been released. + pyah = ref_t(); + } + virtual int idaapi activate(action_activation_ctx_t *ctx) + { + if ( !has_activate ) + return 0; + PYW_GIL_GET_AND_REPORT_ERROR; + newref_t pyctx(SWIG_NewPointerObj(SWIG_as_voidptr(ctx), SWIGTYPE_p_action_activation_ctx_t, 0)); + newref_t pyres(PyObject_CallMethod(pyah.o, (char *)"activate", (char *) "O", pyctx.o)); + return PyErr_Occurred() ? 0 : ((pyres != NULL && PyInt_Check(pyres.o)) ? PyInt_AsLong(pyres.o) : 0); + } + virtual action_state_t idaapi update(action_update_ctx_t *ctx) + { + if ( !has_update ) + return AST_DISABLE; + PYW_GIL_GET_AND_REPORT_ERROR; + newref_t pyctx(SWIG_NewPointerObj(SWIG_as_voidptr(ctx), SWIGTYPE_p_action_update_ctx_t, 0)); + newref_t pyres(PyObject_CallMethod(pyah.o, (char *)"update", (char *) "O", pyctx.o)); + return PyErr_Occurred() ? AST_DISABLE_ALWAYS : ((pyres != NULL && PyInt_Check(pyres.o)) ? action_state_t(PyInt_AsLong(pyres.o)) : AST_DISABLE); + } +private: + ref_t pyah; + bool has_activate; + bool has_update; +}; + +typedef std::map py_action_handlers_t; +static py_action_handlers_t py_action_handlers; + +%} + +%inline %{ +void refresh_lists(void) +{ + Py_BEGIN_ALLOW_THREADS; + callui(ui_list); + Py_END_ALLOW_THREADS; +} +%} + +# This is for get_cursor() +%apply int *OUTPUT {int *x, int *y}; + +SWIG_DECLARE_PY_CLINKED_OBJECT(textctrl_info_t) + +%{ +static void _py_unregister_compiled_form(PyObject *py_form, bool shutdown); +%} + +%inline %{ +// +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +/* +# +def register_timer(interval, callback): + """ + Register a timer + + @param interval: Interval in milliseconds + @param callback: A Python callable that takes no parameters and returns an integer. + The callback may return: + -1 : to unregister the timer + >= 0 : the new or same timer interval + @return: None or a timer object + """ + pass +# +*/ +static PyObject *py_register_timer(int interval, PyObject *py_callback) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( py_callback == NULL || !PyCallable_Check(py_callback) ) + Py_RETURN_NONE; + + // An inner class hosting the callback method + struct tmr_t + { + static int idaapi callback(void *ud) + { + PYW_GIL_GET; + py_timer_ctx_t *ctx = (py_timer_ctx_t *)ud; + newref_t py_result(PyObject_CallFunctionObjArgs(ctx->pycallback, NULL)); + int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result.o); + + // Timer has been unregistered? + if ( ret == -1 ) + { + // Free the context + Py_DECREF(ctx->pycallback); + delete ctx; + } + return ret; + }; + }; + + py_timer_ctx_t *ctx = new py_timer_ctx_t(); + ctx->pycallback = py_callback; + Py_INCREF(py_callback); + ctx->timer_id = register_timer( + interval, + tmr_t::callback, + ctx); + + if ( ctx->timer_id == NULL ) + { + Py_DECREF(py_callback); + delete ctx; + Py_RETURN_NONE; + } + return PyCObject_FromVoidPtr(ctx, NULL); +} + +//------------------------------------------------------------------------ +/* +# +def unregister_timer(timer_obj): + """ + Unregister a timer + + @param timer_obj: a timer object previously returned by a register_timer() + @return: Boolean + @note: After the timer has been deleted, the timer_obj will become invalid. + """ + pass +# +*/ +static PyObject *py_unregister_timer(PyObject *py_timerctx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( py_timerctx == NULL || !PyCObject_Check(py_timerctx) ) + Py_RETURN_FALSE; + + py_timer_ctx_t *ctx = (py_timer_ctx_t *) PyCObject_AsVoidPtr(py_timerctx); + if ( !unregister_timer(ctx->timer_id) ) + Py_RETURN_FALSE; + + Py_DECREF(ctx->pycallback); + delete ctx; + + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------ +/* +# +def choose_idasgn(): + """ + Opens the signature chooser + + @return: None or the selected signature name + """ + pass +# +*/ +static PyObject *py_choose_idasgn() +{ + char *name = choose_idasgn(); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( name == NULL ) + { + Py_RETURN_NONE; + } + else + { + PyObject *py_str = PyString_FromString(name); + qfree(name); + return py_str; + } +} + +//------------------------------------------------------------------------ +/* +# +def get_highlighted_identifier(flags = 0): + """ + Returns the currently highlighted identifier + + @param flags: reserved (pass 0) + @return: None or the highlighted identifier + """ + pass +# +*/ +static PyObject *py_get_highlighted_identifier(int flags = 0) +{ + char buf[MAXSTR]; + bool ok = get_highlighted_identifier(buf, sizeof(buf), flags); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !ok ) + Py_RETURN_NONE; + else + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------ +static int py_load_custom_icon_fn(const char *filename) +{ + return load_custom_icon(filename); +} + +//------------------------------------------------------------------------ +static int py_load_custom_icon_data(PyObject *data, const char *format) +{ + Py_ssize_t len; + char *s; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_AsStringAndSize(data, &s, &len) == -1 ) + return 0; + else + return load_custom_icon(s, len, format); +} + +//------------------------------------------------------------------------ +/* +# +def free_custom_icon(icon_id): + """ + Frees an icon loaded with load_custom_icon() + """ + pass +# +*/ + +//------------------------------------------------------------------------- +/* +# +def readsel2(view, p0, p1): + """ + Read the user selection, and store its information in p0 (from) and p1 (to). + + This can be used as follows: + + + >>> p0 = idaapi.twinpos_t() + p1 = idaapi.twinpos_t() + view = idaapi.get_current_viewer() + idaapi.readsel2(view, p0, p1) + + + At that point, p0 and p1 hold information for the selection. + But, the 'at' property of p0 and p1 is not properly typed. + To specialize it, call #place() on it, passing it the view + they were retrieved from. Like so: + + + >>> place0 = p0.place(view) + place1 = p1.place(view) + + + This will effectively "cast" the place into a specialized type, + holding proper information, depending on the view type (e.g., + disassembly, structures, enums, ...) + + @param view: The view to retrieve the selection for. + @param p0: Storage for the "from" part of the selection. + @param p1: Storage for the "to" part of the selection. + @return: a bool value indicating success. + """ + pass +# +*/ + +//------------------------------------------------------------------------ +/* +# +def umsg(text): + """ + Prints text into IDA's Output window + + @param text: text to print + Can be Unicode, or string in UTF-8 encoding + @return: number of bytes printed + """ + pass +# +*/ +static PyObject* py_umsg(PyObject *o) +{ + PyObject* utf8 = NULL; + if ( PyUnicode_Check(o) ) + { + utf8 = PyUnicode_AsUTF8String(o); + o = utf8; + } + else if ( !PyString_Check(o) ) + { + PyErr_SetString(PyExc_TypeError, "A unicode or UTF-8 string expected"); + return NULL; + } + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = umsg("%s", PyString_AsString(o)); + Py_END_ALLOW_THREADS; + Py_XDECREF(utf8); + return PyInt_FromLong(rc); +} + +//------------------------------------------------------------------------ +/* +# +def msg(text): + """ + Prints text into IDA's Output window + + @param text: text to print + Can be Unicode, or string in local encoding + @return: number of bytes printed + """ + pass +# +*/ +static PyObject* py_msg(PyObject *o) +{ + if ( PyUnicode_Check(o) ) + return py_umsg(o); + + if ( !PyString_Check(o) ) + { + PyErr_SetString(PyExc_TypeError, "A string expected"); + return NULL; + } + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = msg("%s", PyString_AsString(o)); + Py_END_ALLOW_THREADS; + return PyInt_FromLong(rc); +} + +//------------------------------------------------------------------------ +/* +# +def asktext(max_text, defval, prompt): + """ + Asks for a long text + + @param max_text: Maximum text length + @param defval: The default value + @param prompt: The prompt value + @return: None or the entered string + """ + pass +# +*/ +PyObject *py_asktext(int max_text, const char *defval, const char *prompt) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( max_text <= 0 ) + Py_RETURN_NONE; + + char *buf = new char[max_text]; + if ( buf == NULL ) + Py_RETURN_NONE; + + PyObject *py_ret; + if ( asktext(size_t(max_text), buf, defval, "%s", prompt) != NULL ) + { + py_ret = PyString_FromString(buf); + } + else + { + py_ret = Py_None; + Py_INCREF(py_ret); + } + delete [] buf; + return py_ret; +} + +//------------------------------------------------------------------------ +/* +# +def str2ea(addr): + """ + Converts a string express to EA. The expression evaluator may be called as well. + + @return: BADADDR or address value + """ + pass +# +*/ +ea_t py_str2ea(const char *str, ea_t screenEA = BADADDR) +{ + ea_t ea; + bool ok = str2ea(str, &ea, screenEA); + return ok ? ea : BADADDR; +} + +//------------------------------------------------------------------------ +/* +# +def str2user(str): + """ + Insert C-style escape characters to string + + @return: new string with escape characters inserted + """ + pass +# +*/ +PyObject *py_str2user(const char *str) +{ + qstring qstr(str); + qstring retstr; + qstr2user(&retstr, qstr); + return PyString_FromString(retstr.c_str()); +} + +//------------------------------------------------------------------------ +/* +# +def process_ui_action(name): + """ + Invokes an IDA UI action by name + + @param name: action name + @return: Boolean + """ + pass +# +*/ +static bool py_process_ui_action(const char *name, int flags = 0) +{ + return process_ui_action(name, flags, NULL); +} + +//------------------------------------------------------------------------ +/* +# +def del_menu_item(menu_ctx): + """Deprecated. Use detach_menu_item()/unregister_action() instead.""" + pass +# +*/ +static bool py_del_menu_item(PyObject *py_ctx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_ctx) ) + return false; + + py_add_del_menu_item_ctx *ctx = (py_add_del_menu_item_ctx *)PyCObject_AsVoidPtr(py_ctx); + + bool ok = del_menu_item(ctx->menupath.c_str()); + if ( ok ) + { + Py_DECREF(ctx->cb_data); + delete ctx; + } + + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def del_hotkey(ctx): + """ + Deletes a previously registered function hotkey + + @param ctx: Hotkey context previously returned by add_hotkey() + + @return: Boolean. + """ + pass +# +*/ +bool py_del_hotkey(PyObject *pyctx) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(pyctx) ) + return false; + + py_idchotkey_ctx_t *ctx = (py_idchotkey_ctx_t *) PyCObject_AsVoidPtr(pyctx); + if ( !del_idc_hotkey(ctx->hotkey.c_str()) ) + return false; + + Py_DECREF(ctx->pyfunc); + delete ctx; + return true; +} + +//------------------------------------------------------------------------ +/* +# +def add_hotkey(hotkey, pyfunc): + """ + Associates a function call with a hotkey. + Callable pyfunc will be called each time the hotkey is pressed + + @param hotkey: The hotkey + @param pyfunc: Callable + + @return: Context object on success or None on failure. + """ + pass +# +*/ +PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Make sure a callable was passed + if ( !PyCallable_Check(pyfunc) ) + return NULL; + + // Form the function name + qstring idc_func_name; + idc_func_name.sprnt("py_hotkeycb_%p", pyfunc); + + // Can add the hotkey? + if ( add_idc_hotkey(hotkey, idc_func_name.c_str()) == IDCHK_OK ) do + { + // Generate global variable name + qstring idc_gvarname; + idc_gvarname.sprnt("_g_pyhotkey_ref_%p", pyfunc); + + // Now add the global variable + idc_value_t *gvar = add_idc_gvar(idc_gvarname.c_str()); + if ( gvar == NULL ) + break; + + // The function body will call a registered IDC function that + // will take a global variable that wraps a PyCallable as a pvoid + qstring idc_func; + idc_func.sprnt("static %s() { %s(%s); }", + idc_func_name.c_str(), + S_PYINVOKE0, + idc_gvarname.c_str()); + + // Compile the IDC condition + char errbuf[MAXSTR]; + if ( !CompileLineEx(idc_func.c_str(), errbuf, sizeof(errbuf)) ) + break; + + // Create new context + // Define context + py_idchotkey_ctx_t *ctx = new py_idchotkey_ctx_t(); + + // Remember the hotkey + ctx->hotkey = hotkey; + + // Take reference to the callable + ctx->pyfunc = pyfunc; + Py_INCREF(pyfunc); + + // Bind IDC variable w/ the PyCallable + gvar->set_pvoid(pyfunc); + + // Return the context + return PyCObject_FromVoidPtr(ctx, NULL); + } while (false); + + // Cleanup + del_idc_hotkey(hotkey); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------ +/* +# +def add_menu_item(menupath, name, hotkey, flags, callback, args): + """Deprecated. Use register_action()/attach_menu_item() instead.""" + pass +# +*/ +bool idaapi py_menu_item_callback(void *userdata); +static PyObject *py_add_menu_item( + const char *menupath, + const char *name, + const char *hotkey, + int flags, + PyObject *pyfunc, + PyObject *args) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool no_args; + + // No slash in the menu path? + const char *p = strrchr(menupath, '/'); + if ( p == NULL ) + Py_RETURN_NONE; + + if ( args == Py_None ) + { + no_args = true; + args = PyTuple_New(0); + if ( args == NULL ) + return NULL; + } + else if ( !PyTuple_Check(args) ) + { + PyErr_SetString(PyExc_TypeError, "args must be a tuple or None"); + return NULL; + } + else + { + no_args = false; + } + + // Form a tuple holding the function to be called and its arguments + PyObject *cb_data = Py_BuildValue("(OO)", pyfunc, args); + + // If we created an empty tuple, then we must free it + if ( no_args ) + Py_DECREF(args); + + // Add the menu item + bool b = add_menu_item(menupath, name, hotkey, flags, py_menu_item_callback, (void *)cb_data); + + if ( !b ) + { + Py_XDECREF(cb_data); + Py_RETURN_NONE; + } + // Create a context (for the delete_menu_item()) + py_add_del_menu_item_ctx *ctx = new py_add_del_menu_item_ctx(); + + // Form the complete menu path + ctx->menupath.append(menupath, p - menupath + 1); + ctx->menupath.append(name); + // Save callback data + ctx->cb_data = cb_data; + + // Return context to user + return PyCObject_FromVoidPtr(ctx, NULL); +} + +//------------------------------------------------------------------------ +/* +# + +MFF_FAST = 0x0000 +"""execute code as soon as possible +this mode is ok call ui related functions +that do not query the database.""" + +MFF_READ = 0x0001 +"""execute code only when ida is idle and it is safe to query the database. +this mode is recommended only for code that does not modify the database. +(nb: ida may be in the middle of executing another user request, for example it may be waiting for him to enter values into a modal dialog box)""" + +MFF_WRITE = 0x0002 +"""execute code only when ida is idle and it is safe to modify the database. in particular, this flag will suspend execution if there is +a modal dialog box on the screen this mode can be used to call any ida api function. MFF_WRITE implies MFF_READ""" + +MFF_NOWAIT = 0x0004 +"""Do not wait for the request to be executed. +he caller should ensure that the request is not +destroyed until the execution completes. +if not, the request will be ignored. +the return code of execute_sync() is meaningless +in this case. +This flag can be used to delay the code execution +until the next UI loop run even from the main thread""" + +def execute_sync(callable, reqf): + """ + Executes a function in the context of the main thread. + If the current thread not the main thread, then the call is queued and + executed afterwards. + + @note: The Python version of execute_sync() cannot be called from a different thread + for the time being. + @param callable: A python callable object + @param reqf: one of MFF_ flags + @return: -1 or the return value of the callable + """ + pass +# +*/ +//------------------------------------------------------------------------ +static int py_execute_sync(PyObject *py_callable, int reqf) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc = -1; + // Callable? + if ( PyCallable_Check(py_callable) ) + { + struct py_exec_request_t : exec_request_t + { + ref_t py_callable; + virtual int idaapi execute() + { + PYW_GIL_GET; + newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); + int ret = py_result == NULL || !PyInt_Check(py_result.o) + ? -1 + : PyInt_AsLong(py_result.o); + // if the requesting thread decided not to wait for the request to + // complete, we have to self-destroy, nobody else will do it + if ( (code & MFF_NOWAIT) != 0 ) + delete this; + return ret; + } + py_exec_request_t(PyObject *pyc) + { + // No need to GIL-ensure here, since this is created + // within the py_execute_sync() scope. + py_callable = borref_t(pyc); + } + virtual ~py_exec_request_t() + { + // Need to GIL-ensure here, since this might be called + // from the main thread. + PYW_GIL_GET; + py_callable = ref_t(); // Release callable + } + }; + py_exec_request_t *req = new py_exec_request_t(py_callable); + + // Release GIL before executing, or if this is running in the + // non-main thread, this will wait on the req.sem, while the main + // thread might be waiting for the GIL to be available. + Py_BEGIN_ALLOW_THREADS; + rc = execute_sync(*req, reqf); + Py_END_ALLOW_THREADS; + // destroy the request once it is finished. exception: NOWAIT requests + // will be handled in the future, so do not destroy them yet! + if ( (reqf & MFF_NOWAIT) == 0 ) + delete req; + } + return rc; +} + +//------------------------------------------------------------------------ +/* +# + +def execute_ui_requests(callable_list): + """ + Inserts a list of callables into the UI message processing queue. + When the UI is ready it will call one callable. + A callable can request to be called more than once if it returns True. + + @param callable_list: A list of python callable objects. + @note: A callable should return True if it wants to be called more than once. + @return: Boolean. False if the list contains a non callabale item + """ + pass +# +*/ +static bool py_execute_ui_requests(PyObject *py_list) +{ + struct py_ui_request_t: public ui_request_t + { + private: + ref_vec_t py_callables; + size_t py_callable_idx; + + static int idaapi s_py_list_walk_cb( + const ref_t &py_item, + Py_ssize_t index, + void *ud) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Not callable? Terminate iteration + if ( !PyCallable_Check(py_item.o) ) + return CIP_FAILED; + + // Append this callable and increment its reference + py_ui_request_t *_this = (py_ui_request_t *)ud; + _this->py_callables.push_back(py_item); + return CIP_OK; + } + public: + py_ui_request_t(): py_callable_idx(0) + { + } + + virtual bool idaapi run() + { + PYW_GIL_GET; + + // Get callable + ref_t py_callable = py_callables.at(py_callable_idx); + bool reschedule; + newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); + reschedule = py_result != NULL && PyObject_IsTrue(py_result.o); + + // No rescheduling? Then advance to the next callable + if ( !reschedule ) + ++py_callable_idx; + + // Reschedule this C callback only if there are more callables + return py_callable_idx < py_callables.size(); + } + + // Walk the list and extract all callables + bool init(PyObject *py_list) + { + Py_ssize_t count = pyvar_walk_list( + py_list, + s_py_list_walk_cb, + this); + return count > 0; + } + + virtual idaapi ~py_ui_request_t() + { + py_callables.clear(); + } + }; + + py_ui_request_t *req = new py_ui_request_t(); + if ( !req->init(py_list) ) + { + delete req; + return false; + } + execute_ui_requests(req, NULL); + return true; +} + +//------------------------------------------------------------------------ +/* +# +def set_dock_pos(src, dest, orient, left = 0, top = 0, right = 0, bottom = 0): + """ + Sets the dock orientation of a window relatively to another window. + + @param src: Source docking control + @param dest: Destination docking control + @param orient: One of DOR_XXXX constants + @param left, top, right, bottom: These parameter if DOR_FLOATING is used, or if you want to specify the width of docked windows + @return: Boolean + + Example: + set_dock_pos('Structures', 'Enums', DOR_RIGHT) <- docks the Structures window to the right of Enums window + """ + pass +# +*/ + +//------------------------------------------------------------------------ +/* +# +def is_idaq(): + """ + Returns True or False depending if IDAPython is hosted by IDAQ + """ +# +*/ + +//--------------------------------------------------------------------------- +// UI hooks +//--------------------------------------------------------------------------- +int idaapi UI_Callback(void *ud, int notification_code, va_list va); +/* +# +class UI_Hooks(object): + def hook(self): + """ + Creates an UI hook + + @return: Boolean true on success + """ + pass + + def unhook(self): + """ + Removes the UI hook + @return: Boolean true on success + """ + pass + + def preprocess(self, name): + """ + IDA ui is about to handle a user command + + @param name: ui command name + (these names can be looked up in ida[tg]ui.cfg) + @return: 0-ok, nonzero - a plugin has handled the command + """ + pass + + def postprocess(self): + """ + An ida ui command has been handled + + @return: Ignored + """ + pass + + def saving(self): + """ + The kernel is saving the database. + + @return: Ignored + """ + pass + + def saved(self): + """ + The kernel has saved the database. + + @return: Ignored + """ + pass + + def get_ea_hint(self, ea): + """ + The UI wants to display a simple hint for an address in the navigation band + + @param ea: The address + @return: String with the hint or None + """ + pass + + def updating_actions(self, ctx): + """ + The UI is about to batch-update some actions. + + @param ctx: The action_update_ctx_t instance + @return: Ignored + """ + pass + + def updated_actions(self): + """ + The UI is done updating actions. + + @return: Ignored + """ + pass + + def populating_tform_popup(self, form, popup): + """ + The UI is populating the TForm's popup menu. + Now is a good time to call idaapi.attach_action_to_popup() + + @param form: The form + @param popup: The popup menu. + @return: Ignored + """ + pass + + def finish_populating_tform_popup(self, form, popup): + """ + The UI is about to be done populating the TForm's popup menu. + Now is a good time to call idaapi.attach_action_to_popup() + + @param form: The form + @param popup: The popup menu. + @return: Ignored + """ + pass + + def term(self): + """ + IDA is terminated and the database is already closed. + The UI may close its windows in this callback. + """ + # if the user forgot to call unhook, do it for him + self.unhook() + + def __term__(self): + self.term() + +# +*/ +class UI_Hooks +{ +public: + virtual ~UI_Hooks() + { + unhook(); + } + + bool hook() + { + return hook_to_notification_point(HT_UI, UI_Callback, this); + } + + bool unhook() + { + return unhook_from_notification_point(HT_UI, UI_Callback, this); + } + + virtual int preprocess(const char * /*name*/) + { + return 0; + } + + virtual void postprocess() + { + } + + virtual void saving() + { + } + + virtual void saved() + { + } + + virtual void term() + { + } + + virtual PyObject *get_ea_hint(ea_t /*ea*/) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + }; + + virtual void current_tform_changed(TForm * /*form*/, TForm * /*previous_form*/) + { + } + + virtual void updating_actions(action_update_ctx_t *ctx) + { + } + + virtual void updated_actions() + { + } + + virtual void populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/) + { + } + + virtual void finish_populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/) + { + } +}; + +//------------------------------------------------------------------------- +bool py_register_action(action_desc_t *desc) +{ + bool ok = register_action(*desc); + if ( ok ) + { + // Success. We are managing this handler from now on, + // and must prevent it from being destroyed. + py_action_handlers[desc->name] = desc->handler; + // Let's set this to NULL, so when the wrapping Python action_desc_t + // instance is deleted, it doesn't try to delete the handler (See + // kernwin.i's action_desc_t::~action_desc_t()). + desc->handler = NULL; + } + return ok; +} + +//------------------------------------------------------------------------- +bool py_unregister_action(const char *name) +{ + bool ok = unregister_action(name); + if ( ok ) + { + py_action_handler_t *handler = + (py_action_handler_t *) py_action_handlers[name]; + delete handler; + py_action_handlers.erase(name); + } + return ok; +} + +//------------------------------------------------------------------------- +bool py_attach_dynamic_action_to_popup( + TForm *form, + TPopupMenu *popup_handle, + action_desc_t *desc, + const char *popuppath = NULL, + int flags = 0) +{ + bool ok = attach_dynamic_action_to_popup( + form, popup_handle, *desc, popuppath, flags); + if ( ok ) + // Set the handler to null, so the desc won't destroy + // it, as it noticed ownership was taken by IDA. + // In addition, we don't need to register into the + // 'py_action_handlers', because IDA will destroy the + // handler as soon as the popup menu is closed. + desc->handler = NULL; + return ok; +} + +// This is similar to a twinline_t, with improved memory management: +// twinline_t has a dummy destructor, that performs no cleanup. +struct disasm_line_t +{ + disasm_line_t() : at(NULL) {} + ~disasm_line_t() { qfree(at); } + disasm_line_t(const disasm_line_t &other) { *this = other; } + disasm_line_t &operator=(const disasm_line_t &other) + { + qfree(at); + at = other.at == NULL ? NULL : other.at->clone(); + return *this; + } + place_t *at; + qstring line; + color_t prefix_color; + bgcolor_t bg_color; + bool is_default; +}; +DECLARE_TYPE_AS_MOVABLE(disasm_line_t); +typedef qvector disasm_text_t; + +//------------------------------------------------------------------------- +void py_gen_disasm_text(ea_t ea1, ea_t ea2, disasm_text_t &text, bool truncate_lines) +{ + text_t _text; + gen_disasm_text(ea1, ea2, _text, truncate_lines); + for ( size_t i = 0, n = _text.size(); i < n; ++i ) + { + const twinline_t &tl = _text[i]; + disasm_line_t &dl = text.push_back(); + dl.at = tl.at; // Transfer ownership + dl.line.inject(tl.line); // Transfer ownership + } +} + +//------------------------------------------------------------------------- +// Although 'TCustomControl*' and 'TForm*' instances can both be used +// for attach_action_to_popup() at a binary-level, IDAPython SWIG bindings +// require that a 'TForm *' wrapper be passed to wrap_attach_action_to_popup(). +// Thus, we provide another attach_action_to_popup() version, that +// accepts a 'TCustomControl' as first argument. +// +// Since user-created GraphViewer are created like so: +// +-------- PluginForm ----------+ +// |+----- TCustomControl -------+| +// || || +// || || +// || || +// || || +// || || +// |+----------------------------+| +// +------------------------------+ +// The user cannot use GetTForm(), and use that to attach +// an action to, because that'll attach the action to the PluginForm +// instance. +// Instead, the user must use GetTCustomControl(), and call +// this function below with it. +bool attach_action_to_popup( + TCustomControl *tcc, + TPopupMenu *popup_handle, + const char *name, + const char *popuppath = NULL, + int flags = 0) +{ + return attach_action_to_popup((TForm*) tcc, popup_handle, name, popuppath, flags); +} + +//------------------------------------------------------------------------- +/* +# +def set_nav_colorizer(callback): + """ + Set a new colorizer for the navigation band. + + The 'callback' is a function of 2 arguments: + - ea (the EA to colorize for) + - nbytes (the number of bytes at that EA) + and must return a 'long' value. + + The previous colorizer is returned, allowing + the new 'callback' to use 'call_nav_colorizer' + with it. + + Note that the previous colorizer is returned + only the first time set_nav_colorizer() is called: + due to the way the colorizers API is defined in C, + it is impossible to chain more than 2 colorizers + in IDAPython: the original, IDA-provided colorizer, + and a user-provided one. + + Example: colorizer inverting the color provided by the IDA colorizer: + def my_colorizer(ea, nbytes): + global ida_colorizer + orig = idaapi.call_nav_colorizer(ida_colorizer, ea, nbytes) + return long(~orig) + + ida_colorizer = idaapi.set_nav_colorizer(my_colorizer) + """ + pass +# +*/ +nav_colorizer_t *py_set_nav_colorizer(PyObject *new_py_colorizer) +{ + static ref_t py_colorizer; + struct ida_local lambda_t + { + static uint32 idaapi call_py_colorizer(ea_t ea, asize_t nbytes) + { + PYW_GIL_GET; + + if ( py_colorizer == NULL ) // Shouldn't happen. + return 0; + newref_t pyres = PyObject_CallFunction( + py_colorizer.o, "KK", + (unsigned long long) ea, + (unsigned long long) nbytes); + PyW_ShowCbErr("nav_colorizer"); + if ( pyres.o == NULL ) + return 0; + if ( !PyLong_Check(pyres.o) ) + { + static bool warned = false; + if ( !warned ) + { + msg("WARNING: set_nav_colorizer() callback must return a 'long'.\n"); + warned = true; + } + return 0; + } + return PyLong_AsLong(pyres.o); + } + }; + + bool need_install = py_colorizer == NULL; + py_colorizer = borref_t(new_py_colorizer); + return need_install + ? set_nav_colorizer(lambda_t::call_py_colorizer) + : NULL; +} + +//------------------------------------------------------------------------- +/* +# +def call_nav_colorizer(colorizer, ea, nbytes): + """ + To be used with the IDA-provided colorizer, that is + returned as result of the first call to set_nav_colorizer(). + + This is a trivial trampoline, so that SWIG can generate a + wrapper that will do the types checking. + """ + pass +# +*/ +uint32 py_call_nav_colorizer( + nav_colorizer_t *col, + ea_t ea, + asize_t nbytes) +{ + return col(ea, nbytes); +} + + + +//--------------------------------------------------------------------------- +uint32 idaapi choose_sizer(void *self) +{ + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod((PyObject *)self, "sizer", "")); + return PyInt_AsLong(pyres.o); +} + +//--------------------------------------------------------------------------- +char *idaapi choose_getl(void *self, uint32 n, char *buf) +{ + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + (PyObject *)self, + "getl", + "l", + n)); + + const char *res; + if (pyres == NULL || (res = PyString_AsString(pyres.o)) == NULL ) + qstrncpy(buf, "", MAXSTR); + else + qstrncpy(buf, res, MAXSTR); + return buf; +} + +//--------------------------------------------------------------------------- +void idaapi choose_enter(void *self, uint32 n) +{ + PYW_GIL_GET; + newref_t res(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); +} + +//--------------------------------------------------------------------------- +uint32 choose_choose( + void *self, + int flags, + int x0,int y0, + int x1,int y1, + int width, + int deflt, + int icon) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); + const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; + + int r = choose( + flags, + x0, y0, + x1, y1, + self, + width, + choose_sizer, + choose_getl, + title, + icon, + deflt, + NULL, /* del */ + NULL, /* inst */ + NULL, /* update */ + NULL, /* edit */ + choose_enter, + NULL, /* destroy */ + NULL, /* popup_names */ + NULL);/* get_icon */ + + return r; +} + + +PyObject *choose2_find(const char *title); +int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon); +void choose2_refresh(PyObject *self); +void choose2_close(PyObject *self); +int choose2_create(PyObject *self, bool embedded); +void choose2_activate(PyObject *self); +PyObject *choose2_get_embedded(PyObject *self); +PyObject *choose2_get_embedded_selection(PyObject *self); + + +#define DECLARE_FORM_ACTIONS form_actions_t *fa = (form_actions_t *)p_fa; + +//--------------------------------------------------------------------------- +static bool textctrl_info_t_assign(PyObject *self, PyObject *other) +{ + textctrl_info_t *lhs = textctrl_info_t_get_clink(self); + textctrl_info_t *rhs = textctrl_info_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_text(PyObject *self, const char *s) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->text = s; + return true; +} + +//------------------------------------------------------------------------- +static const char *textctrl_info_t_get_text(PyObject *self) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? "" : ti->text.c_str(); +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_flags(PyObject *self, unsigned int flags) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->flags = flags; + return true; +} + +//------------------------------------------------------------------------- +static unsigned int textctrl_info_t_get_flags( + PyObject *self, + unsigned int flags) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? 0 : ti->flags; +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_tabsize( + PyObject *self, + unsigned int tabsize) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->tabsize = tabsize; + return true; +} + +//------------------------------------------------------------------------- +static unsigned int textctrl_info_t_get_tabsize( + PyObject *self, + unsigned int tabsize) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? 0 : ti->tabsize; +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_enable_field(size_t p_fa, int fid, bool enable) +{ + DECLARE_FORM_ACTIONS; + return fa->enable_field(fid, enable); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_show_field(size_t p_fa, int fid, bool show) +{ + DECLARE_FORM_ACTIONS; + return fa->show_field(fid, show); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_move_field( + size_t p_fa, + int fid, + int x, + int y, + int w, + int h) +{ + DECLARE_FORM_ACTIONS; + return fa->move_field(fid, x, y, w, h); +} + +//--------------------------------------------------------------------------- +static int formchgcbfa_get_focused_field(size_t p_fa) +{ + DECLARE_FORM_ACTIONS; + return fa->get_focused_field(); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_set_focused_field(size_t p_fa, int fid) +{ + DECLARE_FORM_ACTIONS; + return fa->set_focused_field(fid); +} + +//--------------------------------------------------------------------------- +static void formchgcbfa_refresh_field(size_t p_fa, int fid) +{ + DECLARE_FORM_ACTIONS; + return fa->refresh_field(fid); +} + +//--------------------------------------------------------------------------- +static void formchgcbfa_close(size_t p_fa, int close_normally) +{ + DECLARE_FORM_ACTIONS; + fa->close(close_normally); +} + +//--------------------------------------------------------------------------- +static PyObject *formchgcbfa_get_field_value( + size_t p_fa, + int fid, + int ft, + size_t sz) +{ + DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + switch ( ft ) + { + // dropdown list + case 8: + { + // Readonly? Then return the selected index + if ( sz == 1 ) + { + int sel_idx; + if ( fa->get_combobox_value(fid, &sel_idx) ) + return PyLong_FromLong(sel_idx); + } + // Not readonly? Then return the qstring + else + { + qstring val; + if ( fa->get_combobox_value(fid, &val) ) + return PyString_FromString(val.c_str()); + } + break; + } + // multilinetext - tuple representing textctrl_info_t + case 7: + { + textctrl_info_t ti; + if ( fa->get_text_value(fid, &ti) ) + return Py_BuildValue("(sII)", ti.text.c_str(), ti.flags, ti.tabsize); + break; + } + // button - uint32 + case 4: + { + uval_t val; + if ( fa->get_unsigned_value(fid, &val) ) + return PyLong_FromUnsignedLong(val); + break; + } + // ushort + case 2: + { + ushort val; + if ( fa->_get_field_value(fid, &val) ) + return PyLong_FromUnsignedLong(val); + break; + } + // string label + case 1: + { + char val[MAXSTR]; + if ( fa->get_ascii_value(fid, val, sizeof(val)) ) + return PyString_FromString(val); + break; + } + // string input + case 3: + { + qstring val; + val.resize(sz + 1); + if ( fa->get_ascii_value(fid, val.begin(), val.size()) ) + return PyString_FromString(val.begin()); + break; + } + case 5: + { + intvec_t intvec; + // Returned as 1-base + if (fa->get_chooser_value(fid, &intvec)) + { + // Make 0-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)--; + ref_t l(PyW_IntVecToPyList(intvec)); + l.incref(); + return l.o; + } + break; + } + // Numeric control + case 6: + { + union + { + sel_t sel; + sval_t sval; + uval_t uval; + ulonglong ull; + } u; + switch ( sz ) + { + case 'S': // sel_t + { + if ( fa->get_segment_value(fid, &u.sel) ) + return Py_BuildValue(PY_FMT64, u.sel); + break; + } + // sval_t + case 'n': + case 'D': + case 'O': + case 'Y': + case 'H': + { + if ( fa->get_signed_value(fid, &u.sval) ) + return Py_BuildValue(PY_SFMT64, u.sval); + break; + } + case 'L': // uint64 + case 'l': // int64 + { + if ( fa->_get_field_value(fid, &u.ull) ) + return Py_BuildValue("K", u.ull); + break; + } + case 'N': + case 'M': // uval_t + { + if ( fa->get_unsigned_value(fid, &u.uval) ) + return Py_BuildValue(PY_FMT64, u.uval); + break; + } + case '$': // ea_t + { + if ( fa->get_ea_value(fid, &u.uval) ) + return Py_BuildValue(PY_FMT64, u.uval); + break; + } + } + break; + } + } + Py_RETURN_NONE; +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_set_field_value( + size_t p_fa, + int fid, + int ft, + PyObject *py_val) +{ + DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + + switch ( ft ) + { + // dropdown list + case 8: + { + // Editable dropdown list + if ( PyString_Check(py_val) ) + { + qstring val(PyString_AsString(py_val)); + return fa->set_combobox_value(fid, &val); + } + // Readonly dropdown list + else + { + int sel_idx = PyLong_AsLong(py_val); + return fa->set_combobox_value(fid, &sel_idx); + } + break; + } + // multilinetext - textctrl_info_t + case 7: + { + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(py_val); + return ti == NULL ? false : fa->set_text_value(fid, ti); + } + // button - uint32 + case 4: + { + uval_t val = PyLong_AsUnsignedLong(py_val); + return fa->set_unsigned_value(fid, &val); + } + // ushort + case 2: + { + ushort val = PyLong_AsUnsignedLong(py_val) & 0xffff; + return fa->_set_field_value(fid, &val); + } + // strings + case 3: + case 1: + return fa->set_ascii_value(fid, PyString_AsString(py_val)); + // intvec_t + case 5: + { + intvec_t intvec; + // Passed as 0-based + if ( !PyW_PyListToIntVec(py_val, intvec) ) + break; + + // Make 1-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)++; + + return fa->set_chooser_value(fid, &intvec); + } + // Numeric + case 6: + { + uint64 num; + if ( PyW_GetNumber(py_val, &num) ) + return fa->_set_field_value(fid, &num); + } + } + return false; +} + +#undef DECLARE_FORM_ACTIONS + +static size_t py_get_AskUsingForm() +{ + // Return a pointer to the function. Note that, although + // the C implementation of AskUsingForm_cv will do some + // Qt/txt widgets generation, the Python's ctypes + // implementation through which the call well go will first + // unblock other threads. No need to do it ourselves. + return (size_t)AskUsingForm_c; +} + +static size_t py_get_OpenForm() +{ + // See comments above. + return (size_t)OpenForm_c; +} + +static qvector py_compiled_form_vec; +static void py_register_compiled_form(PyObject *py_form) +{ + ref_t ref = borref_t(py_form); + if ( !py_compiled_form_vec.has(ref) ) + py_compiled_form_vec.push_back(ref); +} + +static void py_unregister_compiled_form(PyObject *py_form) +{ + ref_t ref = borref_t(py_form); + if ( py_compiled_form_vec.has(ref) ) + py_compiled_form_vec.del(ref); +} + +// +%} + +%{ +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +int idaapi UI_Callback(void *ud, int notification_code, va_list va) +{ + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + UI_Hooks *proxy = (UI_Hooks *)ud; + int ret = 0; + try + { + switch (notification_code) + { + case ui_preprocess: + { + const char *name = va_arg(va, const char *); + return proxy->preprocess(name); + } + + case ui_postprocess: + proxy->postprocess(); + break; + + case ui_saving: + proxy->saving(); + break; + + case ui_saved: + proxy->saved(); + break; + + case ui_term: + proxy->term(); + break; + + case ui_get_ea_hint: + { + ea_t ea = va_arg(va, ea_t); + char *buf = va_arg(va, char *); + size_t sz = va_arg(va, size_t); + char *_buf; + Py_ssize_t _len; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_str = proxy->get_ea_hint(ea); + if ( py_str != NULL + && PyString_Check(py_str) + && PyString_AsStringAndSize(py_str, &_buf, &_len) != - 1 ) + { + qstrncpy(buf, _buf, qmin(_len, sz)); + ret = 1; + } + break; + } + + case ui_current_tform_changed: + { + TForm *form = va_arg(va, TForm *); + TForm *prev_form = va_arg(va, TForm *); + proxy->current_tform_changed(form, prev_form); + } + break; + + case ui_updating_actions: + { + action_update_ctx_t *ctx = va_arg(va, action_update_ctx_t *); + proxy->updating_actions(ctx); + } + break; + + + case ui_updated_actions: + { + proxy->updated_actions(); + } + break; + + case ui_populating_tform_popup: + { + TForm *form = va_arg(va, TForm *); + TPopupMenu *popup = va_arg(va, TPopupMenu *); + proxy->populating_tform_popup(form, popup); + } + break; + + case ui_finish_populating_tform_popup: + { + TForm *form = va_arg(va, TForm *); + TPopupMenu *popup = va_arg(va, TPopupMenu *); + proxy->finish_populating_tform_popup(form, popup); + } + break; + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in UI Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyErr_Occurred() ) + PyErr_Print(); + } + return ret; +} + +//------------------------------------------------------------------------ +bool idaapi py_menu_item_callback(void *userdata) +{ + PYW_GIL_GET; + + // userdata is a tuple of ( func, args ) + // func and args are borrowed references from userdata + PyObject *func = PyTuple_GET_ITEM(userdata, 0); + PyObject *args = PyTuple_GET_ITEM(userdata, 1); + + // Call the python function + newref_t result(PyEval_CallObject(func, args)); + + // We cannot raise an exception in the callback, just print it. + if ( result == NULL ) + { + PyErr_Print(); + return false; + } + + return PyObject_IsTrue(result.o) != 0; +} + + + +//------------------------------------------------------------------------ +// Some defines +#define POPUP_NAMES_COUNT 4 +#define MAX_CHOOSER_MENU_COMMANDS 20 +#define thisobj ((py_choose2_t *) obj) +#define thisdecl py_choose2_t *_this = thisobj +#define MENU_COMMAND_CB(id) \ + static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) \ + { \ + return thisobj->on_command(id, int(n)); \ + } + +//------------------------------------------------------------------------ +// Helper functions +class py_choose2_t; +typedef std::map pychoose2_to_choose2_map_t; +static pychoose2_to_choose2_map_t choosers; + +py_choose2_t *choose2_find_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + return it == choosers.end() ? NULL : it->second; +} + +void choose2_add_instance(PyObject *self, py_choose2_t *c2) +{ + choosers[self] = c2; +} + +void choose2_del_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + if ( it != choosers.end() ) + choosers.erase(it); +} + +//------------------------------------------------------------------------ +class py_choose2_t +{ +private: + enum + { + CHOOSE2_HAVE_DEL = 0x0001, + CHOOSE2_HAVE_INS = 0x0002, + CHOOSE2_HAVE_UPDATE = 0x0004, + CHOOSE2_HAVE_EDIT = 0x0008, + CHOOSE2_HAVE_ENTER = 0x0010, + CHOOSE2_HAVE_GETICON = 0x0020, + CHOOSE2_HAVE_GETATTR = 0x0040, + CHOOSE2_HAVE_COMMAND = 0x0080, + CHOOSE2_HAVE_ONCLOSE = 0x0100, + CHOOSE2_HAVE_SELECT = 0x0200, + CHOOSE2_HAVE_REFRESHED = 0x0400, + }; + // Chooser flags + int flags; + + // Callback flags (to tell which callback exists and which not) + // One of CHOOSE2_HAVE_xxxx + unsigned int cb_flags; + chooser_info_t *embedded; + intvec_t embedded_sel; + + // Menu callback index (in the menu_cbs array) + int menu_cb_idx; + + // Chooser title + qstring title; + + // Column widths + intvec_t widths; + + // Python object link + PyObject *self; + // Chooser columns + qstrvec_t cols; + const char **popup_names; + bool ui_cb_hooked; + + // The number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value + MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) + MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) + MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) + MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) + MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) + MENU_COMMAND_CB(10) MENU_COMMAND_CB(11) + MENU_COMMAND_CB(12) MENU_COMMAND_CB(13) + MENU_COMMAND_CB(14) MENU_COMMAND_CB(15) + MENU_COMMAND_CB(16) MENU_COMMAND_CB(17) + MENU_COMMAND_CB(18) MENU_COMMAND_CB(19) + static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; + + //------------------------------------------------------------------------ + // Static methods to dispatch to member functions + //------------------------------------------------------------------------ + static int idaapi ui_cb(void *obj, int notification_code, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + // UI callback to handle chooser items with attributes + if ( notification_code != ui_get_chooser_item_attrs ) + return 0; + + // Pass events that belong to our chooser only + void *chooser_obj = va_arg(va, void *); + if ( obj != chooser_obj ) + return 0; + + int n = int(va_arg(va, uint32)); + chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); + thisobj->on_get_line_attr(n, attr); + return 1; + } + + static void idaapi s_select(void *obj, const intvec_t &sel) + { + thisobj->on_select(sel); + } + + static void idaapi s_refreshed(void *obj) + { + thisobj->on_refreshed(); + } + + static uint32 idaapi s_sizer(void *obj) + { + return (uint32)thisobj->on_get_size(); + } + + static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) + { + thisobj->on_get_line(int(n), arrptr); + } + + static uint32 idaapi s_del(void *obj, uint32 n) + { + return uint32(thisobj->on_delete_line(int(n))); + } + + static void idaapi s_ins(void *obj) + { + thisobj->on_insert_line(); + } + + static uint32 idaapi s_update(void *obj, uint32 n) + { + return uint32(thisobj->on_refresh(int(n))); + } + + static void idaapi s_edit(void *obj, uint32 n) + { + thisobj->on_edit_line(int(n)); + } + + static void idaapi s_enter(void * obj, uint32 n) + { + thisobj->on_enter(int(n)); + } + + static int idaapi s_get_icon(void *obj, uint32 n) + { + return thisobj->on_get_icon(int(n)); + } + + static void idaapi s_destroy(void *obj) + { + thisobj->on_close(); + } + + //------------------------------------------------------------------------ + // Member functions corresponding to each chooser2() callback + //------------------------------------------------------------------------ + void clear_popup_names() + { + if ( popup_names == NULL ) + return; + + for ( int i=0; i=0; i-- ) + line_arr[i][0] = '\0'; + + // Call Python + PYW_GIL_CHECK_LOCKED_SCOPE(); + pycall_res_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1)); + if ( list.result == NULL ) + return; + + // Go over the List returned by Python and convert to C strings + for ( int i=ncols-1; i>=0; i-- ) + { + borref_t item(PyList_GetItem(list.result.o, Py_ssize_t(i))); + if ( item == NULL ) + continue; + + const char *str = PyString_AsString(item.o); + if ( str != NULL ) + qstrncpy(line_arr[i], str, MAXSTR); + } + } + + size_t on_get_size() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL)); + if ( pyres.result == NULL ) + return 0; + + return PyInt_AsLong(pyres.result.o); + } + + void on_refreshed() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL)); + } + + void on_select(const intvec_t &intvec) + { + PYW_GIL_GET; + ref_t py_list(PyW_IntVecToPyList(intvec)); + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECTION_CHANGE, "O", py_list.o)); + } + + void on_close() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL)); + + // Delete this instance if none modal and not embedded + if ( !is_modal() && get_embedded() == NULL ) + delete this; + } + + int on_delete_line(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_DELETE_LINE, + "i", + IS_CHOOSER_EVENT(lineno) ? lineno : lineno-1)); + return pyres.result == NULL ? 1 : PyInt_AsLong(pyres.result.o); + } + + int on_refresh(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_REFRESH, + "i", + lineno - 1)); + return pyres.result == NULL ? lineno : PyInt_AsLong(pyres.result.o) + 1; + } + + void on_insert_line() + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL)); + } + + void on_enter(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_SELECT_LINE, + "i", + lineno - 1)); + } + + void on_edit_line(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_EDIT_LINE, + "i", + lineno - 1)); + } + + int on_command(int cmd_id, int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_COMMAND, + "ii", + lineno - 1, + cmd_id)); + return pyres.result == NULL ? lineno : PyInt_AsLong(pyres.result.o); + } + + int on_get_icon(int lineno) + { + PYW_GIL_GET; + pycall_res_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_GET_ICON, + "i", + lineno - 1)); + return PyInt_AsLong(pyres.result.o); + } + + void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) + { + PYW_GIL_GET; + pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1)); + if ( pyres.result != NULL ) + { + if ( PyList_Check(pyres.result.o) ) + { + PyObject *item; + if ( (item = PyList_GetItem(pyres.result.o, 0)) != NULL ) + attr->color = PyInt_AsLong(item); + if ( (item = PyList_GetItem(pyres.result.o, 1)) != NULL ) + attr->flags = PyInt_AsLong(item); + } + } + } + + bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const + { + if ( get_embedded() != NULL ) + { + // For embedded chooser, the "caption" will be overloaded to encode + // the AskUsingForm's title, caption and embedded chooser id + // Title:EmbeddedChooserID:Caption + + char title_buf[MAXSTR]; + const char *ptitle; + + static const char delimiter[] = ":"; + char temp[MAXSTR]; + qstrncpy(temp, caption, sizeof(temp)); + + char *ctx; + char *p = qstrtok(temp, delimiter, &ctx); + if ( p == NULL ) + return false; + + // Copy the title + char title_str[MAXSTR]; + qstrncpy(title_str, p, sizeof(title_str)); + + // Copy the echooser ID + p = qstrtok(NULL, delimiter, &ctx); + if ( p == NULL ) + return false; + + char id_str[10]; + qstrncpy(id_str, p, sizeof(id_str)); + + // Form the new title of the form: "AskUsingFormTitle:EchooserId" + qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str); + + // Adjust the title + *out_title = title_buf; + + // Adjust the caption + p = qstrtok(NULL, delimiter, &ctx); + *out_caption = caption + (p - temp); + } + else + { + *out_title = title; + *out_caption = caption; + } + return true; + } + + // This must be called at the end of create(), when many dependencies + // have been computed (title, widths, popup_names, [cb_]flags, ...) + void fill_chooser_info( + chooser_info_t *out, + int deflt, + int desired_width, + int desired_height, + int icon) + { + memset(out, 0, sizeof(*out)); + out->obj = this; + out->cb = sizeof(*out); + out->title = title.c_str(); + out->columns = widths.size(); + out->deflt = deflt; + out->flags = flags; + out->width = desired_width; + out->height = desired_height; + out->icon = icon; + out->popup_names = popup_names; + out->widths = widths.begin(); + out->destroyer = s_destroy; + out->getl = s_getl; + out->sizer = s_sizer; + out->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL; + out->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL; + out->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL; + out->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL; + out->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL; + out->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL; + out->get_attrs = NULL; + out->initializer = NULL; + // Fill callbacks that are only present in idaq + if ( is_idaq() ) + { + out->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL; + out->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL; + } + else + { + out->select = NULL; + out->refresh = NULL; + } + } + +public: + //------------------------------------------------------------------------ + // Public methods + //------------------------------------------------------------------------ + py_choose2_t(): flags(0), cb_flags(0), + embedded(NULL), menu_cb_idx(0), + self(NULL), popup_names(NULL), ui_cb_hooked(false) + { + } + + ~py_choose2_t() + { + // Remove from list + choose2_del_instance(self); + + // Uninstall hooks + install_hooks(false); + + delete embedded; + Py_XDECREF(self); + clear_popup_names(); + } + + static py_choose2_t *find_chooser(const char *title) + { + return (py_choose2_t *) get_chooser_obj(title); + } + + void close() + { + // Will trigger on_close() + close_chooser(title.c_str()); + } + + bool activate() + { + TForm *frm = find_tform(title.c_str()); + if ( frm == NULL ) + return false; + + switchto_tform(frm, true); + return true; + } + + int add_command( + const char *_caption, + int flags=0, + int menu_index=-1, + int icon=-1) + { + if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) + return -1; + + qstring title, caption; + if ( !split_chooser_caption(&title, &caption, _caption) + || !add_chooser_command( + title.c_str(), + caption.c_str(), + menu_cbs[menu_cb_idx], + menu_index, + icon, + flags) ) + return -1; + + return menu_cb_idx++; + } + + // Create a chooser. + // If it detects the "embedded" attribute, then it will create a chooser_info_t structure + // Otherwise the chooser window is created and displayed + int create(PyObject *self) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Get flags + ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS)); + if ( flags_attr == NULL ) + return -1; + flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0; + + // Get the title + if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) + return -1; + + // Get columns + ref_t cols_attr(PyW_TryGetAttrString(self, "cols")); + if ( cols_attr == NULL ) + return -1; + + // Get col count + int ncols = int(PyList_Size(cols_attr.o)); + + // Get cols caption and widthes + cols.qclear(); + for ( int i=0; iself = self; + + // Hook to notification point (to handle chooser item attributes) + install_hooks(true); + + // Check if *embedded + ref_t emb_attr(PyW_TryGetAttrString(self, S_EMBEDDED)); + int rc; + if ( emb_attr != NULL && PyObject_IsTrue(emb_attr.o) == 1 ) + { + // Create an embedded chooser structure + embedded = new chooser_info_t(); + fill_chooser_info(embedded, deflt, pts[0], pts[1], icon); + rc = 1; // success + } + else + { + chooser_info_t ci; + fill_chooser_info(&ci, deflt, -1, -1, icon); + rc = choose3(&ci); + clear_popup_names(); + if ( is_modal() ) + --rc; // modal chooser return the index of the selected item + } + return rc; + } + + inline PyObject *get_self() + { + return self; + } + + void refresh() + { + refresh_chooser(title.c_str()); + } + + bool is_modal() + { + return (flags & CH_MODAL) != 0; + } + + intvec_t *get_sel_vec() + { + return &embedded_sel; + } + + chooser_info_t *get_embedded() const + { + return embedded; + } +}; + +//------------------------------------------------------------------------ +// Initialize the callback pointers +#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id +chooser_cb_t *py_choose2_t::menu_cbs[MAX_CHOOSER_MENU_COMMANDS] = +{ + DECL_MENU_COMMAND_CB(0), DECL_MENU_COMMAND_CB(1), + DECL_MENU_COMMAND_CB(2), DECL_MENU_COMMAND_CB(3), + DECL_MENU_COMMAND_CB(4), DECL_MENU_COMMAND_CB(5), + DECL_MENU_COMMAND_CB(6), DECL_MENU_COMMAND_CB(7), + DECL_MENU_COMMAND_CB(8), DECL_MENU_COMMAND_CB(9), + DECL_MENU_COMMAND_CB(10), DECL_MENU_COMMAND_CB(11), + DECL_MENU_COMMAND_CB(12), DECL_MENU_COMMAND_CB(13), + DECL_MENU_COMMAND_CB(14), DECL_MENU_COMMAND_CB(15), + DECL_MENU_COMMAND_CB(16), DECL_MENU_COMMAND_CB(17), + DECL_MENU_COMMAND_CB(18), DECL_MENU_COMMAND_CB(19) +}; +#undef DECL_MENU_COMMAND_CB + +#undef POPUP_NAMES_COUNT +#undef MAX_CHOOSER_MENU_COMMANDS +#undef thisobj +#undef thisdecl +#undef MENU_COMMAND_CB + +//------------------------------------------------------------------------ +int choose2_create(PyObject *self, bool embedded) +{ + py_choose2_t *c2; + + c2 = choose2_find_instance(self); + if ( c2 != NULL ) + { + if ( !embedded ) + c2->activate(); + return 1; + } + + c2 = new py_choose2_t(); + + choose2_add_instance(self, c2); + + int r = c2->create(self); + // Non embedded chooser? Return immediately + if ( !embedded ) + return r; + + // Embedded chooser was not created? + if ( c2->get_embedded() == NULL || r != 1 ) + { + delete c2; + r = 0; + } + return r; +} + +//------------------------------------------------------------------------ +void choose2_close(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 == NULL ) + return; + + // Modal or embedded chooser? + if ( c2->get_embedded() != NULL || c2->is_modal() ) + { + // Then simply delete the instance + delete c2; + } + else + { + // Close the chooser. + // In turn this will lead to the deletion of the object + c2->close(); + } +} + +//------------------------------------------------------------------------ +void choose2_refresh(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->refresh(); +} + +//------------------------------------------------------------------------ +void choose2_activate(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->activate(); +} + +//------------------------------------------------------------------------ +PyObject *choose2_get_embedded_selection(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_choose2_t *c2 = choose2_find_instance(self); + chooser_info_t *embedded; + + if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) + Py_RETURN_NONE; + + // Returned as 1-based + intvec_t &intvec = *c2->get_sel_vec(); + + // Make 0-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)--; + + ref_t ret(PyW_IntVecToPyList(intvec)); + ret.incref(); + return ret.o; +} + +//------------------------------------------------------------------------ +// Return the C instances as 64bit numbers +PyObject *choose2_get_embedded(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_choose2_t *c2 = choose2_find_instance(self); + chooser_info_t *embedded; + + if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) + Py_RETURN_NONE; + else + return Py_BuildValue("(KK)", + PY_ULONG_LONG(embedded), + PY_ULONG_LONG(c2->get_sel_vec())); +} + +//------------------------------------------------------------------------ +int choose2_add_command( + PyObject *self, + const char *caption, + int flags=0, + int menu_index=-1, + int icon=-1) +{ + py_choose2_t *c2 = choose2_find_instance(self); + return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon); +} + +//------------------------------------------------------------------------ +PyObject *choose2_find(const char *title) +{ + py_choose2_t *c2 = py_choose2_t::find_chooser(title); + return c2 == NULL ? NULL : c2->get_self(); +} + + +void free_compiled_form_instances(void) +{ + while ( !py_compiled_form_vec.empty() ) + { + const ref_t &ref = py_compiled_form_vec[0]; + qstring title; + if ( !PyW_GetStringAttr(ref.o, "title", &title) ) + title = ""; + msg("WARNING: Form \"%s\" was not Free()d. Force-freeing.\n", title.c_str()); + // Will call 'py_unregister_compiled_form()', and thus trim the vector down. + newref_t unused(PyObject_CallMethod(ref.o, (char *)"Free", "()")); + } +} +// + +%} + +%{ +// +//-------------------------------------------------------------------------- +#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, + int *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, int *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) + { + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_EXECUTE_LINE, + "s", + line)); + PyW_ShowCbErr(S_ON_EXECUTE_LINE); + return result != NULL && PyObject_IsTrue(result.o); + } + + //-------------------------------------------------------------------------- + // 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, + int *vk_key, + int shift) + { + PYW_GIL_GET; + newref_t 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.o); + + PyW_ShowCbErr(S_ON_KEYDOWN); + + if ( ok ) + { + Py_ssize_t sz = PyTuple_Size(result.o); + PyObject *item; + +#define GET_TUPLE_ENTRY(col, PyThingy, AsThingy, out) \ + do \ + { \ + if ( sz > col ) \ + { \ + borref_t _r(PyTuple_GetItem(result.o, col)); \ + if ( _r != NULL && PyThingy##_Check(_r.o) ) \ + *out = PyThingy##_##AsThingy(_r.o); \ + } \ + } while ( false ) + + GET_TUPLE_ENTRY(0, PyString, AsString, line); + GET_TUPLE_ENTRY(1, PyInt, AsLong, p_x); + GET_TUPLE_ENTRY(2, PyInt, AsLong, p_sellen); + GET_TUPLE_ENTRY(3, PyInt, AsLong, vk_key); + *vk_key &= 0xffff; +#undef GET_TUPLE_ENTRY + } + 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) + { + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_COMPLETE_LINE, + "sisi", + prefix, + n, + line, + x)); + + bool ok = result != NULL && PyString_Check(result.o); + PyW_ShowCbErr(S_ON_COMPLETE_LINE); + if ( ok ) + *completion = PyString_AsString(result.o); + return ok; + } + + // Private ctor (use bind()) + py_cli_t() + { + } + +public: + //--------------------------------------------------------------------------- + static int bind(PyObject *py_obj) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + 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' + { + ref_t flags_attr(PyW_TryGetAttrString(py_obj, S_FLAGS)); + if ( flags_attr == NULL ) + py_cli->cli.flags = 0; + else + py_cli->cli.flags = PyLong_AsLong(flags_attr.o); + } + + // 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); + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + 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 +// + +// +//--------------------------------------------------------------------------- +class plgform_t +{ +private: + ref_t py_obj; + TForm *form; + + static int idaapi s_callback(void *ud, int notification_code, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + + plgform_t *_this = (plgform_t *)ud; + if ( notification_code == ui_tform_visible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + // Qt: QWidget* + // G: HWND + // We wrap and pass as a CObject in the hope that a Python UI framework + // can unwrap a CObject and get the hwnd/widget back + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CREATE, "O", + PyCObject_FromVoidPtr(form, NULL))); + PyW_ShowCbErr(S_ON_CREATE); + } + } + else if ( notification_code == ui_tform_invisible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + { + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CLOSE, "O", + PyCObject_FromVoidPtr(form, NULL))); + PyW_ShowCbErr(S_ON_CLOSE); + } + _this->unhook(); + } + } + return 0; + } + + void unhook() + { + unhook_from_notification_point(HT_UI, s_callback, this); + form = NULL; + + // Call DECREF at last, since it may trigger __del__ + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_obj = ref_t(); + } + +public: + plgform_t(): form(NULL) + { + } + + bool show( + PyObject *obj, + const char *caption, + int options) + { + // Already displayed? + TForm *f = find_tform(caption); + if ( f != NULL ) + { + // Our form? + if ( f == form ) + { + // Switch to it + switchto_tform(form, true); + return true; + } + // Fail to create + return false; + } + + // Create a form + form = create_tform(caption, NULL); + if ( form == NULL ) + return false; + + if ( !hook_to_notification_point(HT_UI, s_callback, this) ) + { + form = NULL; + return false; + } + + py_obj = borref_t(obj); + + if ( is_idaq() ) + options |= FORM_QWIDGET; + + this->form = form; + open_tform(form, options); + return true; + } + + void close(int options = 0) + { + if ( form != NULL ) + close_tform(form, options); + } + + static PyObject *create() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new plgform_t(), destroy); + } + + static void destroy(void *obj) + { + delete (plgform_t *)obj; + } +}; +// +%} + +%inline %{ +// +//--------------------------------------------------------------------------- +#define DECL_PLGFORM PYW_GIL_CHECK_LOCKED_SCOPE(); plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); +static PyObject *plgform_new() +{ + return plgform_t::create(); +} + +static bool plgform_show( + PyObject *py_link, + PyObject *py_obj, + const char *caption, + int options = FORM_TAB|FORM_MENU|FORM_RESTORE) +{ + DECL_PLGFORM; + return plgform->show(py_obj, caption, options); +} + +static void plgform_close( + PyObject *py_link, + int options) +{ + DECL_PLGFORM; + plgform->close(options); +} +#undef DECL_PLGFORM +// +%} + +%{ +// +//--------------------------------------------------------------------------- +// Base class for all custviewer place_t providers +class custviewer_data_t +{ +public: + virtual void *get_ud() = 0; + virtual place_t *get_min() = 0; + virtual place_t *get_max() = 0; +}; + +//--------------------------------------------------------------------------- +class cvdata_simpleline_t: public custviewer_data_t +{ +private: + strvec_t lines; + simpleline_place_t pl_min, pl_max; +public: + + void *get_ud() + { + return &lines; + } + + place_t *get_min() + { + return &pl_min; + } + + place_t *get_max() + { + return &pl_max; + } + + strvec_t &get_lines() + { + return lines; + } + + void set_minmax(size_t start=0, size_t end=size_t(-1)) + { + if ( start == 0 && end == size_t(-1) ) + { + end = lines.size(); + pl_min.n = 0; + pl_max.n = end == 0 ? 0 : end - 1; + } + else + { + pl_min.n = start; + pl_max.n = end; + } + } + + bool set_line(size_t nline, simpleline_t &sl) + { + if ( nline >= lines.size() ) + return false; + lines[nline] = sl; + return true; + } + + bool del_line(size_t nline) + { + if ( nline >= lines.size() ) + return false; + lines.erase(lines.begin()+nline); + return true; + } + + void add_line(simpleline_t &line) + { + lines.push_back(line); + } + + void add_line(const char *str) + { + lines.push_back(simpleline_t(str)); + } + + bool insert_line(size_t nline, simpleline_t &line) + { + if ( nline >= lines.size() ) + return false; + lines.insert(lines.begin()+nline, line); + return true; + } + + bool patch_line(size_t nline, size_t offs, int value) + { + if ( nline >= lines.size() ) + return false; + qstring &L = lines[nline].line; + L[offs] = (uchar) value & 0xFF; + return true; + } + + const size_t to_lineno(place_t *pl) const + { + return ((simpleline_place_t *)pl)->n; + } + + bool curline(place_t *pl, size_t *n) + { + if ( pl == NULL ) + return false; + + *n = to_lineno(pl); + return true; + } + + simpleline_t *get_line(size_t nline) + { + return nline >= lines.size() ? NULL : &lines[nline]; + } + + simpleline_t *get_line(place_t *pl) + { + return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); + } + + const size_t count() const + { + return lines.size(); + } + + void clear_lines() + { + lines.clear(); + set_minmax(); + } +}; + +//--------------------------------------------------------------------------- +// FIXME: This should inherit py_view_base.hpp's py_customidamemo_t, +// just like py_graph.hpp's py_graph_t does. +// There should be a way to "merge" the two mechanisms; they are similar. +class customviewer_t +{ +protected: + qstring _title; + TForm *_form; + TCustomControl *_cv; + custviewer_data_t *_data; + int _features; + enum + { + HAVE_HINT = 0x0001, + HAVE_KEYDOWN = 0x0002, + HAVE_POPUP = 0x0004, + HAVE_DBLCLICK = 0x0008, + HAVE_CURPOS = 0x0010, + HAVE_CLICK = 0x0020, + HAVE_CLOSE = 0x0040 + }; +private: + struct cvw_popupctx_t + { + size_t menu_id; + customviewer_t *cv; + cvw_popupctx_t(): menu_id(0), cv(NULL) { } + cvw_popupctx_t(size_t mid, customviewer_t *v): menu_id(mid), cv(v) { } + }; + typedef std::map cvw_popupmap_t; + static cvw_popupmap_t _global_popup_map; + static size_t _global_popup_id; + qstring _curline; + intvec_t _installed_popups; + + static bool idaapi s_popup_menu_cb(void *ud) + { + size_t mid = (size_t)ud; + cvw_popupmap_t::iterator it = _global_popup_map.find(mid); + if ( it == _global_popup_map.end() ) + return false; + + PYW_GIL_GET; + return it->second.cv->on_popup_menu(it->second.menu_id); + } + + static bool idaapi s_cv_keydown( + TCustomControl * /*cv*/, + int vk_key, + int shift, + void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_keydown(vk_key, shift); + } + + // The popup menu is being constructed + static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + _this->on_popup(); + } + + // The user clicked + static bool idaapi s_cv_click(TCustomControl * /*cv*/, int shift, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_click(shift); + } + + // The user double clicked + static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_dblclick(shift); + } + + // Cursor position has been changed + static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) + { + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + _this->on_curpos_changed(); + } + + //-------------------------------------------------------------------------- + static int idaapi s_ui_cb(void *ud, int code, va_list va) + { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + customviewer_t *_this = (customviewer_t *)ud; + switch ( code ) + { + case ui_get_custom_viewer_hint: + { + TCustomControl *viewer = va_arg(va, TCustomControl *); + place_t *place = va_arg(va, place_t *); + int *important_lines = va_arg(va, int *); + qstring &hint = *va_arg(va, qstring *); + if ( (_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer ) + return 0; + else + return _this->on_hint(place, important_lines, hint) ? 1 : 0; + } + + case ui_tform_invisible: + { + TForm *form = va_arg(va, TForm *); + if ( _this->_form != form ) + break; + } + // fallthrough... + case ui_term: + unhook_from_notification_point(HT_UI, s_ui_cb, _this); + _this->on_close(); + _this->on_post_close(); + break; + } + + return 0; + } + + void on_post_close() + { + init_vars(); + clear_popup_menu(); + } + +public: + + inline TForm *get_tform() { return _form; } + inline TCustomControl *get_tcustom_control() { return _cv; } + + // + // All the overridable callbacks + // + + // OnClick + virtual bool on_click(int /*shift*/) { return false; } + + // OnDblClick + virtual bool on_dblclick(int /*shift*/) { return false; } + + // OnCurorPositionChanged + virtual void on_curpos_changed() { } + + // OnHostFormClose + virtual void on_close() { } + + // OnKeyDown + virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } + + // OnPopupShow + virtual bool on_popup() { return false; } + + // OnHint + virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } + + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) { return false; } + + void init_vars() + { + _data = NULL; + _features = 0; + _curline.clear(); + _cv = NULL; + _form = NULL; + } + + customviewer_t() + { + init_vars(); + } + + ~customviewer_t() + { + } + + //-------------------------------------------------------------------------- + void close() + { + if ( _form != NULL ) + close_tform(_form, FORM_SAVE | FORM_CLOSE_LATER); + } + + //-------------------------------------------------------------------------- + bool set_range( + const place_t *minplace = NULL, + const place_t *maxplace = NULL) + { + if ( _cv == NULL ) + return false; + + set_custom_viewer_range( + _cv, + minplace == NULL ? _data->get_min() : minplace, + maxplace == NULL ? _data->get_max() : maxplace); + return true; + } + + place_t *get_place( + bool mouse = false, + int *x = 0, + int *y = 0) + { + return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); + } + + //-------------------------------------------------------------------------- + bool refresh() + { + if ( _cv == NULL ) + return false; + + refresh_custom_viewer(_cv); + return true; + } + + //-------------------------------------------------------------------------- + bool refresh_current() + { + int x, y; + place_t *pl = get_place(false, &x, &y); + if ( pl == NULL ) + return false; + + return jumpto(pl, x, y); + } + + //-------------------------------------------------------------------------- + bool get_current_word(bool mouse, qstring &word) + { + // query the cursor position + int x, y; + if ( get_place(mouse, &x, &y) == NULL ) + return false; + + // query the line at the cursor + const char *line = get_current_line(mouse, true); + if ( line == NULL ) + return false; + + if ( x >= (int)strlen(line) ) + return false; + + // find the beginning of the word + const char *ptr = line + x; + while ( ptr > line && !qisspace(ptr[-1]) ) + ptr--; + + // find the end of the word + const char *begin = ptr; + ptr = line + x; + while ( !qisspace(*ptr) && *ptr != '\0' ) + ptr++; + + word.qclear(); + word.append(begin, ptr-begin); + return true; + } + + //-------------------------------------------------------------------------- + const char *get_current_line(bool mouse, bool notags) + { + const char *r = get_custom_viewer_curline(_cv, mouse); + if ( r == NULL || !notags ) + return r; + + size_t sz = strlen(r); + if ( sz == 0 ) + return r; + + _curline.resize(sz + 5, '\0'); + tag_remove(r, &_curline[0], sz + 1); + return _curline.c_str(); + } + + //-------------------------------------------------------------------------- + bool is_focused() + { + return get_current_viewer() == _cv; + } + + //-------------------------------------------------------------------------- + bool jumpto(place_t *place, int x, int y) + { + return ::jumpto(_cv, place, x, y); + } + + //-------------------------------------------------------------------------- + void clear_popup_menu() + { + if ( _cv != NULL ) + set_custom_viewer_popup_menu(_cv, NULL); + + for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); + it != it_end; + ++it) + { + _global_popup_map.erase(*it); + } + _installed_popups.clear(); + } + + //-------------------------------------------------------------------------- + size_t add_popup_menu( + const char *title, + const char *hotkey) + { + size_t menu_id = _global_popup_id + 1; + + // Overlap / already exists? + if (_cv == NULL || // No custviewer? + // Overlap? + menu_id == 0 || + // Already exists? + _global_popup_map.find(menu_id) != _global_popup_map.end()) + { + return 0; + } + add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); + + // Save global association + _global_popup_map[menu_id] = cvw_popupctx_t(menu_id, this); + _global_popup_id = menu_id; + + // Remember what menu IDs are set with this form + _installed_popups.push_back(menu_id); + return menu_id; + } + + //-------------------------------------------------------------------------- + bool create(const char *title, int features, custviewer_data_t *data) + { + // Already created? (in the instance) + if ( _form != NULL ) + return true; + + // Already created? (in IDA windows list) + HWND hwnd(NULL); + TForm *form = create_tform(title, &hwnd); + if ( hwnd == NULL ) + return false; + + _title = title; + _data = data; + _form = form; + _features = features; + + // Create the viewer + _cv = create_custom_viewer( + title, + (TWinControl *)_form, + _data->get_min(), + _data->get_max(), + _data->get_min(), + 0, + _data->get_ud()); + + // Set user-data + set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); + + // + // Set other optional callbacks + // + if ( (features & HAVE_KEYDOWN) != 0 ) + set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); + + if ( (features & HAVE_POPUP) != 0 ) + set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); + + if ( (features & HAVE_DBLCLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); + + if ( (features & HAVE_CURPOS) != 0 ) + set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); + + if ( (features & HAVE_CLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); + + // Hook to UI notifications (for TForm close event) + hook_to_notification_point(HT_UI, s_ui_cb, this); + + return true; + } + + //-------------------------------------------------------------------------- + bool show() + { + // Closed already? + if ( _form == NULL ) + return false; + + open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE|FORM_QWIDGET); + return true; + } +}; + +customviewer_t::cvw_popupmap_t customviewer_t::_global_popup_map; +size_t customviewer_t::_global_popup_id = 0; +//--------------------------------------------------------------------------- +class py_simplecustview_t: public customviewer_t +{ +private: + cvdata_simpleline_t data; + PyObject *py_self, *py_this, *py_last_link; + int features; + + //-------------------------------------------------------------------------- + // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t + static bool py_to_simpleline(PyObject *py, simpleline_t &sl) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( PyString_Check(py) ) + { + sl.line = PyString_AsString(py); + return true; + } + Py_ssize_t sz; + if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) + return false; + + PyObject *py_val = PyTuple_GetItem(py, 0); + if ( !PyString_Check(py_val) ) + return false; + + sl.line = PyString_AsString(py_val); + + if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) + sl.color = color_t(PyLong_AsUnsignedLong(py_val)); + + if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) + sl.bgcolor = PyLong_AsUnsignedLong(py_val); + + return true; + } + + // + // Callbacks + // + virtual bool on_click(int shift) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift)); + PyW_ShowCbErr(S_ON_CLICK); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + // OnDblClick + virtual bool on_dblclick(int shift) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift)); + PyW_ShowCbErr(S_ON_DBL_CLICK); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + // OnCurorPositionChanged + virtual void on_curpos_changed() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL)); + PyW_ShowCbErr(S_ON_CURSOR_POS_CHANGED); + } + + //-------------------------------------------------------------------------- + // OnHostFormClose + virtual void on_close() + { + // Call the close method if it is there and the object is still bound + if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL)); + PyW_ShowCbErr(S_ON_CLOSE); + + // Cleanup + Py_DECREF(py_self); + py_self = NULL; + } + } + + //-------------------------------------------------------------------------- + // OnKeyDown + virtual bool on_keydown(int vk_key, int shift) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_KEYDOWN, + "ii", + vk_key, + shift)); + + PyW_ShowCbErr(S_ON_KEYDOWN); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- +// OnPopupShow + virtual bool on_popup() + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP, + NULL)); + PyW_ShowCbErr(S_ON_POPUP); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + // OnHint + virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) + { + size_t ln = data.to_lineno(place); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_HINT, + PY_FMT64, + pyul_t(ln))); + + PyW_ShowCbErr(S_ON_HINT); + bool ok = py_result != NULL && PyTuple_Check(py_result.o) && PyTuple_Size(py_result.o) == 2; + if ( ok ) + { + if ( important_lines != NULL ) + *important_lines = PyInt_AsLong(PyTuple_GetItem(py_result.o, 0)); + hint = PyString_AsString(PyTuple_GetItem(py_result.o, 1)); + } + return ok; + } + + //-------------------------------------------------------------------------- + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP_MENU, + PY_FMT64, + pyul_t(menu_id))); + PyW_ShowCbErr(S_ON_POPUP_MENU); + return py_result != NULL && PyObject_IsTrue(py_result.o); + } + + //-------------------------------------------------------------------------- + void refresh_range() + { + data.set_minmax(); + set_range(); + } + +public: + py_simplecustview_t() + { + py_this = py_self = py_last_link = NULL; + } + ~py_simplecustview_t() + { + } + + //-------------------------------------------------------------------------- + // Edits an existing line + bool edit_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + + return data.set_line(nline, sl); + } + + // Low level: patches a line string directly + bool patch_line(size_t nline, size_t offs, int value) + { + return data.patch_line(nline, offs, value); + } + + // Insert a line + bool insert_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + return data.insert_line(nline, sl); + } + + // Adds a line tuple + bool add_line(PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + data.add_line(sl); + refresh_range(); + return true; + } + + //-------------------------------------------------------------------------- + bool del_line(size_t nline) + { + bool ok = data.del_line(nline); + if ( ok ) + refresh_range(); + return ok; + } + + //-------------------------------------------------------------------------- + // Gets the position and returns a tuple (lineno, x, y) + PyObject *get_pos(bool mouse) + { + place_t *pl; + int x, y; + pl = get_place(mouse, &x, &y); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( pl == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); + } + + //-------------------------------------------------------------------------- + // Returns the line tuple + PyObject *get_line(size_t nline) + { + simpleline_t *r = data.get_line(nline); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( r == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); + } + + // Returns the count of lines + const size_t count() const + { + return data.count(); + } + + // Clears lines + void clear() + { + data.clear_lines(); + refresh_range(); + } + + //-------------------------------------------------------------------------- + bool jumpto(size_t ln, int x, int y) + { + simpleline_place_t l(ln); + return customviewer_t::jumpto(&l, x, y); + } + + //-------------------------------------------------------------------------- + // Initializes and links the Python object to this class + bool init(PyObject *py_link, const char *title) + { + // Already created? + if ( _form != NULL ) + return true; + + // Probe callbacks + features = 0; + static struct + { + const char *cb_name; + int feature; + } const cbtable[] = + { + {S_ON_CLICK, HAVE_CLICK}, + {S_ON_CLOSE, HAVE_CLOSE}, + {S_ON_HINT, HAVE_HINT}, + {S_ON_KEYDOWN, HAVE_KEYDOWN}, + {S_ON_POPUP, HAVE_POPUP}, + {S_ON_DBL_CLICK, HAVE_DBLCLICK}, + {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} + }; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + for ( size_t i=0; i +%} + +%inline %{ +// +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); +} +// + +// +// +// Pywraps Simple Custom Viewer functions +// +PyObject *pyscv_init(PyObject *py_link, const char *title) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_simplecustview_t *_this = new py_simplecustview_t(); + bool ok = _this->init(py_link, title); + if ( !ok ) + { + delete _this; + Py_RETURN_NONE; + } + return _this->get_pythis(); +} +#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) + +//-------------------------------------------------------------------------- +bool pyscv_refresh(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh(); +} + +//-------------------------------------------------------------------------- +bool pyscv_delete(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + _this->close(); + delete _this; + return true; +} + +//-------------------------------------------------------------------------- +bool pyscv_refresh_current(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh_current(); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) +{ + DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + const char *line; + if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) + Py_RETURN_NONE; + return PyString_FromString(line); +} + +//-------------------------------------------------------------------------- +bool pyscv_is_focused(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->is_focused(); +} + +void pyscv_clear_popup_menu(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear_popup_menu(); +} + +size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); +} + +size_t pyscv_count(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->count(); +} + +bool pyscv_show(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? false : _this->show(); +} + +void pyscv_close(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->close(); +} + +bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->jumpto(ln, x, y); +} + +// Returns the line tuple +PyObject *pyscv_get_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } + return _this->get_line(nline); +} + +//-------------------------------------------------------------------------- +// Gets the position and returns a tuple (lineno, x, y) +PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) +{ + DECL_THIS; + if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } + return _this->get_pos(mouse); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_clear_lines(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear(); + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +// Adds a line tuple +bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->add_line(py_sl); +} + +//-------------------------------------------------------------------------- +bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->insert_line(nline, py_sl); +} + +//-------------------------------------------------------------------------- +bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) +{ + DECL_THIS; + return _this == NULL ? false : _this->patch_line(nline, offs, value); +} + +//-------------------------------------------------------------------------- +bool pyscv_del_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + return _this == NULL ? false : _this->del_line(nline); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_selection(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_RETURN_NONE; + } + return _this->py_get_selection(); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) +{ + DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( _this != NULL ) + { + qstring word; + if ( _this->get_current_word(mouse, word) ) + return PyString_FromString(word.c_str()); + } + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +// Edits an existing line +bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->edit_line(nline, py_sl); +} + +//------------------------------------------------------------------------- +TForm *pyscv_get_tform(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? NULL : _this->get_tform(); +} + +//------------------------------------------------------------------------- +TCustomControl *pyscv_get_tcustom_control(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? NULL : _this->get_tcustom_control(); +} + + +#undef DECL_THIS +// +%} + +%include "kernwin.hpp" + +%template(disasm_text_t) qvector; + +%extend action_desc_t { + action_desc_t( + const char *name, + const char *label, + PyObject *handler, + const char *shortcut = NULL, + const char *tooltip = NULL, + int icon = -1) + { + action_desc_t *ad = new action_desc_t(); +#define DUPSTR(Prop) ad->Prop = Prop == NULL ? NULL : qstrdup(Prop) + DUPSTR(name); + DUPSTR(label); + DUPSTR(shortcut); + DUPSTR(tooltip); +#undef DUPSTR + ad->icon = icon; + ad->handler = new py_action_handler_t(handler); + ad->owner = &PLUGIN; + return ad; + } + + ~action_desc_t() + { + if ( $self->handler != NULL ) // Ownership not taken? + delete $self->handler; +#define FREESTR(Prop) qfree((char *) $self->Prop) + FREESTR(name); + FREESTR(label); + FREESTR(shortcut); + FREESTR(tooltip); +#undef FREESTR + delete $self; + } +} + +//------------------------------------------------------------------------- +uint32 choose_choose(PyObject *self, + int flags, + int x0,int y0, + int x1,int y1, + int width, + int deflt, + int icon); + +%extend place_t { + static idaplace_t *as_idaplace_t(place_t *p) { return (idaplace_t *) p; } + static enumplace_t *as_enumplace_t(place_t *p) { return (enumplace_t *) p; } + static structplace_t *as_structplace_t(place_t *p) { return (structplace_t *) p; } + static simpleline_place_t *as_simpleline_place_t(place_t *p) { return (simpleline_place_t *) p; } +} + +%extend twinpos_t { + + %pythoncode { + def place_as_idaplace_t(self): + return place_t.as_idaplace_t(self.at) + def place_as_enumplace_t(self): + return place_t.as_enumplace_t(self.at) + def place_as_structplace_t(self): + return place_t.as_structplace_t(self.at) + def place_as_simpleline_place_t(self): + return place_t.as_simpleline_place_t(self.at) + + def place(self, view): + ptype = get_viewer_place_type(view) + if ptype == TCCPT_IDAPLACE: + return self.place_as_idaplace_t() + elif ptype == TCCPT_ENUMPLACE: + return self.place_as_enumplace_t() + elif ptype == TCCPT_STRUCTPLACE: + return self.place_as_structplace_t() + elif ptype == TCCPT_SIMPLELINE_PLACE: + return self.place_as_simpleline_place_t() + else: + return self.at + } +} + +%pythoncode %{ + +# +DP_LEFT = 0x0001 +DP_TOP = 0x0002 +DP_RIGHT = 0x0004 +DP_BOTTOM = 0x0008 +DP_INSIDE = 0x0010 +# if not before, then it is after +# (use DP_INSIDE | DP_BEFORE to insert a tab before a given tab) +# this flag alone cannot be used to determine orientation +DP_BEFORE = 0x0020 +# used with combination of other flags +DP_TAB = 0x0040 +DP_FLOATING = 0x0080 + +# ---------------------------------------------------------------------- +def load_custom_icon(file_name=None, data=None, format=None): + """ + Loads a custom icon and returns an identifier that can be used with other APIs + + If file_name is passed then the other two arguments are ignored. + + @param file_name: The icon file name + @param data: The icon data + @param format: The icon data format + + @return: Icon id or 0 on failure. + Use free_custom_icon() to free it + """ + if file_name is not None: + return _idaapi.py_load_custom_icon_fn(file_name) + elif not (data is None and format is None): + return _idaapi.py_load_custom_icon_data(data, format) + else: + return 0 + +# ---------------------------------------------------------------------- +def asklong(defval, format): + res, val = _idaapi._asklong(defval, format) + + if res == 1: + return val + else: + return None + +# ---------------------------------------------------------------------- +def askaddr(defval, format): + res, ea = _idaapi._askaddr(defval, format) + + if res == 1: + return ea + else: + return None + +# ---------------------------------------------------------------------- +def askseg(defval, format): + res, sel = _idaapi._askseg(defval, format) + + if res == 1: + return sel + else: + return None + +# ---------------------------------------------------------------------- +class action_handler_t: + def __init__(self): + pass + + def activate(self, ctx): + return 0 + + def update(self, ctx): + pass + + + +class Choose2(object): + """ + Choose2 wrapper class. + + Some constants are defined in this class. Please refer to kernwin.hpp for more information. + """ + + CH_MODAL = 0x01 + """Modal chooser""" + + CH_MULTI = 0x02 + """Allow multi selection""" + + CH_MULTI_EDIT = 0x04 + CH_NOBTNS = 0x08 + CH_ATTRS = 0x10 + CH_NOIDB = 0x20 + """use the chooser even without an open database, same as x0=-2""" + CH_UTF8 = 0x40 + """string encoding is utf-8""" + + CH_BUILTIN_MASK = 0xF80000 + + # column flags (are specified in the widths array) + CHCOL_PLAIN = 0x00000000 + CHCOL_PATH = 0x00010000 + CHCOL_HEX = 0x00020000 + CHCOL_DEC = 0x00030000 + CHCOL_FORMAT = 0x00070000 + + + def __init__(self, title, cols, flags=0, popup_names=None, + icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, + embedded=False, width=None, height=None): + """ + Constructs a chooser window. + @param title: The chooser title + @param cols: a list of colums; each list item is a list of two items + example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ] + @param flags: One of CH_XXXX constants + @param deflt: Default starting item + @param popup_names: list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] + @param icon: Icon index (the icon should exist in ida resources or an index to a custom loaded icon) + @param x1, y1, x2, y2: The default location + @param embedded: Create as embedded chooser + @param width: Embedded chooser width + @param height: Embedded chooser height + """ + self.title = title + self.flags = flags + self.cols = cols + self.deflt = deflt + self.popup_names = popup_names + self.icon = icon + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + self.embedded = embedded + if embedded: + self.x1 = width + self.y1 = height + + + def Embedded(self): + """ + Creates an embedded chooser (as opposed to Show()) + @return: Returns 1 on success + """ + return _idaapi.choose2_create(self, True) + + + def GetEmbSelection(self): + """ + Returns the selection associated with an embedded chooser + + @return: + - None if chooser is not embedded + - A list with selection indices (0-based) + """ + return _idaapi.choose2_get_embedded_selection(self) + + + def Show(self, modal=False): + """ + Activates or creates a chooser window + @param modal: Display as modal dialog + @return: For modal choosers it will return the selected item index (0-based) + """ + if modal: + self.flags |= Choose2.CH_MODAL + + # Disable the timeout + old = _idaapi.set_script_timeout(0) + n = _idaapi.choose2_create(self, False) + _idaapi.set_script_timeout(old) + + # Delete the modal chooser instance + self.Close() + + return n + else: + self.flags &= ~Choose2.CH_MODAL + return _idaapi.choose2_create(self, False) + + + def Activate(self): + """Activates a visible chooser""" + return _idaapi.choose2_activate(self) + + + def Refresh(self): + """Causes the refresh callback to trigger""" + return _idaapi.choose2_refresh(self) + + + def Close(self): + """Closes the chooser""" + return _idaapi.choose2_close(self) + + + def AddCommand(self, + caption, + flags = _idaapi.CHOOSER_POPUP_MENU, + menu_index = -1, + icon = -1, + emb=None): + """ + Deprecated: Use + - register_action() + - attach_action_to_menu() + - attach_action_to_popup() + """ + # Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl + if self.embedded and ((emb is None) or (emb != 2002)): + raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()") + return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) + + # + # Implement these methods in the subclass: + # +# +# def OnClose(self): +# """ +# Called when the window is being closed. +# This callback is mandatory. +# @return: nothing +# """ +# pass +# +# def OnGetLine(self, n): +# """Called when the chooser window requires lines. +# This callback is mandatory. +# @param n: Line number (0-based) +# @return: The user should return a list with ncols elements. +# example: a list [col1, col2, col3, ...] describing the n-th line +# """ +# return ["col1 val", "col2 val"] +# +# def OnGetSize(self): +# """Returns the element count. +# This callback is mandatory. +# @return: Number of elements +# """ +# return len(self.the_list) +# +# def OnEditLine(self, n): +# """ +# Called when an item is being edited. +# @param n: Line number (0-based) +# @return: Nothing +# """ +# pass +# +# def OnInsertLine(self): +# """ +# Called when 'Insert' is selected either via the hotkey or popup menu. +# @return: Nothing +# """ +# pass +# +# def OnSelectLine(self, n): +# """ +# Called when a line is selected and then Ok or double click was pressed +# @param n: Line number (0-based) +# """ +# pass +# +# def OnSelectionChange(self, sel_list): +# """ +# Called when the selection changes +# @param sel_list: A list of selected item indices +# """ +# pass +# +# def OnDeleteLine(self, n): +# """ +# Called when a line is about to be deleted +# @param n: Line number (0-based) +# """ +# return self.n +# +# def OnRefresh(self, n): +# """ +# Triggered when the 'Refresh' is called from the popup menu item. +# +# @param n: The currently selected line (0-based) at the time of the refresh call +# @return: Return the number of elements +# """ +# return self.n +# +# def OnRefreshed(self): +# """ +# Triggered when a refresh happens (for example due to column sorting) +# @param n: Line number (0-based) +# @return: Return the number of elements +# """ +# return self.n +# +# def OnCommand(self, n, cmd_id): +# """Return int ; check add_chooser_command()""" +# return 0 +# +# def OnGetIcon(self, n): +# """ +# Return icon number for a given item (or -1 if no icon is avail) +# @param n: Line number (0-based) +# """ +# return -1 +# +# def OnGetLineAttr(self, n): +# """ +# Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t +# @param n: Line number (0-based) +# """ +# return [0x0, CHITEM_BOLD] +# + + +#ICON WARNING|QUESTION|INFO|NONE +#AUTOHIDE NONE|DATABASE|REGISTRY|SESSION +#HIDECANCEL +#BUTTON YES|NO|CANCEL "Value|NONE" +#STARTITEM {id:ItemName} +#HELP / ENDHELP +try: + import types + from ctypes import * + # On Windows, we use stdcall + + # Callback for buttons + # typedef void (idaapi *formcb_t)(TView *fields[], int code); + + _FORMCB_T = WINFUNCTYPE(None, c_void_p, c_int) + + # Callback for form change + # typedef int (idaapi *formchgcb_t)(int field_id, form_actions_t &fa); + _FORMCHGCB_T = WINFUNCTYPE(c_int, c_int, c_void_p) +except: + try: + _FORMCB_T = CFUNCTYPE(None, c_void_p, c_int) + _FORMCHGCB_T = CFUNCTYPE(c_int, c_int, c_void_p) + except: + _FORMCHGCB_T = _FORMCB_T = None + + +# ----------------------------------------------------------------------- +# textctrl_info_t clinked object +class textctrl_info_t(py_clinked_object_t): + """Class representing textctrl_info_t""" + + # Some constants + TXTF_AUTOINDENT = 0x0001 + """Auto-indent on new line""" + TXTF_ACCEPTTABS = 0x0002 + """Tab key inserts 'tabsize' spaces""" + TXTF_READONLY = 0x0004 + """Text cannot be edited (but can be selected and copied)""" + TXTF_SELECTED = 0x0008 + """Shows the field with its text selected""" + TXTF_MODIFIED = 0x0010 + """Gets/sets the modified status""" + TXTF_FIXEDFONT = 0x0020 + """The control uses IDA's fixed font""" + + def __init__(self, text="", flags=0, tabsize=0): + py_clinked_object_t.__init__(self) + if text: + self.text = text + if flags: + self.flags = flags + if tabsize: + self.tabsize = tabsize + + def _create_clink(self): + return _idaapi.textctrl_info_t_create() + + def _del_clink(self, lnk): + return _idaapi.textctrl_info_t_destroy(lnk) + + def _get_clink_ptr(self): + return _idaapi.textctrl_info_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.textctrl_info_t_assign(self, other) + + def __set_text(self, s): + """Sets the text value""" + return _idaapi.textctrl_info_t_set_text(self, s) + + def __get_text(self): + """Sets the text value""" + return _idaapi.textctrl_info_t_get_text(self) + + def __set_flags__(self, flags): + """Sets the flags value""" + return _idaapi.textctrl_info_t_set_flags(self, flags) + + def __get_flags__(self): + """Returns the flags value""" + return _idaapi.textctrl_info_t_get_flags(self) + + def __set_tabsize__(self, tabsize): + """Sets the tabsize value""" + return _idaapi.textctrl_info_t_set_tabsize(self, tabsize) + + def __get_tabsize__(self): + """Returns the tabsize value""" + return _idaapi.textctrl_info_t_get_tabsize(self) + + value = property(__get_text, __set_text) + """Alias for the text property""" + text = property(__get_text, __set_text) + """Text value""" + flags = property(__get_flags__, __set_flags__) + """Flags value""" + tabsize = property(__get_tabsize__, __set_tabsize__) + +# ----------------------------------------------------------------------- +class Form(object): + + FT_ASCII = 'A' + """Ascii string - char *""" + FT_SEG = 'S' + """Segment - sel_t *""" + FT_HEX = 'N' + """Hex number - uval_t *""" + FT_SHEX = 'n' + """Signed hex number - sval_t *""" + FT_COLOR = 'K' + """Color button - bgcolor_t *""" + FT_ADDR = '$' + """Address - ea_t *""" + FT_UINT64 = 'L' + """default base uint64 - uint64""" + FT_INT64 = 'l' + """default base int64 - int64""" + FT_RAWHEX = 'M' + """Hex number, no 0x prefix - uval_t *""" + FT_FILE = 'f' + """File browse - char * at least QMAXPATH""" + FT_DEC = 'D' + """Decimal number - sval_t *""" + FT_OCT = 'O' + """Octal number, C notation - sval_t *""" + FT_BIN = 'Y' + """Binary number, 0b prefix - sval_t *""" + FT_CHAR = 'H' + """Char value -- sval_t *""" + FT_IDENT = 'I' + """Identifier - char * at least MAXNAMELEN""" + FT_BUTTON = 'B' + """Button - def handler(code)""" + FT_DIR = 'F' + """Path to directory - char * at least QMAXPATH""" + FT_TYPE = 'T' + """Type declaration - char * at least MAXSTR""" + _FT_USHORT = '_US' + """Unsigned short""" + FT_FORMCHG = '%/' + """Form change callback - formchgcb_t""" + FT_ECHOOSER = 'E' + """Embedded chooser - idaapi.Choose2""" + FT_MULTI_LINE_TEXT = 't' + """Multi text control - textctrl_info_t""" + FT_DROPDOWN_LIST = 'b' + """Dropdown list control - Form.DropdownControl""" + + FT_CHKGRP = 'C' + FT_CHKGRP2= 'c' + FT_RADGRP = 'R' + FT_RADGRP2= 'r' + + @staticmethod + def fieldtype_to_ctype(tp, i64 = False): + """ + Factory method returning a ctype class corresponding to the field type string + """ + if tp in (Form.FT_SEG, Form.FT_HEX, Form.FT_RAWHEX, Form.FT_ADDR): + return c_ulonglong if i64 else c_ulong + elif tp in (Form.FT_SHEX, Form.FT_DEC, Form.FT_OCT, Form.FT_BIN, Form.FT_CHAR): + return c_longlong if i64 else c_long + elif tp == Form.FT_UINT64: + return c_ulonglong + elif tp == Form.FT_INT64: + return c_longlong + elif tp == Form.FT_COLOR: + return c_ulong + elif tp == Form._FT_USHORT: + return c_ushort + elif tp in (Form.FT_FORMCHG, Form.FT_ECHOOSER): + return c_void_p + else: + return None + + + # + # Generic argument helper classes + # + class NumericArgument(object): + """ + Argument representing various integer arguments (ushort, uint32, uint64, etc...) + @param tp: One of Form.FT_XXX + """ + DefI64 = False + def __init__(self, tp, value): + cls = Form.fieldtype_to_ctype(tp, self.DefI64) + if cls is None: + raise TypeError("Invalid numeric field type: %s" % tp) + # Get a pointer type to the ctype type + self.arg = pointer(cls(value)) + + def __set_value(self, v): + self.arg.contents.value = v + value = property(lambda self: self.arg.contents.value, __set_value) + + + class StringArgument(object): + """ + Argument representing a character buffer + """ + def __init__(self, size=None, value=None): + if size is None: + raise SyntaxError("The string size must be passed") + + if value is None: + self.arg = create_string_buffer(size) + else: + self.arg = create_string_buffer(value, size) + self.size = size + + def __set_value(self, v): + self.arg.value = v + value = property(lambda self: self.arg.value, __set_value) + + + # + # Base control class + # + class Control(object): + def __init__(self): + self.id = 0 + """Automatically assigned control ID""" + + self.arg = None + """Control argument value. This could be one element or a list/tuple (for multiple args per control)""" + + self.form = None + """Reference to the parent form. It is filled by Form.Add()""" + + + def get_tag(self): + """ + Control tag character. One of Form.FT_XXXX. + The form class will expand the {} notation and replace them with the tags + """ + pass + + def get_arg(self): + """ + Control returns the parameter to be pushed on the stack + (Of AskUsingForm()) + """ + return self.arg + + def free(self): + """ + Free the control + """ + # Release the parent form reference + self.form = None + + + # + # Label controls + # + class LabelControl(Control): + """ + Base class for static label control + """ + def __init__(self, tp): + Form.Control.__init__(self) + self.tp = tp + + def get_tag(self): + return '%%%d%s' % (self.id, self.tp) + + + class StringLabel(LabelControl): + """ + String label control + """ + def __init__(self, value, tp=None, sz=1024): + """ + Type field can be one of: + A - ascii string + T - type declaration + I - ident + F - folder + f - file + X - command + """ + if tp is None: + tp = Form.FT_ASCII + Form.LabelControl.__init__(self, tp) + self.size = sz + self.arg = create_string_buffer(value, sz) + + + class NumericLabel(LabelControl, NumericArgument): + """ + Numeric label control + """ + def __init__(self, value, tp=None): + if tp is None: + tp = Form.FT_HEX + Form.LabelControl.__init__(self, tp) + Form.NumericArgument.__init__(self, tp, value) + + + # + # Group controls + # + class GroupItemControl(Control): + """ + Base class for group control items + """ + def __init__(self, tag, parent): + Form.Control.__init__(self) + self.tag = tag + self.parent = parent + # Item position (filled when form is compiled) + self.pos = 0 + + def assign_pos(self): + self.pos = self.parent.next_child_pos() + + def get_tag(self): + return "%s%d" % (self.tag, self.id) + + + class ChkGroupItemControl(GroupItemControl): + """ + Checkbox group item control + """ + def __init__(self, tag, parent): + Form.GroupItemControl.__init__(self, tag, parent) + + def __get_value(self): + return (self.parent.value & (1 << self.pos)) != 0 + + def __set_value(self, v): + pv = self.parent.value + if v: + pv = pv | (1 << self.pos) + else: + pv = pv & ~(1 << self.pos) + + self.parent.value = pv + + checked = property(__get_value, __set_value) + """Get/Sets checkbox item check status""" + + + class RadGroupItemControl(GroupItemControl): + """ + Radiobox group item control + """ + def __init__(self, tag, parent): + Form.GroupItemControl.__init__(self, tag, parent) + + def __get_value(self): + return self.parent.value == self.pos + + def __set_value(self, v): + self.parent.value = self.pos + + selected = property(__get_value, __set_value) + """Get/Sets radiobox item selection status""" + + + class GroupControl(Control, NumericArgument): + """ + Base class for group controls + """ + def __init__(self, children_names, tag, value=0): + Form.Control.__init__(self) + self.children_names = children_names + self.tag = tag + self._reset() + Form.NumericArgument.__init__(self, Form._FT_USHORT, value) + + def _reset(self): + self.childpos = 0 + + def next_child_pos(self): + v = self.childpos + self.childpos += 1 + return v + + def get_tag(self): + return "%d" % self.id + + + class ChkGroupControl(GroupControl): + """ + Checkbox group control class. + It holds a set of checkbox controls + """ + ItemClass = None + """ + Group control item factory class instance + We need this because later we won't be treating ChkGroupControl or RadGroupControl + individually, instead we will be working with GroupControl in general. + """ + def __init__(self, children_names, value=0, secondary=False): + # Assign group item factory class + if Form.ChkGroupControl.ItemClass is None: + Form.ChkGroupControl.ItemClass = Form.ChkGroupItemControl + + Form.GroupControl.__init__( + self, + children_names, + Form.FT_CHKGRP2 if secondary else Form.FT_CHKGRP, + value) + + + class RadGroupControl(GroupControl): + """ + Radiobox group control class. + It holds a set of radiobox controls + """ + ItemClass = None + def __init__(self, children_names, value=0, secondary=False): + """ + Creates a radiogroup control. + @param children_names: A tuple containing group item names + @param value: Initial selected radio item + @param secondory: Allows rendering one the same line as the previous group control. + Use this if you have another group control on the same line. + """ + # Assign group item factory class + if Form.RadGroupControl.ItemClass is None: + Form.RadGroupControl.ItemClass = Form.RadGroupItemControl + + Form.GroupControl.__init__( + self, + children_names, + Form.FT_RADGRP2 if secondary else Form.FT_RADGRP, + value) + + + # + # Input controls + # + class InputControl(Control): + """ + Generic form input control. + It could be numeric control, string control, directory/file browsing, etc... + """ + def __init__(self, tp, width, swidth, hlp = None): + """ + @param width: Display width + @param swidth: String width + """ + Form.Control.__init__(self) + self.tp = tp + self.width = width + self.switdh = swidth + self.hlp = hlp + + def get_tag(self): + return "%s%d:%s:%s:%s" % ( + self.tp, self.id, + self.width, + self.switdh, + ":" if self.hlp is None else self.hlp) + + + class NumericInput(InputControl, NumericArgument): + """ + A composite class serving as a base numeric input control class + """ + def __init__(self, tp=None, value=0, width=50, swidth=10, hlp=None): + if tp is None: + tp = Form.FT_HEX + Form.InputControl.__init__(self, tp, width, swidth, hlp) + Form.NumericArgument.__init__(self, self.tp, value) + + + class ColorInput(NumericInput): + """ + Color button input control + """ + def __init__(self, value = 0): + """ + @param value: Initial color value in RGB + """ + Form.NumericInput.__init__(self, tp=Form.FT_COLOR, value=value) + + + class StringInput(InputControl, StringArgument): + """ + Base string input control class. + This class also constructs a StringArgument + """ + def __init__(self, + tp=None, + width=1024, + swidth=40, + hlp=None, + value=None, + size=None): + """ + @param width: String size. But in some cases it has special meaning. For example in FileInput control. + If you want to define the string buffer size then pass the 'size' argument + @param swidth: Control width + @param value: Initial value + @param size: String size + """ + if tp is None: + tp = Form.FT_ASCII + if not size: + size = width + Form.InputControl.__init__(self, tp, width, swidth, hlp) + Form.StringArgument.__init__(self, size=size, value=value) + + + class FileInput(StringInput): + """ + File Open/Save input control + """ + def __init__(self, + width=512, + swidth=80, + save=False, open=False, + hlp=None, value=None): + + if save == open: + raise ValueError("Invalid mode. Choose either open or save") + if width < 512: + raise ValueError("Invalid width. Must be greater than 512.") + + # The width field is overloaded in this control and is used + # to denote the type of the FileInput dialog (save or load) + # On the other hand it is passed as is to the StringArgument part + Form.StringInput.__init__( + self, + tp=Form.FT_FILE, + width="1" if save else "0", + swidth=swidth, + hlp=hlp, + size=width, + value=value) + + + class DirInput(StringInput): + """ + Directory browsing control + """ + def __init__(self, + width=512, + swidth=80, + hlp=None, + value=None): + + if width < 512: + raise ValueError("Invalid width. Must be greater than 512.") + + Form.StringInput.__init__( + self, + tp=Form.FT_DIR, + width=width, + swidth=swidth, + hlp=hlp, + size=width, + value=value) + + + class ButtonInput(InputControl): + """ + Button control. + A handler along with a 'code' (numeric value) can be associated with the button. + This way one handler can handle many buttons based on the button code (or in other terms id or tag) + """ + def __init__(self, handler, code="", swidth="", hlp=None): + """ + @param handler: Button handler. A callback taking one argument which is the code. + @param code: A code associated with the button and that is later passed to the handler. + """ + Form.InputControl.__init__( + self, + Form.FT_BUTTON, + code, + swidth, + hlp) + self.arg = _FORMCB_T(lambda view, code, h=handler: h(code)) + + + class FormChangeCb(Control): + """ + Form change handler. + This can be thought of like a dialog procedure. + Everytime a form action occurs, this handler will be called along with the control id. + The programmer can then call various form actions accordingly: + - EnableField + - ShowField + - MoveField + - GetFieldValue + - etc... + + Special control IDs: -1 (The form is initialized) and -2 (Ok has been clicked) + + """ + def __init__(self, handler): + """ + Constructs the handler. + @param handler: The handler (preferrably a member function of a class derived from the Form class). + """ + Form.Control.__init__(self) + + # Save the handler + self.handler = handler + + # Create a callback stub + # We use this mechanism to create an intermediate step + # where we can create an 'fa' adapter for use by Python + self.arg = _FORMCHGCB_T(self.helper_cb) + + def helper_cb(self, fid, p_fa): + # Remember the pointer to the forms_action in the parent form + self.form.p_fa = p_fa + + # Call user's handler + r = self.handler(fid) + return 0 if r is None else r + + def get_tag(self): + return Form.FT_FORMCHG + + def free(self): + Form.Control.free(self) + # Remove reference to the handler + # (Normally the handler is a member function in the parent form) + self.handler = None + + + class EmbeddedChooserControl(InputControl): + """ + Embedded chooser control. + This control links to a Chooser2 control created with the 'embedded=True' + """ + def __init__(self, + chooser=None, + swidth=40, + hlp=None): + """ + Embedded chooser control + + @param chooser: A chooser2 instance (must be constructed with 'embedded=True') + """ + + # !! Make sure a chooser instance is passed !! + if chooser is None or not isinstance(chooser, Choose2): + raise ValueError("Invalid chooser passed.") + + # Create an embedded chooser structure from the Choose2 instance + if chooser.Embedded() != 1: + raise ValueError("Failed to create embedded chooser instance.") + + # Construct input control + Form.InputControl.__init__(self, Form.FT_ECHOOSER, "", swidth) + + # Get a pointer to the chooser_info_t and the selection vector + # (These two parameters are the needed arguments for the AskUsingForm()) + emb, sel = _idaapi.choose2_get_embedded(chooser) + + # Get a pointer to a c_void_p constructed from an address + p_embedded = pointer(c_void_p.from_address(emb)) + p_sel = pointer(c_void_p.from_address(sel)) + + # - Create the embedded chooser info on control creation + # - Do not free the embeded chooser because after we get the args + # via Compile() the user can still call Execute() which relies + # on the already computed args + self.arg = (p_embedded, p_sel) + + # Save chooser instance + self.chooser = chooser + + # Add a bogus 'size' attribute + self.size = 0 + + + value = property(lambda self: self.chooser) + """Returns the embedded chooser instance""" + + + def AddCommand(self, + caption, + flags = _idaapi.CHOOSER_POPUP_MENU, + menu_index = -1, + icon = -1): + """ + Adds a new embedded chooser command + Save the returned value and later use it in the OnCommand handler + + @return: Returns a negative value on failure or the command index + """ + if not self.form.title: + raise ValueError("Form title is not set!") + + # Encode all information for the AddCommand() in the 'caption' parameter + caption = "%s:%d:%s" % (self.form.title, self.id, caption) + return self.chooser.AddCommand(caption, flags=flags, menu_index=menu_index, icon=icon, emb=2002) + + + def free(self): + """ + Frees the embedded chooser data + """ + self.chooser.Close() + self.chooser = None + Form.Control.free(self) + + + class DropdownListControl(InputControl, _qstrvec_t): + """ + Dropdown control + This control allows manipulating a dropdown control + """ + def __init__(self, items=[], readonly=True, selval=0, width=50, swidth=50, hlp = None): + """ + @param items: A string list of items used to prepopulate the control + @param readonly: Specifies whether the dropdown list is editable or not + @param selval: The preselected item index (when readonly) or text value (when editable) + @param width: the control width (n/a if the dropdown list is readonly) + @param swidth: string width + """ + + # Ignore the width if readonly was set + if readonly: + width = 0 + + # Init the input control base class + Form.InputControl.__init__( + self, + Form.FT_DROPDOWN_LIST, + width, + swidth, + hlp) + + # Init the associated qstrvec + _qstrvec_t.__init__(self, items) + + # Remember if readonly or not + self.readonly = readonly + + if readonly: + # Create a C integer and remember it + self.__selval = c_int(selval) + val_addr = addressof(self.__selval) + else: + # Create an strvec with one qstring + self.__selval = _qstrvec_t([selval]) + # Get address of the first element + val_addr = self.__selval.addressof(0) + + # Two arguments: + # - argument #1: a pointer to the qstrvec containing the items + # - argument #2: an integer to hold the selection + # or + # #2: a qstring to hold the dropdown text control value + self.arg = ( + pointer(c_void_p.from_address(self.clink_ptr)), + pointer(c_void_p.from_address(val_addr)) + ) + + + def __set_selval(self, val): + if self.readonly: + self.__selval.value = val + else: + self.__selval[0] = val + + def __get_selval(self): + # Return the selection index + # or the entered text value + return self.__selval.value if self.readonly else self.__selval[0] + + value = property(__get_selval, __set_selval) + selval = property(__get_selval, __set_selval) + """ + Read/write the selection value. + The value is used as an item index in readonly mode or text value in editable mode + This value can be used only after the form has been closed. + """ + + def free(self): + self._free() + + + def set_items(self, items): + """Sets the dropdown list items""" + self.from_list(items) + + + class MultiLineTextControl(InputControl, textctrl_info_t): + """ + Multi line text control. + This class inherits from textctrl_info_t. Thus the attributes are also inherited + This control allows manipulating a multilinetext control + """ + def __init__(self, text="", flags=0, tabsize=0, width=50, swidth=50, hlp = None): + """ + @param text: Initial text value + @param flags: One of textctrl_info_t.TXTF_.... values + @param tabsize: Tab size + @param width: Display width + @param swidth: String width + """ + # Init the input control base class + Form.InputControl.__init__(self, Form.FT_MULTI_LINE_TEXT, width, swidth, hlp) + + # Init the associated textctrl_info base class + textctrl_info_t.__init__(self, text=text, flags=flags, tabsize=tabsize) + + # Get the argument as a pointer from the embedded ti + self.arg = pointer(c_void_p.from_address(self.clink_ptr)) + + + def free(self): + self._free() + + + # + # Form class + # + def __init__(self, form, controls): + """ + Contruct a Form class. + This class wraps around AskUsingForm() or OpenForm() and provides an easier / alternative syntax for describing forms. + The form control names are wrapped inside the opening and closing curly braces and the control themselves are + defined and instantiated via various form controls (subclasses of Form). + + @param form: The form string + @param controls: A dictionary containing the control name as a _key_ and control object as _value_ + """ + self._reset() + self.form = form + """Form string""" + self.controls = controls + """Dictionary of controls""" + self.__args = None + + self.title = None + """The Form title. It will be filled when the form is compiled""" + + self.modal = True + """By default, forms are modal""" + + self.openform_flags = 0 + """ + If non-modal, these flags will be passed to OpenForm. + This is an OR'ed combination of the PluginForm.FORM_* values. + """ + + + def Free(self): + """ + Frees all resources associated with a compiled form. + Make sure you call this function when you finish using the form. + """ + + # Free all the controls + for ctrl in self.__controls.values(): + ctrl.free() + + # Reset the controls + # (Note that we are not removing the form control attributes, no need) + self._reset() + + # Unregister, so we don't try and free it again at closing-time. + _idaapi.py_unregister_compiled_form(self) + + + def _reset(self): + """ + Resets the Form class state variables + """ + self.__controls = {} + self.__ctrl_id = 1 + + + def __getitem__(self, name): + """Returns a control object by name""" + return self.__controls[name] + + + def Add(self, name, ctrl, mkattr = True): + """ + Low level function. Prefer AddControls() to this function. + This function adds one control to the form. + + @param name: Control name + @param ctrl: Control object + @param mkattr: Create control name / control object as a form attribute + """ + # Assign a unique ID + ctrl.id = self.__ctrl_id + self.__ctrl_id += 1 + + # Create attribute with control name + if mkattr: + setattr(self, name, ctrl) + + # Remember the control + self.__controls[name] = ctrl + + # Link the form to the control via its form attribute + ctrl.form = self + + # Is it a group? Add each child + if isinstance(ctrl, Form.GroupControl): + self._AddGroup(ctrl, mkattr) + + + def FindControlById(self, id): + """ + Finds a control instance given its id + """ + for ctrl in self.__controls.values(): + if ctrl.id == id: + return ctrl + return None + + + @staticmethod + def _ParseFormTitle(form): + """ + Parses the form's title from the form text + """ + help_state = 0 + for i, line in enumerate(form.split("\n")): + if line.startswith("STARTITEM ") or line.startswith("BUTTON "): + continue + # Skip "HELP" and remember state + elif help_state == 0 and line == "HELP": + help_state = 1 # Mark inside HELP + continue + elif help_state == 1 and line == "ENDHELP": + help_state = 2 # Mark end of HELP + continue + return line.strip() + + return None + + + def _AddGroup(self, Group, mkattr=True): + """ + Internal function. + This function expands the group item names and creates individual group item controls + + @param Group: The group class (checkbox or radio group class) + """ + + # Create group item controls for each child + for child_name in Group.children_names: + self.Add( + child_name, + # Use the class factory + Group.ItemClass(Group.tag, Group), + mkattr) + + + def AddControls(self, controls, mkattr=True): + """ + Adds controls from a dictionary. + The dictionary key is the control name and the value is a Form.Control object + @param controls: The control dictionary + """ + for name, ctrl in controls.items(): + # Add the control + self.Add(name, ctrl, mkattr) + + + def CompileEx(self, form): + """ + Low level function. + Compiles (parses the form syntax and adds the control) the form string and + returns the argument list to be passed the argument list to AskUsingForm(). + + The form controls are wrapped inside curly braces: {ControlName}. + + A special operator can be used to return the ID of a given control by its name: {id:ControlName}. + This is useful when you use the STARTITEM form keyword to set the initially focused control. + + @param form: Compiles the form and returns the arguments needed to be passed to AskUsingForm() + """ + # First argument is the form string + args = [None] + + # Second argument, if form is not modal, is the set of flags + if not self.modal: + args.append(self.openform_flags | 0x80) # Add FORM_QWIDGET + + ctrlcnt = 1 + + # Reset all group control internal flags + for ctrl in self.__controls.values(): + if isinstance(ctrl, Form.GroupControl): + ctrl._reset() + + p = 0 + while True: + i1 = form.find("{", p) + # No more items? + if i1 == -1: + break + + # Check if escaped + if (i1 != 0) and form[i1-1] == "\\": + # Remove escape sequence and restart search + form = form[:i1-1] + form[i1:] + + # Skip current marker + p = i1 + + # Continue search + continue + + i2 = form.find("}", i1) + if i2 == -1: + raise SyntaxError("No matching closing brace '}'") + + # Parse control name + ctrlname = form[i1+1:i2] + if not ctrlname: + raise ValueError("Control %d has an invalid name!" % ctrlcnt) + + # Is it the IDOF operator? + if ctrlname.startswith("id:"): + idfunc = True + # Take actual ctrlname + ctrlname = ctrlname[3:] + else: + idfunc = False + + # Find the control + ctrl = self.__controls.get(ctrlname, None) + if ctrl is None: + raise ValueError("No matching control '%s'" % ctrlname) + + # Replace control name by tag + if idfunc: + tag = str(ctrl.id) + else: + tag = ctrl.get_tag() + taglen = len(tag) + form = form[:i1] + tag + form[i2+1:] + + # Set new position + p = i1 + taglen + + # Was it an IDOF() ? No need to push parameters + # Just ID substitution is fine + if idfunc: + continue + + + # For GroupItem controls, there are no individual arguments + # The argument is assigned for the group itself + if isinstance(ctrl, Form.GroupItemControl): + # GroupItem controls will have their position dynamically set + ctrl.assign_pos() + else: + # Push argument(s) + # (Some controls need more than one argument) + arg = ctrl.get_arg() + if isinstance(arg, (types.ListType, types.TupleType)): + # Push all args + args.extend(arg) + else: + # Push one arg + args.append(arg) + + ctrlcnt += 1 + + # If no FormChangeCb instance was passed, and thus there's no '%/' + # in the resulting form string, let's provide a minimal one, so that + # we will retrieve 'p_fa', and thus actions that rely on it will work. + if form.find(Form.FT_FORMCHG) < 0: + form = form + Form.FT_FORMCHG + fccb = Form.FormChangeCb(lambda *args: 1) + self.Add("___dummyfchgcb", fccb) + # Regardless of the actual position of '%/' in the form + # string, a formchange callback _must_ be right after + # the form string. + if self.modal: + inspos = 1 + else: + inspos = 2 + args.insert(inspos, fccb.get_arg()) + + # Patch in the final form string + args[0] = form + + self.title = self._ParseFormTitle(form) + return args + + + def Compile(self): + """ + Compiles a form and returns the form object (self) and the argument list. + The form object will contain object names corresponding to the form elements + + @return: It will raise an exception on failure. Otherwise the return value is ignored + """ + + # Reset controls + self._reset() + + # Insert controls + self.AddControls(self.controls) + + # Compile form and get args + self.__args = self.CompileEx(self.form) + + # Register this form, to make sure it will be freed at closing-time. + _idaapi.py_register_compiled_form(self) + + return (self, self.__args) + + + def Compiled(self): + """ + Checks if the form has already been compiled + + @return: Boolean + """ + return self.__args is not None + + + def _ChkCompiled(self): + if not self.Compiled(): + raise SyntaxError("Form is not compiled") + + + def Execute(self): + """ + Displays a modal dialog containing the compiled form. + @return: 1 - ok ; 0 - cancel + """ + self._ChkCompiled() + if not self.modal: + raise SyntaxError("Form is not modal. Open() should be instead") + + return AskUsingForm(*self.__args) + + + def Open(self): + """ + Opens a widget containing the compiled form. + """ + self._ChkCompiled() + if self.modal: + raise SyntaxError("Form is modal. Execute() should be instead") + + OpenForm(*self.__args) + + + def EnableField(self, ctrl, enable): + """ + Enable or disable an input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) + + + def ShowField(self, ctrl, show): + """ + Show or hide an input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_show_field(self.p_fa, ctrl.id, show) + + + def MoveField(self, ctrl, x, y, w, h): + """ + Move/resize an input field + + @return: False - no such fiel + """ + return _idaapi.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) + + + def GetFocusedField(self): + """ + Get currently focused input field. + @return: None if no field is selected otherwise the control ID + """ + id = _idaapi.formchgcbfa_get_focused_field(self.p_fa) + return self.FindControlById(id) + + + def SetFocusedField(self, ctrl): + """ + Set currently focused input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) + + + def RefreshField(self, ctrl): + """ + Refresh a field + @return: False - no such control + """ + return _idaapi.formchgcbfa_refresh_field(self.p_fa, ctrl.id) + + + def Close(self, close_normally): + """ + Close the form + @param close_normally: + 1: form is closed normally as if the user pressed Enter + 0: form is closed abnormally as if the user pressed Esc + @return: None + """ + return _idaapi.formchgcbfa_close(self.p_fa, close_normally) + + + def GetControlValue(self, ctrl): + """ + Returns the control's value depending on its type + @param ctrl: Form control instance + @return: + - color button, radio controls: integer + - file/dir input, string input and string label: string + - embedded chooser control (0-based indices of selected items): integer list + - for multilinetext control: textctrl_info_t + - dropdown list controls: string (when editable) or index (when readonly) + - None: on failure + """ + tid, sz = self.ControlToFieldTypeIdAndSize(ctrl) + r = _idaapi.formchgcbfa_get_field_value( + self.p_fa, + ctrl.id, + tid, + sz) + # Multilinetext? Unpack the tuple into a new textctrl_info_t instance + if r is not None and tid == 7: + return textctrl_info_t(text=r[0], flags=r[1], tabsize=r[2]) + else: + return r + + + def SetControlValue(self, ctrl, value): + """ + Set the control's value depending on its type + @param ctrl: Form control instance + @param value: + - embedded chooser: a 0-base indices list to select embedded chooser items + - multilinetext: a textctrl_info_t + - dropdown list: an integer designating the selection index if readonly + a string designating the edit control value if not readonly + @return: Boolean true on success + """ + tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) + return _idaapi.formchgcbfa_set_field_value( + self.p_fa, + ctrl.id, + tid, + value) + + + @staticmethod + def ControlToFieldTypeIdAndSize(ctrl): + """ + Converts a control object to a tuple containing the field id + and the associated buffer size + """ + # Input control depend on the associated buffer size (supplied by the user) + + # Make sure you check instances types taking into account inheritance + if isinstance(ctrl, Form.DropdownListControl): + return (8, 1 if ctrl.readonly else 0) + elif isinstance(ctrl, Form.MultiLineTextControl): + return (7, 0) + elif isinstance(ctrl, Form.EmbeddedChooserControl): + return (5, 0) + # Group items or controls + elif isinstance(ctrl, (Form.GroupItemControl, Form.GroupControl)): + return (2, 0) + elif isinstance(ctrl, Form.StringLabel): + return (3, min(_idaapi.MAXSTR, ctrl.size)) + elif isinstance(ctrl, Form.ColorInput): + return (4, 0) + elif isinstance(ctrl, Form.NumericInput): + # Pass the numeric control type + return (6, ord(ctrl.tp[0])) + elif isinstance(ctrl, Form.InputControl): + return (1, ctrl.size) + else: + raise NotImplementedError, "Not yet implemented" + +# -------------------------------------------------------------------------- +# Instantiate AskUsingForm function pointer +try: + import ctypes + # Setup the numeric argument size + Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL + AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm()) + OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_OpenForm()) +except: + def AskUsingForm__(*args): + warning("AskUsingForm() needs ctypes library in order to work") + return 0 + def OpenForm__(*args): + warning("OpenForm() needs ctypes library in order to work") + + +def AskUsingForm(*args): + """ + Calls AskUsingForm() + @param: Compiled Arguments obtain through the Form.Compile() function + @return: 1 = ok, 0 = cancel + """ + old = set_script_timeout(0) + r = AskUsingForm__(*args) + set_script_timeout(old) + return r + +def OpenForm(*args): + """ + Calls OpenForm() + @param: Compiled Arguments obtain through the Form.Compile() function + """ + old = set_script_timeout(0) + r = OpenForm__(*args) + set_script_timeout(old) + + +# + +# +class PluginForm(object): + """ + PluginForm class. + + This form can be used to host additional controls. Please check the PyQt example. + """ + + FORM_MDI = 0x01 + """start by default as MDI (obsolete)""" + FORM_TAB = 0x02 + """attached by default to a tab""" + FORM_RESTORE = 0x04 + """restore state from desktop config""" + FORM_ONTOP = 0x08 + """form should be "ontop""" + FORM_MENU = 0x10 + """form must be listed in the windows menu (automatically set for all plugins)""" + FORM_CENTERED = 0x20 + """form will be centered on the screen""" + FORM_PERSIST = 0x40 + """form will persist until explicitly closed with Close()""" + + + def __init__(self): + """ + """ + self.__clink__ = _idaapi.plgform_new() + + + + def Show(self, caption, options = 0): + """ + Creates the form if not was not created or brings to front if it was already created + + @param caption: The form caption + @param options: One of PluginForm.FORM_ constants + """ + options |= PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE + return _idaapi.plgform_show(self.__clink__, self, caption, options) + + + @staticmethod + def FormToPyQtWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PyQt + + @param ctx: Context. Reference to a module that already imported SIP and QtGui modules + """ + return ctx.sip.wrapinstance(ctx.sip.voidptr(form).__int__(), ctx.QtGui.QWidget) + + + @staticmethod + def FormToPySideWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PySide + + @param ctx: Context. Reference to a module that already imported QtGui module + """ + if form is None: + return None + if type(form).__name__ == "SwigPyObject": + # Since 'form' is a SwigPyObject, we first need to convert it to a PyCObject. + # However, there's no easy way of doing it, so we'll use a rather brutal approach: + # converting the SwigPyObject to a 'long' (will go through 'SwigPyObject_long', + # that will return the pointer's value as a long), and then convert that value + # back to a pointer into a PyCObject. + ptr_l = long(form) + from ctypes import pythonapi, c_void_p, py_object + pythonapi.PyCObject_FromVoidPtr.restype = py_object + pythonapi.PyCObject_AsVoidPtr.argtypes = [c_void_p, c_void_p] + form = pythonapi.PyCObject_FromVoidPtr(ptr_l, 0) + return ctx.QtGui.QWidget.FromCObject(form) + + + def OnCreate(self, form): + """ + This event is called when the plugin form is created. + The programmer should populate the form when this event is triggered. + + @return: None + """ + pass + + + def OnClose(self, form): + """ + Called when the plugin form is closed + + @return: None + """ + pass + + + def Close(self, options): + """ + Closes the form. + + @param options: Close options (FORM_SAVE, FORM_NO_CONTEXT, ...) + + @return: None + """ + return _idaapi.plgform_close(self.__clink__, options) + + FORM_SAVE = 0x1 + """Save state in desktop config""" + + FORM_NO_CONTEXT = 0x2 + """Don't change the current context (useful for toolbars)""" + + FORM_DONT_SAVE_SIZE = 0x4 + """Don't save size of the window""" + + FORM_CLOSE_LATER = 0x8 + """This flag should be used when Close() is called from an event handler""" +# + +class Choose: + """ + Choose - class for choose() with callbacks + """ + def __init__(self, list, title, flags=0, deflt=1, icon=37): + self.list = list + self.title = title + + self.flags = flags + self.x0 = -1 + self.x1 = -1 + self.y0 = -1 + self.y1 = -1 + + self.width = -1 + self.deflt = deflt + self.icon = icon + + # HACK: Add a circular reference for non-modal choosers. This prevents the GC + # from collecting the class object the callbacks need. Unfortunately this means + # that the class will never be collected, unless refhack is set to None explicitly. + if (flags & Choose2.CH_MODAL) == 0: + self.refhack = self + + def sizer(self): + """ + Callback: sizer - returns the length of the list + """ + return len(self.list) + + def getl(self, n): + """ + Callback: getl - get one item from the list + """ + if n == 0: + return self.title + if n <= self.sizer(): + return str(self.list[n-1]) + else: + return "" + + + def ins(self): + pass + + + def update(self, n): + pass + + + def edit(self, n): + pass + + + def enter(self, n): + print "enter(%d) called" % n + + + def destroy(self): + pass + + + def get_icon(self, n): + pass + + + def choose(self): + """ + choose - Display the choose dialogue + """ + old = set_script_timeout(0) + n = _idaapi.choose_choose( + self, + self.flags, + self.x0, + self.y0, + self.x1, + self.y1, + self.width, + self.deflt, + self.icon) + set_script_timeout(old) + return n +%} + +%pythoncode %{ +# +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: + # +# +# 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 prefix_start (string) +# @param n: completion number (int) +# @param line: the current line (string) +# @param prefix_start: the index where PREFIX starts in LINE (int) +# +# @return: None if no completion could be generated otherwise a String with the completion suggestion +# """ +# return None +# + +# +# +class simplecustviewer_t(object): + """The base class for implementing simple custom viewers""" + def __init__(self): + self.__this = None + + def __del__(self): + """Destructor. It also frees the associated C++ object""" + try: + _idaapi.pyscv_delete(self.__this) + except: + pass + + @staticmethod + def __make_sl_arg(line, fgcolor=None, bgcolor=None): + return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) + + def Create(self, title): + """ + Creates the custom view. This should be the first method called after instantiation + + @param title: The title of the view + @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. + In this case better close existing windows + """ + self.title = title + self.__this = _idaapi.pyscv_init(self, title) + return True if self.__this else False + + def Close(self): + """ + Destroys the view. + One has to call Create() afterwards. + Show() can be called and it will call Create() internally. + @return: Boolean + """ + return _idaapi.pyscv_close(self.__this) + + def Show(self): + """ + Shows an already created view. It the view was close, then it will call Create() for you + @return: Boolean + """ + return _idaapi.pyscv_show(self.__this) + + def Refresh(self): + return _idaapi.pyscv_refresh(self.__this) + + def RefreshCurrent(self): + """Refreshes the current line only""" + return _idaapi.pyscv_refresh_current(self.__this) + + def Count(self): + """Returns the number of lines in the view""" + return _idaapi.pyscv_count(self.__this) + + def GetSelection(self): + """ + Returns the selected area or None + @return: + - tuple(x1, y1, x2, y2) + - None if no selection + """ + return _idaapi.pyscv_get_selection(self.__this) + + def ClearLines(self): + """Clears all the lines""" + _idaapi.pyscv_clear_lines(self.__this) + + def AddLine(self, line, fgcolor=None, bgcolor=None): + """ + Adds a colored line to the view + @return: Boolean + """ + return _idaapi.pyscv_add_line(self.__this, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): + """ + Inserts a line in the given position + @return: Boolean + """ + return _idaapi.pyscv_insert_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): + """ + Edits an existing line. + @return: Boolean + """ + return _idaapi.pyscv_edit_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def PatchLine(self, lineno, offs, value): + """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" + return _idaapi.pyscv_patch_line(self.__this, lineno, offs, value) + + def DelLine(self, lineno): + """ + Deletes an existing line + @return: Boolean + """ + return _idaapi.pyscv_del_line(self.__this, lineno) + + def GetLine(self, lineno): + """ + Returns a line + @param lineno: The line number + @return: + Returns a tuple (colored_line, fgcolor, bgcolor) or None + """ + return _idaapi.pyscv_get_line(self.__this, lineno) + + def GetCurrentWord(self, mouse = 0): + """ + Returns the current word + @param mouse: Use mouse position or cursor position + @return: None if failed or a String containing the current word at mouse or cursor + """ + return _idaapi.pyscv_get_current_word(self.__this, mouse) + + def GetCurrentLine(self, mouse = 0, notags = 0): + """ + Returns the current line. + @param mouse: Current line at mouse pos + @param notags: If True then tag_remove() will be called before returning the line + @return: Returns the current line (colored or uncolored) or None on failure + """ + return _idaapi.pyscv_get_current_line(self.__this, mouse, notags) + + def GetPos(self, mouse = 0): + """ + Returns the current cursor or mouse position. + @param mouse: return mouse position + @return: Returns a tuple (lineno, x, y) + """ + return _idaapi.pyscv_get_pos(self.__this, mouse) + + def GetLineNo(self, mouse = 0): + """Calls GetPos() and returns the current line number or -1 on failure""" + r = self.GetPos(mouse) + return -1 if not r else r[0] + + def Jump(self, lineno, x=0, y=0): + return _idaapi.pyscv_jumpto(self.__this, lineno, x, y) + + def AddPopupMenu(self, title, hotkey=""): + """ + Adds a popup menu item + @param title: The name of the menu item + @param hotkey: Hotkey of the item or just empty + @return: Returns the + """ + return _idaapi.pyscv_add_popup_menu(self.__this, title, hotkey) + + def ClearPopupMenu(self): + """ + Clears all previously installed popup menu items. + Use this function if you're generating menu items on the fly (in the OnPopup() callback), + and before adding new items + """ + _idaapi.pyscv_clear_popup_menu(self.__this) + + def IsFocused(self): + """Returns True if the current view is the focused view""" + return _idaapi.pyscv_is_focused(self.__this) + + def GetTForm(self): + """ + Return the TForm hosting this view. + + @return: The TForm that hosts this view, or None. + """ + return _idaapi.pyscv_get_tform(self.__this) + + def GetTCustomControl(self): + """ + Return the TCustomControl underlying this view. + + @return: The TCustomControl underlying this view, or None. + """ + return _idaapi.pyscv_get_tcustom_control(self.__this) + + + + # Here are all the supported events +# +# def OnClick(self, shift): +# """ +# User clicked in the view +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnClick, shift=%d" % shift +# return True +# +# def OnDblClick(self, shift): +# """ +# User dbl-clicked in the view +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnDblClick, shift=%d" % shift +# return True +# +# def OnCursorPosChanged(self): +# """ +# Cursor position changed. +# @return: Nothing +# """ +# print "OnCurposChanged" +# +# def OnClose(self): +# """ +# The view is closing. Use this event to cleanup. +# @return: Nothing +# """ +# print "OnClose" +# +# def OnKeydown(self, vkey, shift): +# """ +# User pressed a key +# @param vkey: Virtual key code +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) +# return False +# +# def OnPopup(self): +# """ +# Context menu popup is about to be shown. Create items dynamically if you wish +# @return: Boolean. True if you handled the event +# """ +# print "OnPopup" +# +# def OnHint(self, lineno): +# """ +# Hint requested for the given line number. +# @param lineno: The line number (zero based) +# @return: +# - tuple(number of important lines, hint string) +# - None: if no hint available +# """ +# return (1, "OnHint, line=%d" % lineno) +# +# def OnPopupMenu(self, menu_id): +# """ +# A context (or popup) menu item was executed. +# @param menu_id: ID previously registered with add_popup_menu() +# @return: Boolean +# """ +# print "OnPopupMenu, menu_id=" % menu_id +# return True +# +# +%} diff --git a/swig/lines.i b/swig/lines.i index 34f10a4b..eb282f7b 100644 --- a/swig/lines.i +++ b/swig/lines.i @@ -1,329 +1,330 @@ -// FIXME: These should be fixed -%ignore requires_color_esc; -%ignore tag_on; -%ignore tag_off; -%ignore tag_addchr; -%ignore tag_addstr; -%ignore get_user_defined_prefix; -// Ignore va_list versions -%ignore printf_line_v; -%ignore gen_colored_cmt_line_v; -%ignore gen_cmt_line_v; -%ignore add_long_cmt_v; -%ignore describex; -// Kernel-only and unexported symbols -%ignore init_sourcefiles; -%ignore save_sourcefiles; -%ignore term_sourcefiles; -%ignore move_sourcefiles; -%ignore gen_xref_lines; -%ignore ml_getcmt_t; -%ignore ml_getnam_t; -%ignore ml_genxrf_t; -%ignore ml_saver_t; -%ignore setup_makeline; -%ignore MAKELINE_NONE; -%ignore MAKELINE_BINPREF; -%ignore MAKELINE_VOID; -%ignore MAKELINE_STACK; -%ignore save_line_in_array; -%ignore init_lines_array; -%ignore finish_makeline; -%ignore finish_makeline_ex; -%ignore generate_many_lines_ex; -%ignore MakeNull_ex; -%ignore gen_labeled_line; -%ignore gen_lname_line; -%ignore makeline_producer_t; -%ignore set_makeline_producer; -%ignore closing_comment; -%ignore close_comment; -%ignore copy_extra_cmts; -%ignore gen_extra_cmts; -%ignore Dumper; -%ignore init_lines; -%ignore save_lines; -%ignore term_lines; -%ignore gl_namedone; -%ignore data_as_stack; -%ignore unhide_hint_text; -%ignore calc_stack_alignment; -%ignore align_down_to_stack; -%ignore align_up_to_stack; -%ignore remove_spaces; -%ignore bgcolors; - -%ignore set_user_defined_prefix; -%rename (set_user_defined_prefix) py_set_user_defined_prefix; - -%ignore generate_disassembly; -%rename (generate_disassembly) py_generate_disassembly; - -%ignore tag_remove; -%rename (tag_remove) py_tag_remove; - -%ignore tag_addr; -%rename (tag_addr) py_tag_addr; - -%ignore tag_skipcodes; -%rename (tag_skipcodes) py_tag_skipcodes; - -%ignore tag_skipcode; -%rename (tag_skipcode) py_tag_skipcode; - -%ignore tag_advance; -%rename (tag_advance) py_tag_advance; - -%include "lines.hpp" - -%{ -// -//------------------------------------------------------------------------ -static PyObject *py_get_user_defined_prefix = NULL; -static void idaapi s_py_get_user_defined_prefix( - ea_t ea, - int lnnum, - int indent, - const char *line, - char *buf, - size_t bufsize) -{ - PYW_GIL_GET; - newref_t py_ret( - PyObject_CallFunction( - py_get_user_defined_prefix, - PY_FMT64 "iis" PY_FMT64, - ea, lnnum, indent, line, bufsize)); - - // Error? Display it - // No error? Copy the buffer - if ( !PyW_ShowCbErr("py_get_user_defined_prefix") ) - { - Py_ssize_t py_len; - char *py_str; - if ( PyString_AsStringAndSize(py_ret.o, &py_str, &py_len) != -1 ) - { - memcpy(buf, py_str, qmin(bufsize, py_len)); - if ( py_len < bufsize ) - buf[py_len] = '\0'; - } - } -} -// -%} - -%inline %{ -// - -//------------------------------------------------------------------------ -/* -# -def set_user_defined_prefix(width, callback): - """ - User-defined line-prefixes are displayed just after the autogenerated - line prefixes. In order to use them, the plugin should call the - following function to specify its width and contents. - @param width: the width of the user-defined prefix - @param callback: a get_user_defined_prefix callback to get the contents of the prefix. - Its arguments: - ea - linear address - lnnum - line number - indent - indent of the line contents (-1 means the default instruction) - indent and is used for instruction itself. see explanations for printf_line() - line - the line to be generated. the line usually contains color tags this argument - can be examined to decide whether to generated the prefix - bufsize- the maximum allowed size of the output buffer - It returns a buffer of size < bufsize - - In order to remove the callback before unloading the plugin, specify the width = 0 or the callback = None - """ - pass -# -*/ -static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( width == 0 || pycb == Py_None ) - { - // Release old callback reference - Py_XDECREF(py_get_user_defined_prefix); - - // ...and clear it - py_get_user_defined_prefix = NULL; - - // Uninstall user defind prefix - set_user_defined_prefix(0, NULL); - } - else if ( PyCallable_Check(pycb) ) - { - // Release old callback reference - Py_XDECREF(py_get_user_defined_prefix); - - // Copy new callback and hold a reference - py_get_user_defined_prefix = pycb; - Py_INCREF(py_get_user_defined_prefix); - - set_user_defined_prefix(width, s_py_get_user_defined_prefix); - } - else - { - Py_RETURN_FALSE; - } - Py_RETURN_TRUE; -} - -//------------------------------------------------------------------------- -/* -# -def tag_remove(colstr): - """ - Remove color escape sequences from a string - @param colstr: the colored string with embedded tags - @return: - None on failure - or a new string w/o the tags - """ - pass -# -*/ -PyObject *py_tag_remove(const char *instr) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - size_t sz = strlen(instr); - char *buf = new char[sz + 5]; - if ( buf == NULL ) - Py_RETURN_NONE; - - ssize_t r = tag_remove(instr, buf, sz); - PyObject *res; - if ( r < 0 ) - { - Py_INCREF(Py_None); - res = Py_None; - } - else - { - res = PyString_FromString(buf); - } - delete [] buf; - return res; -} - -//------------------------------------------------------------------------- -PyObject *py_tag_addr(ea_t ea) -{ - char buf[100]; - tag_addr(buf, buf + sizeof(buf), ea); - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyString_FromString(buf); -} - -//------------------------------------------------------------------------- -int py_tag_skipcode(const char *line) -{ - return tag_skipcode(line)-line; -} - -//------------------------------------------------------------------------- -int py_tag_skipcodes(const char *line) -{ - return tag_skipcodes(line)-line; -} - -//------------------------------------------------------------------------- -int py_tag_advance(const char *line, int cnt) -{ - return tag_advance(line, cnt)-line; -} - -//------------------------------------------------------------------------- -/* -# -def generate_disassembly(ea, max_lines, as_stack, notags): - """ - Generate disassembly lines (many lines) and put them into a buffer - - @param ea: address to generate disassembly for - @param max_lines: how many lines max to generate - @param as_stack: Display undefined items as 2/4/8 bytes - @return: - - None on failure - - tuple(most_important_line_number, tuple(lines)) : Returns a tuple containing - the most important line number and a tuple of generated lines - """ - pass -# -*/ -PyObject *py_generate_disassembly( - ea_t ea, - int max_lines, - bool as_stack, - bool notags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( max_lines <= 0 ) - Py_RETURN_NONE; - - qstring qbuf; - char **lines = new char *[max_lines]; - int lnnum; - int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); - - newref_t py_tuple(PyTuple_New(nlines)); - for ( int i=0; i - -%} - -%pythoncode %{ -# - -# ---------------- Color escape sequence defitions ------------------------- -COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 -SCOLOR_FG_MAX = '\x28' # Max color number -SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 -SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 -SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 -SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 -SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 -SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 -SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded - -# ---------------- Line prefix colors -------------------------------------- -PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) - -def requires_color_esc(c): - """ - Checks if the given character requires escaping - @param c: character (string of one char) - @return: Boolean - """ - t = ord(c[0]) - return c >= COLOR_ON and c <= COLOR_INV - -def COLSTR(str, tag): - """ - Utility function to create a colored line - @param str: The string - @param tag: Color tag constant. One of SCOLOR_XXXX - """ - return SCOLOR_ON + tag + str + SCOLOR_OFF + tag - -# - -%} \ No newline at end of file +// FIXME: These should be fixed +%ignore requires_color_esc; +%ignore tag_on; +%ignore tag_off; +%ignore tag_addchr; +%ignore tag_addstr; +%ignore get_user_defined_prefix; +// Ignore va_list versions +%ignore printf_line_v; +%ignore gen_colored_cmt_line_v; +%ignore gen_cmt_line_v; +%ignore add_long_cmt_v; +%ignore describex; +// Kernel-only and unexported symbols +%ignore init_sourcefiles; +%ignore save_sourcefiles; +%ignore term_sourcefiles; +%ignore move_sourcefiles; +%ignore gen_xref_lines; +%ignore ml_getcmt_t; +%ignore ml_getnam_t; +%ignore ml_genxrf_t; +%ignore ml_saver_t; +%ignore setup_makeline; +%ignore MAKELINE_NONE; +%ignore MAKELINE_BINPREF; +%ignore MAKELINE_VOID; +%ignore MAKELINE_STACK; +%ignore save_line_in_array; +%ignore init_lines_array; +%ignore finish_makeline; +%ignore finish_makeline_ex; +%ignore generate_many_lines_ex; +%ignore MakeNull_ex; +%ignore gen_labeled_line; +%ignore gen_lname_line; +%ignore makeline_producer_t; +%ignore set_makeline_producer; +%ignore closing_comment; +%ignore close_comment; +%ignore copy_extra_cmts; +%ignore gen_extra_cmts; +%ignore Dumper; +%ignore init_lines; +%ignore save_lines; +%ignore term_lines; +%ignore gl_namedone; +%ignore data_as_stack; +%ignore unhide_hint_text; +%ignore calc_stack_alignment; +%ignore align_down_to_stack; +%ignore align_up_to_stack; +%ignore remove_spaces; +%ignore prepare_ghost_copy_desc; +%ignore bgcolors; + +%ignore set_user_defined_prefix; +%rename (set_user_defined_prefix) py_set_user_defined_prefix; + +%ignore generate_disassembly; +%rename (generate_disassembly) py_generate_disassembly; + +%ignore tag_remove; +%rename (tag_remove) py_tag_remove; + +%ignore tag_addr; +%rename (tag_addr) py_tag_addr; + +%ignore tag_skipcodes; +%rename (tag_skipcodes) py_tag_skipcodes; + +%ignore tag_skipcode; +%rename (tag_skipcode) py_tag_skipcode; + +%ignore tag_advance; +%rename (tag_advance) py_tag_advance; + +%include "lines.hpp" + +%{ +// +//------------------------------------------------------------------------ +static PyObject *py_get_user_defined_prefix = NULL; +static void idaapi s_py_get_user_defined_prefix( + ea_t ea, + int lnnum, + int indent, + const char *line, + char *buf, + size_t bufsize) +{ + PYW_GIL_GET; + newref_t py_ret( + PyObject_CallFunction( + py_get_user_defined_prefix, + PY_FMT64 "iis" PY_FMT64, + ea, lnnum, indent, line, bufsize)); + + // Error? Display it + // No error? Copy the buffer + if ( !PyW_ShowCbErr("py_get_user_defined_prefix") ) + { + Py_ssize_t py_len; + char *py_str; + if ( PyString_AsStringAndSize(py_ret.o, &py_str, &py_len) != -1 ) + { + memcpy(buf, py_str, qmin(bufsize, py_len)); + if ( py_len < bufsize ) + buf[py_len] = '\0'; + } + } +} +// +%} + +%inline %{ +// + +//------------------------------------------------------------------------ +/* +# +def set_user_defined_prefix(width, callback): + """ + User-defined line-prefixes are displayed just after the autogenerated + line prefixes. In order to use them, the plugin should call the + following function to specify its width and contents. + @param width: the width of the user-defined prefix + @param callback: a get_user_defined_prefix callback to get the contents of the prefix. + Its arguments: + ea - linear address + lnnum - line number + indent - indent of the line contents (-1 means the default instruction) + indent and is used for instruction itself. see explanations for printf_line() + line - the line to be generated. the line usually contains color tags this argument + can be examined to decide whether to generated the prefix + bufsize- the maximum allowed size of the output buffer + It returns a buffer of size < bufsize + + In order to remove the callback before unloading the plugin, specify the width = 0 or the callback = None + """ + pass +# +*/ +static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( width == 0 || pycb == Py_None ) + { + // Release old callback reference + Py_XDECREF(py_get_user_defined_prefix); + + // ...and clear it + py_get_user_defined_prefix = NULL; + + // Uninstall user defind prefix + set_user_defined_prefix(0, NULL); + } + else if ( PyCallable_Check(pycb) ) + { + // Release old callback reference + Py_XDECREF(py_get_user_defined_prefix); + + // Copy new callback and hold a reference + py_get_user_defined_prefix = pycb; + Py_INCREF(py_get_user_defined_prefix); + + set_user_defined_prefix(width, s_py_get_user_defined_prefix); + } + else + { + Py_RETURN_FALSE; + } + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +/* +# +def tag_remove(colstr): + """ + Remove color escape sequences from a string + @param colstr: the colored string with embedded tags + @return: + None on failure + or a new string w/o the tags + """ + pass +# +*/ +PyObject *py_tag_remove(const char *instr) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + size_t sz = strlen(instr); + char *buf = new char[sz + 5]; + if ( buf == NULL ) + Py_RETURN_NONE; + + ssize_t r = tag_remove(instr, buf, sz); + PyObject *res; + if ( r < 0 ) + { + Py_INCREF(Py_None); + res = Py_None; + } + else + { + res = PyString_FromString(buf); + } + delete [] buf; + return res; +} + +//------------------------------------------------------------------------- +PyObject *py_tag_addr(ea_t ea) +{ + char buf[100]; + tag_addr(buf, buf + sizeof(buf), ea); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +int py_tag_skipcode(const char *line) +{ + return tag_skipcode(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_skipcodes(const char *line) +{ + return tag_skipcodes(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_advance(const char *line, int cnt) +{ + return tag_advance(line, cnt)-line; +} + +//------------------------------------------------------------------------- +/* +# +def generate_disassembly(ea, max_lines, as_stack, notags): + """ + Generate disassembly lines (many lines) and put them into a buffer + + @param ea: address to generate disassembly for + @param max_lines: how many lines max to generate + @param as_stack: Display undefined items as 2/4/8 bytes + @return: + - None on failure + - tuple(most_important_line_number, tuple(lines)) : Returns a tuple containing + the most important line number and a tuple of generated lines + """ + pass +# +*/ +PyObject *py_generate_disassembly( + ea_t ea, + int max_lines, + bool as_stack, + bool notags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( max_lines <= 0 ) + Py_RETURN_NONE; + + qstring qbuf; + char **lines = new char *[max_lines]; + int lnnum; + int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); + + newref_t py_tuple(PyTuple_New(nlines)); + for ( int i=0; i + +%} + +%pythoncode %{ +# + +# ---------------- Color escape sequence defitions ------------------------- +COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 +SCOLOR_FG_MAX = '\x28' # Max color number +SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 +SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 +SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 +SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 +SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 +SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 +SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded + +# ---------------- Line prefix colors -------------------------------------- +PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) + +def requires_color_esc(c): + """ + Checks if the given character requires escaping + @param c: character (string of one char) + @return: Boolean + """ + t = ord(c[0]) + return c >= COLOR_ON and c <= COLOR_INV + +def COLSTR(str, tag): + """ + Utility function to create a colored line + @param str: The string + @param tag: Color tag constant. One of SCOLOR_XXXX + """ + return SCOLOR_ON + tag + str + SCOLOR_OFF + tag + +# + +%} diff --git a/swig/loader.i b/swig/loader.i index 520a16b8..c49f7da6 100644 --- a/swig/loader.i +++ b/swig/loader.i @@ -1,221 +1,221 @@ -// Ignore callback members -%ignore loader_t::accept_file; -%ignore loader_t::load_file; -%ignore loader_t::save_file; -%ignore loader_t::move_segm; -%ignore loader_t::init_loader_options; -%ignore plugin_t; - -%ignore vloader_failure; -%ignore loader_failure; - -// TODO: These could be wrapped if needed -%ignore load_info_t; -%ignore add_plugin_option; -%ignore get_plugins_path; -%ignore build_loaders_list; -%ignore free_loaders_list; -%ignore get_loader_name_from_dll; -%ignore get_loader_name; -%ignore init_loader_options; -%ignore load_nonbinary_file; -%ignore impinfo_t; -%ignore import_module; -%ignore plugin_info_t; -%ignore get_plugins; -%ignore invoke_plugin; -%ignore dbg_info_t; -%ignore get_debugger_plugins; -%ignore init_plugins; -%ignore term_plugins; - -// Callback and loader-only symbols are ignored (for now) -%ignore html_header_cb_t; -%ignore html_footer_cb_t; -%ignore html_line_cb_t; -%ignore gen_outline_t; -%ignore create_filename_cmt; -%ignore hook_cb_t; -%ignore hook_type_t; -%ignore hook_to_notification_point; -%ignore unhook_from_notification_point; -%ignore invoke_callbacks; - -// Ignore this experimental function -%ignore gen_dev_event; - -// Ignore kernel-only & unexported symbols -%ignore LDSC; -%ignore PLUGIN; -%ignore LNE_MAXSEG; -%ignore dlldata; -%ignore DLLDATASTART; -%ignore ldrdata; -%ignore LDRDATASTART; -%ignore idadll_t; -%ignore load_dll; -%ignore RE_NOFILE; -%ignore RE_NOTIDP; -%ignore RE_NOPAGE; -%ignore RE_NOLINK; -%ignore RE_BADRTP; -%ignore RE_BADORD; -%ignore RE_BADATP; -%ignore RE_BADMAP; -%ignore load_dll_or_die; -%ignore load_dll_or_say; -%ignore free_dll; -%ignore IDP_DESC_START; -%ignore IDP_DESC_END; -%ignore get_idp_desc; -%ignore get_idp_descs; -%ignore init_fileregions; -%ignore term_fileregions; -%ignore save_fileregions; -%ignore add_fileregion; -%ignore move_fileregions; -%ignore del_fileregions; -%ignore local_gen_idc_file; -%ignore print_all_places; -%ignore save_text_line; -%ignore print_all_structs; -%ignore print_all_enums; -%ignore enum_processor_modules; -%ignore enum_plugins; -%ignore database_id0; -%ignore is_database_ext; -%ignore ida_database_memory; -%ignore ida_workdir; -%ignore is_temp_database; -%ignore pe_create_idata; -%ignore pe_load_resources; -%ignore pe_create_flat_group; -%ignore initializing; -%ignore highest_processor_level; -%ignore dbcheck_t; -%ignore DBCHK_NONE; -%ignore DBCHK_OK; -%ignore DBCHK_BAD; -%ignore DBCHK_NEW; -%ignore check_database; -%ignore open_database; -%ignore get_workbase_fname; -%ignore close_database; -%ignore compress_btree; -%ignore get_input_file_from_archive; -%ignore loader_move_segm; -%ignore generate_ida_copyright; -%ignore clear_plugin_options; -%ignore is_in_loader; -%ignore get_ids_filename; -%ignore is_embedded_dbfile_ext; -%ignore cpp_namespaces; -%ignore max_trusted_idb_count; -%ignore no_disk_space_handler; - -%ignore mem2base; -%rename (mem2base) py_mem2base; -%ignore update_snapshot_attributes; -%ignore build_snapshot_tree; -%ignore visit_snapshot_tree; -%ignore snapshot_t; -%ignore snapshots_t; -%ignore load_plugin; -%rename (load_plugin) py_load_plugin; -%ignore run_plugin; -%rename (run_plugin) py_run_plugin; - -%include "loader.hpp" - -%inline %{ -// - -//------------------------------------------------------------------------ -/* -# -def mem2base(mem, ea, fpos): - """ - Load database from the memory. - @param mem: the buffer - @param ea: start linear addresses - @param fpos: position in the input file the data is taken from. - if == -1, then no file position correspond to the data. - @return: - - Returns zero if the passed buffer was not a string - - Otherwise 1 is returned - """ - pass -# -*/ -static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) -{ - Py_ssize_t len; - char *buf; - { - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) - return 0; - } - - return mem2base((void *)buf, ea, ea+len, fpos); -} - -//------------------------------------------------------------------------ -/* -# -def load_plugin(name): - """ - Loads a plugin - @return: - - None if plugin could not be loaded - - An opaque object representing the loaded plugin - """ - pass -# -*/ -static PyObject *py_load_plugin(const char *name) -{ - plugin_t *r = load_plugin(name); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( r == NULL ) - Py_RETURN_NONE; - else - return PyCObject_FromVoidPtr(r, NULL); -} - -//------------------------------------------------------------------------ -/* -# -def run_plugin(plg): - """ - Runs a plugin - @param plg: A plugin object (returned by load_plugin()) - @return: Boolean - """ - pass -# -*/ -static bool py_run_plugin(PyObject *plg, int arg) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(plg) ) - { - return false; - } - else - { - plugin_t *p = (plugin_t *)PyCObject_AsVoidPtr(plg); - bool rc; - Py_BEGIN_ALLOW_THREADS; - rc = run_plugin(p, arg); - Py_END_ALLOW_THREADS; - return rc; - } -} - -// - -%} - - - +// Ignore callback members +%ignore loader_t::accept_file; +%ignore loader_t::load_file; +%ignore loader_t::save_file; +%ignore loader_t::move_segm; +%ignore loader_t::init_loader_options; +%ignore plugin_t; + +%ignore vloader_failure; +%ignore loader_failure; + +// TODO: These could be wrapped if needed +%ignore load_info_t; +%ignore add_plugin_option; +%ignore get_plugins_path; +%ignore build_loaders_list; +%ignore free_loaders_list; +%ignore get_loader_name_from_dll; +%ignore get_loader_name; +%ignore init_loader_options; +%ignore load_nonbinary_file; +%ignore impinfo_t; +%ignore import_module; +%ignore plugin_info_t; +%ignore get_plugins; +%ignore invoke_plugin; +%ignore dbg_info_t; +%ignore get_debugger_plugins; +%ignore init_plugins; +%ignore term_plugins; + +// Callback and loader-only symbols are ignored (for now) +%ignore html_header_cb_t; +%ignore html_footer_cb_t; +%ignore html_line_cb_t; +%ignore gen_outline_t; +%ignore create_filename_cmt; +%ignore hook_cb_t; +%ignore hook_type_t; +%ignore hook_to_notification_point; +%ignore unhook_from_notification_point; +%ignore invoke_callbacks; + +// Ignore this experimental function +%ignore gen_dev_event; + +// Ignore kernel-only & unexported symbols +%ignore LDSC; +%ignore PLUGIN; +%ignore LNE_MAXSEG; +%ignore dlldata; +%ignore DLLDATASTART; +%ignore ldrdata; +%ignore LDRDATASTART; +%ignore idadll_t; +%ignore load_dll; +%ignore RE_NOFILE; +%ignore RE_NOTIDP; +%ignore RE_NOPAGE; +%ignore RE_NOLINK; +%ignore RE_BADRTP; +%ignore RE_BADORD; +%ignore RE_BADATP; +%ignore RE_BADMAP; +%ignore load_dll_or_die; +%ignore load_dll_or_say; +%ignore free_dll; +%ignore IDP_DESC_START; +%ignore IDP_DESC_END; +%ignore get_idp_desc; +%ignore get_idp_descs; +%ignore init_fileregions; +%ignore term_fileregions; +%ignore save_fileregions; +%ignore add_fileregion; +%ignore move_fileregions; +%ignore del_fileregions; +%ignore local_gen_idc_file; +%ignore print_all_places; +%ignore save_text_line; +%ignore print_all_structs; +%ignore print_all_enums; +%ignore enum_processor_modules; +%ignore enum_plugins; +%ignore database_id0; +%ignore is_database_ext; +%ignore ida_database_memory; +%ignore ida_workdir; +%ignore is_temp_database; +%ignore pe_create_idata; +%ignore pe_load_resources; +%ignore pe_create_flat_group; +%ignore initializing; +%ignore highest_processor_level; +%ignore dbcheck_t; +%ignore DBCHK_NONE; +%ignore DBCHK_OK; +%ignore DBCHK_BAD; +%ignore DBCHK_NEW; +%ignore check_database; +%ignore open_database; +%ignore get_workbase_fname; +%ignore close_database; +%ignore compress_btree; +%ignore get_input_file_from_archive; +%ignore loader_move_segm; +%ignore generate_ida_copyright; +%ignore clear_plugin_options; +%ignore is_in_loader; +%ignore get_ids_filename; +%ignore is_embedded_dbfile_ext; +%ignore cpp_namespaces; +%ignore max_trusted_idb_count; +%ignore no_disk_space_handler; + +%ignore mem2base; +%rename (mem2base) py_mem2base; +%ignore update_snapshot_attributes; +%ignore build_snapshot_tree; +%ignore visit_snapshot_tree; +%ignore snapshot_t; +%ignore snapshots_t; +%ignore load_plugin; +%rename (load_plugin) py_load_plugin; +%ignore run_plugin; +%rename (run_plugin) py_run_plugin; + +%include "loader.hpp" + +%inline %{ +// + +//------------------------------------------------------------------------ +/* +# +def mem2base(mem, ea, fpos): + """ + Load database from the memory. + @param mem: the buffer + @param ea: start linear addresses + @param fpos: position in the input file the data is taken from. + if == -1, then no file position correspond to the data. + @return: + - Returns zero if the passed buffer was not a string + - Otherwise 1 is returned + """ + pass +# +*/ +static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) +{ + Py_ssize_t len; + char *buf; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) + return 0; + } + + return mem2base((void *)buf, ea, ea+len, fpos); +} + +//------------------------------------------------------------------------ +/* +# +def load_plugin(name): + """ + Loads a plugin + @return: + - None if plugin could not be loaded + - An opaque object representing the loaded plugin + """ + pass +# +*/ +static PyObject *py_load_plugin(const char *name) +{ + plugin_t *r = load_plugin(name); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( r == NULL ) + Py_RETURN_NONE; + else + return PyCObject_FromVoidPtr(r, NULL); +} + +//------------------------------------------------------------------------ +/* +# +def run_plugin(plg): + """ + Runs a plugin + @param plg: A plugin object (returned by load_plugin()) + @return: Boolean + """ + pass +# +*/ +static bool py_run_plugin(PyObject *plg, int arg) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(plg) ) + { + return false; + } + else + { + plugin_t *p = (plugin_t *)PyCObject_AsVoidPtr(plg); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = run_plugin(p, arg); + Py_END_ALLOW_THREADS; + return rc; + } +} + +// + +%} + + + diff --git a/swig/moves.i b/swig/moves.i index 9255d0b9..3a2a1904 100644 --- a/swig/moves.i +++ b/swig/moves.i @@ -10,4 +10,4 @@ %ignore DEFINE_CURLOC_HELPERS; %ignore DEFINE_LOCATION_HELPERS; -%include "moves.hpp" \ No newline at end of file +%include "moves.hpp" diff --git a/swig/nalt.i b/swig/nalt.i index 0d555dbe..618f1c5e 100644 --- a/swig/nalt.i +++ b/swig/nalt.i @@ -1,882 +1,882 @@ -%ignore nmSerEA; -%ignore nmSerN; -%ignore maxSerialName; -%ignore get_import_module_name; -%rename (get_import_module_name) py_get_import_module_name; -%ignore NALT_EA; -%ignore enum_import_names; -%rename (enum_import_names) py_enum_import_names; - -%ignore get_wide_value; -%ignore set_wide_value; -%ignore del_wide_value; - -%ignore get_strid; -%ignore _set_strid; -%ignore _del_strid; -%ignore set_strid; -%ignore del_strid; -%ignore xrefpos_t; -%ignore get_xrefpos; -%ignore set_xrefpos; -%ignore del_xrefpos; - -%ignore set_aflags0; -%ignore get_aflags0; -%ignore del_aflags0; - -%ignore get_linnum0; -%ignore set_linnum0; -%ignore del_linnum0; - -%ignore get_enum_id0; -%ignore set_enum_id0; -%ignore del_enum_id0; -%ignore get_enum_id1; -%ignore set_enum_id1; -%ignore del_enum_id1; - -%ignore set_ind_purged; - -%ignore get_str_type; -%ignore set_str_type; -%ignore del_str_type; - -%ignore _get_item_color; -%ignore _set_item_color; -%ignore _del_item_color; - -%ignore get_nalt_cmt; -%ignore set_nalt_cmt; -%ignore del_nalt_cmt; -%ignore get_nalt_rptcmt; -%ignore set_nalt_rptcmt; -%ignore del_nalt_rptcmt; -%ignore get_fop1; -%ignore set_fop1; -%ignore del_fop1; -%ignore get_fop2; -%ignore set_fop2; -%ignore del_fop2; -%ignore get_fop3; -%ignore set_fop3; -%ignore del_fop3; -%ignore get_fop4; -%ignore set_fop4; -%ignore del_fop4; -%ignore get_fop5; -%ignore set_fop5; -%ignore del_fop5; -%ignore get_fop6; -%ignore set_fop6; -%ignore del_fop6; -%ignore get_manual_insn0; -%ignore set_manual_insn0; -%ignore del_manual_insn0; -%ignore get_graph_groups0; -%ignore set_graph_groups0; -%ignore del_graph_groups0; - -%ignore switch_info_t; -%ignore switch_info_ex_t; -%ignore get_switch_info_ex; -%ignore set_switch_info_ex; -%ignore del_switch_info_ex; - -%ignore refinfo_t::_get_target; -%ignore refinfo_t::_get_value; -%ignore refinfo_t::_get_opval; - -%ignore custom_refinfo_handler_t; -%ignore custom_refinfo_handlers_t; -%ignore register_custom_refinfo; -%ignore unregister_custom_refinfo; -%ignore get_custom_refinfos; - -%ignore write_struc_path; -%ignore read_struc_path; -%ignore del_struc_path; -%ignore get_stroff0; -%ignore set_stroff0; -%ignore del_stroff0; -%ignore get_stroff1; -%ignore set_stroff1; -%ignore del_stroff1; - -%ignore get__segtrans; -%ignore set__segtrans; -%ignore del__segtrans; - -%ignore get_switch_info; -%ignore set_switch_info; -%ignore del_switch_info; -%ignore get_ti; -%ignore set_ti; -%ignore del_ti; -%ignore get_op_tinfo; -%ignore set_op_tinfo; -%ignore del_tinfo; -%ignore get_op_ti; -%ignore set_op_ti; -%ignore del_ti; - -%template (ids_array) wrapped_array_t; - -%extend strpath_t { - wrapped_array_t __getIds() { - return wrapped_array_t($self->ids); - } - - %pythoncode { - ids = property(__getIds) - } -} - -%include "nalt.hpp" - -%{ -// - -//------------------------------------------------------------------------- -// callback for enumerating imports -// ea: import address -// name: import name (NULL if imported by ordinal) -// ord: import ordinal (0 for imports by name) -// param: user parameter passed to enum_import_names() -// return: 1-ok, 0-stop enumeration -static int idaapi py_import_enum_cb( - ea_t ea, - const char *name, - uval_t ord, - void *param) -{ - // If no name, try to get the name associated with the 'ea'. It may be coming from IDS - char name_buf[MAXSTR]; - if ( name == NULL ) - name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf)); - - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_name; - if ( name == NULL ) - py_name = borref_t(Py_None); - else - py_name = newref_t(PyString_FromString(name)); - - newref_t py_ord(Py_BuildValue(PY_FMT64, pyul_t(ord))); - newref_t py_ea(Py_BuildValue(PY_FMT64, pyul_t(ea))); - newref_t py_result( - PyObject_CallFunctionObjArgs( - (PyObject *)param, - py_ea.o, - py_name.o, - py_ord.o, - NULL)); - return py_result != NULL && PyObject_IsTrue(py_result.o) ? 1 : 0; -} - -//------------------------------------------------------------------------- -switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) - return NULL; - - switch_info_ex_t *r; - newref_t attr(PyObject_GetAttrString(self, S_CLINK_NAME)); - return PyCObject_Check(attr.o) ? ((switch_info_ex_t *) PyCObject_AsVoidPtr(attr.o)) : NULL; -} -// -%} - -%rename (get_switch_info_ex) py_get_switch_info_ex; -%rename (set_switch_info_ex) py_set_switch_info_ex; -%rename (del_switch_info_ex) py_del_switch_info_ex; -%rename (create_switch_xrefs) py_create_switch_xrefs; -%rename (create_switch_table) py_create_switch_table; -%rename (calc_switch_cases) py_calc_switch_cases; - -%inline %{ -// - -//------------------------------------------------------------------------- -/* -# -def get_import_module_name(path, fname, callback): - """ - Returns the name of an imported module given its index - @return: None or the module name - """ - pass -# -*/ -static PyObject *py_get_import_module_name(int mod_index) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - char buf[MAXSTR]; - if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) - Py_RETURN_NONE; - - return PyString_FromString(buf); -} - -//------------------------------------------------------------------------- -/* -# -def get_switch_info_ex(ea): - """ - Returns the a switch_info_ex_t structure containing the information about the switch. - Please refer to the SDK sample 'uiswitch' - @return: None or switch_info_ex_t instance - """ - pass -# -*/ -PyObject *py_get_switch_info_ex(ea_t ea) -{ - switch_info_ex_t *ex = new switch_info_ex_t(); - ref_t py_obj; - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 - || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) - { - delete ex; - Py_RETURN_NONE; - } - py_obj.incref(); - return py_obj.o; -} - -//------------------------------------------------------------------------- -/* -# -def create_switch_xrefs(insn_ea, si): - """ - This function creates xrefs from the indirect jump. - - Usually there is no need to call this function directly because the kernel - will call it for switch tables - - Note: Custom switch information are not supported yet. - - @param insn_ea: address of the 'indirect jump' instruction - @param si: switch information - - @return: Boolean - """ - pass -# -*/ -idaman bool ida_export py_create_switch_xrefs( - ea_t insn_ea, - PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return false; - - create_switch_xrefs(insn_ea, swi); - return true; -} - -//------------------------------------------------------------------------- -struct cases_and_targets_t -{ - casevec_t cases; - eavec_t targets; -}; - -//------------------------------------------------------------------------- -/* -# -def calc_switch_cases(insn_ea, si): - """ - Get information about a switch's cases. - - The returned information can be used as follows: - - for idx in xrange(len(results.cases)): - cur_case = results.cases[idx] - for cidx in xrange(len(cur_case)): - print "case: %d" % cur_case[cidx] - print " goto 0x%x" % results.targets[idx] - - @param insn_ea: address of the 'indirect jump' instruction - @param si: switch information - - @return: a structure with 2 members: 'cases', and 'targets'. - """ - pass -# -*/ -idaman cases_and_targets_t *ida_export py_calc_switch_cases( - ea_t insn_ea, - PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return NULL; - - cases_and_targets_t *ct = new cases_and_targets_t; - if ( !calc_switch_cases(insn_ea, swi, &ct->cases, &ct->targets) ) - { - delete ct; - return NULL; - } - - return ct; -} - - -//------------------------------------------------------------------------- -/* -# -def create_switch_table(insn_ea, si): - """ - Create switch table from the switch information - - @param insn_ea: address of the 'indirect jump' instruction - @param si: switch information - - @return: Boolean - """ - pass -# -*/ -idaman bool ida_export py_create_switch_table( - ea_t insn_ea, - PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return false; - - create_switch_table(insn_ea, swi); - return true; -} - -//------------------------------------------------------------------------- -/* -# -def set_switch_info_ex(ea, switch_info_ex): - """ - Saves the switch information in the database - Please refer to the SDK sample 'uiswitch' - @return: Boolean - """ - pass -# -*/ -bool py_set_switch_info_ex(ea_t ea, PyObject *py_swi) -{ - switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); - if ( swi == NULL ) - return false; - - set_switch_info_ex(ea, swi); - return true; -} - -//------------------------------------------------------------------------- -/* -# -def del_switch_info_ex(ea): - """ - Deletes stored switch information - """ - pass -# -*/ -void py_del_switch_info_ex(ea_t ea) -{ - del_switch_info_ex(ea); -} - -//------------------------------------------------------------------------- -/* -# -def enum_import_names(mod_index, callback): - """ - Enumerate imports from a specific module. - Please refer to ex_imports.py example. - - @param mod_index: The module index - @param callback: A callable object that will be invoked with an ea, name (could be None) and ordinal. - @return: 1-finished ok, -1 on error, otherwise callback return value (<=0) - """ - pass -# -*/ -static int py_enum_import_names(int mod_index, PyObject *py_cb) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCallable_Check(py_cb) ) - return -1; - return enum_import_names(mod_index, py_import_enum_cb, py_cb); -} - -//------------------------------------------------------------------------- -static PyObject *switch_info_ex_t_create() -{ - switch_info_ex_t *inst = new switch_info_ex_t(); - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(inst, NULL); -} - -//--------------------------------------------------------------------------- -static bool switch_info_ex_t_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); - delete inst; - return true; -} - -static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) -{ - switch_info_ex_t *lhs = switch_info_ex_t_get_clink(self); - switch_info_ex_t *rhs = switch_info_ex_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -// Auto generated - begin -// - -static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", (char)link->regdtyp); -} -static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->regdtyp = (char)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_flags2(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("i", link->flags2); -} -static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->flags2 = (int)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_jcases(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("i", link->jcases); -} -static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->jcases = (int)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_regnum(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("i", (int)link->regnum); -} -static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->regnum = (int)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_flags(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", (ushort)link->flags); -} -static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->flags = (uint16)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_ncases(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", (uint16)link->ncases); -} -static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - link->ncases = (ushort)PyInt_AsLong(value); -} - -static PyObject *switch_info_ex_t_get_defjump(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); -} -static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - uint64 v(0); PyW_GetNumber(value, &v); - link->defjump = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_jumps(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->jumps); -} -static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - PYW_GIL_CHECK_LOCKED_SCOPE(); - uint64 v(0); PyW_GetNumber(value, &v); - link->jumps = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_elbase(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); -} -static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->elbase = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_startea(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); -} -static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->startea = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_custom(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); -} -static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->custom = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); -} -static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->ind_lowcase = (pyul_t)v; -} - -static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->values); -} -static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) -{ - switch_info_ex_t *link = switch_info_ex_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyW_GetNumber(value, &v); - link->values = (pyul_t)v; -} - -// -// Auto generated - end -// -//------------------------------------------------------------------------- -// -%} - -%pythoncode %{ -# -SWI_SPARSE = 0x1 -"""sparse switch ( value table present ) otherwise lowcase present""" -SWI_V32 = 0x2 -"""32-bit values in table""" -SWI_J32 = 0x4 -"""32-bit jump offsets""" -SWI_VSPLIT = 0x8 -"""value table is split (only for 32-bit values)""" -SWI_DEFAULT = 0x10 -"""default case is present""" -SWI_END_IN_TBL = 0x20 -"""switchend in table (default entry)""" -SWI_JMP_INV = 0x40 -"""jumptable is inversed (last entry is for first entry in values table)""" -SWI_SHIFT_MASK = 0x180 -"""use formula (element*shift + elbase) to find jump targets""" - -SWI_ELBASE = 0x200 -"""elbase is present (if not and shift!=0, endof(jumpea) is used)""" -SWI_JSIZE = 0x400 -"""jump offset expansion bit""" - -SWI_VSIZE = 0x800 -"""value table element size expansion bit""" - -SWI_SEPARATE = 0x1000 -"""do not create an array of individual dwords""" - -SWI_SIGNED = 0x2000 -"""jump table entries are signed""" - -SWI_CUSTOM = 0x4000 -"""custom jump table - ph.create_switch_xrefs will be called to create code xrefs for the table. it must return 2. custom jump table must be created by the module""" - -SWI_EXTENDED = 0x8000 -"""this is switch_info_ex_t""" - -SWI2_INDIRECT = 0x0001 -"""value table elements are used as indexes into the jump table""" -SWI2_SUBTRACT = 0x0002 -"""table values are subtracted from the elbase instead of being addded""" - -# -------------------------------------------------------------------------- -class switch_info_ex_t(py_clinked_object_t): - def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) - - def _create_clink(self): - return _idaapi.switch_info_ex_t_create() - - def _del_clink(self, lnk): - return _idaapi.switch_info_ex_t_destroy(lnk) - - def assign(self, other): - return _idaapi.switch_info_ex_t_assign(self, other) - - def is_indirect(self): - return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_INDIRECT) != 0 - - def is_subtract(self): - return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_SUBTRACT) != 0 - - def get_jtable_size(self): - return self.jcases if self.is_indirect() else self.ncases - - def get_lowcase(self): - return self.ind_lowcase if self.is_indirect() else self.lowcase - - def set_expr(self, r, dt): - self.regnum = r - self.regdtyp = dt - - def get_shift(self): - return (self.flags & SWI_SHIFT_MASK) >> 7 - - def set_shift(self, shift): - self.flags &= ~SWI_SHIFT_MASK - self.flags |= ((shift & 3) << 7) - - def get_jtable_element_size(self): - code = self.flags & (SWI_J32|SWI_JSIZE) - if code == 0: return 2 - elif code == SWI_J32: return 4 - elif code == SWI_JSIZE: return 1 - else: return 8 - - def set_jtable_element_size(self, size): - self.flags &= ~(SWI_J32|SWI_JSIZE) - if size == 4: self.flags |= SWI_J32 - elif size == 1: self.flags |= SWI_JSIZE - elif size == 8: self.flags |= SWI_J32|SWI_JSIZE - elif size != 2: return False - return True - - def get_vtable_element_size(self): - code = self.flags & (SWI_V32|SWI_VSIZE) - if code == 0: return 2 - elif code == SWI_V32: return 4 - elif code == SWI_VSIZE: return 1 - return 8 - - def set_vtable_element_size(self, size): - self.flags &= ~SWI_V32|SWI_VSIZE - if size == 4: self.flags |= SWI_V32 - elif size == 1: self.flags |= SWI_VSIZE - elif size == 8: self.flags |= SWI_V32|SWI_VSIZE - elif size != 2: return False - return True - - # - # Autogenerated - # - def __get_regdtyp__(self): - return _idaapi.switch_info_ex_t_get_regdtyp(self) - def __set_regdtyp__(self, v): - _idaapi.switch_info_ex_t_set_regdtyp(self, v) - def __get_flags2__(self): - return _idaapi.switch_info_ex_t_get_flags2(self) - def __set_flags2__(self, v): - _idaapi.switch_info_ex_t_set_flags2(self, v) - def __get_jcases__(self): - return _idaapi.switch_info_ex_t_get_jcases(self) - def __set_jcases__(self, v): - _idaapi.switch_info_ex_t_set_jcases(self, v) - def __get_regnum__(self): - return _idaapi.switch_info_ex_t_get_regnum(self) - def __set_regnum__(self, v): - _idaapi.switch_info_ex_t_set_regnum(self, v) - def __get_flags__(self): - return _idaapi.switch_info_ex_t_get_flags(self) - def __set_flags__(self, v): - _idaapi.switch_info_ex_t_set_flags(self, v) - def __get_ncases__(self): - return _idaapi.switch_info_ex_t_get_ncases(self) - def __set_ncases__(self, v): - _idaapi.switch_info_ex_t_set_ncases(self, v) - def __get_defjump__(self): - return _idaapi.switch_info_ex_t_get_defjump(self) - def __set_defjump__(self, v): - _idaapi.switch_info_ex_t_set_defjump(self, v) - def __get_jumps__(self): - return _idaapi.switch_info_ex_t_get_jumps(self) - def __set_jumps__(self, v): - _idaapi.switch_info_ex_t_set_jumps(self, v) - def __get_elbase__(self): - return _idaapi.switch_info_ex_t_get_elbase(self) - def __set_elbase__(self, v): - _idaapi.switch_info_ex_t_set_elbase(self, v) - def __get_startea__(self): - return _idaapi.switch_info_ex_t_get_startea(self) - def __set_startea__(self, v): - _idaapi.switch_info_ex_t_set_startea(self, v) - def __get_custom__(self): - return _idaapi.switch_info_ex_t_get_custom(self) - def __set_custom__(self, v): - _idaapi.switch_info_ex_t_set_custom(self, v) - def __get_ind_lowcase__(self): - return _idaapi.switch_info_ex_t_get_ind_lowcase(self) - def __set_ind_lowcase__(self, v): - _idaapi.switch_info_ex_t_set_ind_lowcase(self, v) - def __get_values_lowcase__(self): - return _idaapi.switch_info_ex_t_get_values_lowcase(self) - def __set_values_lowcase__(self, v): - _idaapi.switch_info_ex_t_set_values_lowcase(self, v) - regdtyp = property(__get_regdtyp__, __set_regdtyp__) - """size of the switch expression register as dtyp""" - flags2 = property(__get_flags2__, __set_flags2__) - jcases = property(__get_jcases__, __set_jcases__) - """number of entries in the jump table (SWI2_INDIRECT)""" - regnum = property(__get_regnum__, __set_regnum__) - """the switch expression as a register number""" - flags = property(__get_flags__, __set_flags__) - """the switch expression as a register number""" - ncases = property(__get_ncases__, __set_ncases__) - """number of cases (excluding default)""" - defjump = property(__get_defjump__, __set_defjump__) - """default jump address""" - jumps = property(__get_jumps__, __set_jumps__) - """jump table address""" - elbase = property(__get_elbase__, __set_elbase__) - """element base""" - startea = property(__get_startea__, __set_startea__) - """start of switch idiom""" - custom = property(__get_custom__, __set_custom__) - """information for custom tables (filled and used by modules)""" - ind_lowcase = property(__get_ind_lowcase__, __set_ind_lowcase__) - values = property(__get_values_lowcase__, __set_values_lowcase__) - lowcase = property(__get_values_lowcase__, __set_values_lowcase__) - -# -%} \ No newline at end of file +%ignore nmSerEA; +%ignore nmSerN; +%ignore maxSerialName; +%ignore get_import_module_name; +%rename (get_import_module_name) py_get_import_module_name; +%ignore NALT_EA; +%ignore enum_import_names; +%rename (enum_import_names) py_enum_import_names; + +%ignore get_wide_value; +%ignore set_wide_value; +%ignore del_wide_value; + +%ignore get_strid; +%ignore _set_strid; +%ignore _del_strid; +%ignore set_strid; +%ignore del_strid; +%ignore xrefpos_t; +%ignore get_xrefpos; +%ignore set_xrefpos; +%ignore del_xrefpos; + +%ignore set_aflags0; +%ignore get_aflags0; +%ignore del_aflags0; + +%ignore get_linnum0; +%ignore set_linnum0; +%ignore del_linnum0; + +%ignore get_enum_id0; +%ignore set_enum_id0; +%ignore del_enum_id0; +%ignore get_enum_id1; +%ignore set_enum_id1; +%ignore del_enum_id1; + +%ignore set_ind_purged; + +%ignore get_str_type; +%ignore set_str_type; +%ignore del_str_type; + +%ignore _get_item_color; +%ignore _set_item_color; +%ignore _del_item_color; + +%ignore get_nalt_cmt; +%ignore set_nalt_cmt; +%ignore del_nalt_cmt; +%ignore get_nalt_rptcmt; +%ignore set_nalt_rptcmt; +%ignore del_nalt_rptcmt; +%ignore get_fop1; +%ignore set_fop1; +%ignore del_fop1; +%ignore get_fop2; +%ignore set_fop2; +%ignore del_fop2; +%ignore get_fop3; +%ignore set_fop3; +%ignore del_fop3; +%ignore get_fop4; +%ignore set_fop4; +%ignore del_fop4; +%ignore get_fop5; +%ignore set_fop5; +%ignore del_fop5; +%ignore get_fop6; +%ignore set_fop6; +%ignore del_fop6; +%ignore get_manual_insn0; +%ignore set_manual_insn0; +%ignore del_manual_insn0; +%ignore get_graph_groups0; +%ignore set_graph_groups0; +%ignore del_graph_groups0; + +%ignore switch_info_t; +%ignore switch_info_ex_t; +%ignore get_switch_info_ex; +%ignore set_switch_info_ex; +%ignore del_switch_info_ex; + +%ignore refinfo_t::_get_target; +%ignore refinfo_t::_get_value; +%ignore refinfo_t::_get_opval; + +%ignore custom_refinfo_handler_t; +%ignore custom_refinfo_handlers_t; +%ignore register_custom_refinfo; +%ignore unregister_custom_refinfo; +%ignore get_custom_refinfos; + +%ignore write_struc_path; +%ignore read_struc_path; +%ignore del_struc_path; +%ignore get_stroff0; +%ignore set_stroff0; +%ignore del_stroff0; +%ignore get_stroff1; +%ignore set_stroff1; +%ignore del_stroff1; + +%ignore get__segtrans; +%ignore set__segtrans; +%ignore del__segtrans; + +%ignore get_switch_info; +%ignore set_switch_info; +%ignore del_switch_info; +%ignore get_ti; +%ignore set_ti; +%ignore del_ti; +%ignore get_op_tinfo; +%ignore set_op_tinfo; +%ignore del_tinfo; +%ignore get_op_ti; +%ignore set_op_ti; +%ignore del_ti; + +%template (ids_array) wrapped_array_t; + +%extend strpath_t { + wrapped_array_t __getIds() { + return wrapped_array_t($self->ids); + } + + %pythoncode { + ids = property(__getIds) + } +} + +%include "nalt.hpp" + +%{ +// + +//------------------------------------------------------------------------- +// callback for enumerating imports +// ea: import address +// name: import name (NULL if imported by ordinal) +// ord: import ordinal (0 for imports by name) +// param: user parameter passed to enum_import_names() +// return: 1-ok, 0-stop enumeration +static int idaapi py_import_enum_cb( + ea_t ea, + const char *name, + uval_t ord, + void *param) +{ + // If no name, try to get the name associated with the 'ea'. It may be coming from IDS + char name_buf[MAXSTR]; + if ( name == NULL ) + name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf)); + + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_name; + if ( name == NULL ) + py_name = borref_t(Py_None); + else + py_name = newref_t(PyString_FromString(name)); + + newref_t py_ord(Py_BuildValue(PY_FMT64, pyul_t(ord))); + newref_t py_ea(Py_BuildValue(PY_FMT64, pyul_t(ea))); + newref_t py_result( + PyObject_CallFunctionObjArgs( + (PyObject *)param, + py_ea.o, + py_name.o, + py_ord.o, + NULL)); + return py_result != NULL && PyObject_IsTrue(py_result.o) ? 1 : 0; +} + +//------------------------------------------------------------------------- +switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) + return NULL; + + switch_info_ex_t *r; + newref_t attr(PyObject_GetAttrString(self, S_CLINK_NAME)); + return PyCObject_Check(attr.o) ? ((switch_info_ex_t *) PyCObject_AsVoidPtr(attr.o)) : NULL; +} +// +%} + +%rename (get_switch_info_ex) py_get_switch_info_ex; +%rename (set_switch_info_ex) py_set_switch_info_ex; +%rename (del_switch_info_ex) py_del_switch_info_ex; +%rename (create_switch_xrefs) py_create_switch_xrefs; +%rename (create_switch_table) py_create_switch_table; +%rename (calc_switch_cases) py_calc_switch_cases; + +%inline %{ +// + +//------------------------------------------------------------------------- +/* +# +def get_import_module_name(path, fname, callback): + """ + Returns the name of an imported module given its index + @return: None or the module name + """ + pass +# +*/ +static PyObject *py_get_import_module_name(int mod_index) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + char buf[MAXSTR]; + if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) + Py_RETURN_NONE; + + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +/* +# +def get_switch_info_ex(ea): + """ + Returns the a switch_info_ex_t structure containing the information about the switch. + Please refer to the SDK sample 'uiswitch' + @return: None or switch_info_ex_t instance + """ + pass +# +*/ +PyObject *py_get_switch_info_ex(ea_t ea) +{ + switch_info_ex_t *ex = new switch_info_ex_t(); + ref_t py_obj; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 + || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) + { + delete ex; + Py_RETURN_NONE; + } + py_obj.incref(); + return py_obj.o; +} + +//------------------------------------------------------------------------- +/* +# +def create_switch_xrefs(insn_ea, si): + """ + This function creates xrefs from the indirect jump. + + Usually there is no need to call this function directly because the kernel + will call it for switch tables + + Note: Custom switch information are not supported yet. + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: Boolean + """ + pass +# +*/ +idaman bool ida_export py_create_switch_xrefs( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + create_switch_xrefs(insn_ea, swi); + return true; +} + +//------------------------------------------------------------------------- +struct cases_and_targets_t +{ + casevec_t cases; + eavec_t targets; +}; + +//------------------------------------------------------------------------- +/* +# +def calc_switch_cases(insn_ea, si): + """ + Get information about a switch's cases. + + The returned information can be used as follows: + + for idx in xrange(len(results.cases)): + cur_case = results.cases[idx] + for cidx in xrange(len(cur_case)): + print "case: %d" % cur_case[cidx] + print " goto 0x%x" % results.targets[idx] + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: a structure with 2 members: 'cases', and 'targets'. + """ + pass +# +*/ +idaman cases_and_targets_t *ida_export py_calc_switch_cases( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return NULL; + + cases_and_targets_t *ct = new cases_and_targets_t; + if ( !calc_switch_cases(insn_ea, swi, &ct->cases, &ct->targets) ) + { + delete ct; + return NULL; + } + + return ct; +} + + +//------------------------------------------------------------------------- +/* +# +def create_switch_table(insn_ea, si): + """ + Create switch table from the switch information + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: Boolean + """ + pass +# +*/ +idaman bool ida_export py_create_switch_table( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + create_switch_table(insn_ea, swi); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def set_switch_info_ex(ea, switch_info_ex): + """ + Saves the switch information in the database + Please refer to the SDK sample 'uiswitch' + @return: Boolean + """ + pass +# +*/ +bool py_set_switch_info_ex(ea_t ea, PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + set_switch_info_ex(ea, swi); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def del_switch_info_ex(ea): + """ + Deletes stored switch information + """ + pass +# +*/ +void py_del_switch_info_ex(ea_t ea) +{ + del_switch_info_ex(ea); +} + +//------------------------------------------------------------------------- +/* +# +def enum_import_names(mod_index, callback): + """ + Enumerate imports from a specific module. + Please refer to ex_imports.py example. + + @param mod_index: The module index + @param callback: A callable object that will be invoked with an ea, name (could be None) and ordinal. + @return: 1-finished ok, -1 on error, otherwise callback return value (<=0) + """ + pass +# +*/ +static int py_enum_import_names(int mod_index, PyObject *py_cb) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCallable_Check(py_cb) ) + return -1; + return enum_import_names(mod_index, py_import_enum_cb, py_cb); +} + +//------------------------------------------------------------------------- +static PyObject *switch_info_ex_t_create() +{ + switch_info_ex_t *inst = new switch_info_ex_t(); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(inst, NULL); +} + +//--------------------------------------------------------------------------- +static bool switch_info_ex_t_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); + delete inst; + return true; +} + +static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) +{ + switch_info_ex_t *lhs = switch_info_ex_t_get_clink(self); + switch_info_ex_t *rhs = switch_info_ex_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +// Auto generated - begin +// + +static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", (char)link->regdtyp); +} +static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->regdtyp = (char)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_flags2(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", link->flags2); +} +static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->flags2 = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_jcases(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", link->jcases); +} +static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->jcases = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_regnum(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", (int)link->regnum); +} +static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->regnum = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_flags(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", (ushort)link->flags); +} +static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->flags = (uint16)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_ncases(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", (uint16)link->ncases); +} +static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + link->ncases = (ushort)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_defjump(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); +} +static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 v(0); PyW_GetNumber(value, &v); + link->defjump = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_jumps(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->jumps); +} +static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 v(0); PyW_GetNumber(value, &v); + link->jumps = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_elbase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); +} +static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->elbase = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_startea(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); +} +static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->startea = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_custom(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); +} +static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->custom = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); +} +static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->ind_lowcase = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->values); +} +static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyW_GetNumber(value, &v); + link->values = (pyul_t)v; +} + +// +// Auto generated - end +// +//------------------------------------------------------------------------- +// +%} + +%pythoncode %{ +# +SWI_SPARSE = 0x1 +"""sparse switch ( value table present ) otherwise lowcase present""" +SWI_V32 = 0x2 +"""32-bit values in table""" +SWI_J32 = 0x4 +"""32-bit jump offsets""" +SWI_VSPLIT = 0x8 +"""value table is split (only for 32-bit values)""" +SWI_DEFAULT = 0x10 +"""default case is present""" +SWI_END_IN_TBL = 0x20 +"""switchend in table (default entry)""" +SWI_JMP_INV = 0x40 +"""jumptable is inversed (last entry is for first entry in values table)""" +SWI_SHIFT_MASK = 0x180 +"""use formula (element*shift + elbase) to find jump targets""" + +SWI_ELBASE = 0x200 +"""elbase is present (if not and shift!=0, endof(jumpea) is used)""" +SWI_JSIZE = 0x400 +"""jump offset expansion bit""" + +SWI_VSIZE = 0x800 +"""value table element size expansion bit""" + +SWI_SEPARATE = 0x1000 +"""do not create an array of individual dwords""" + +SWI_SIGNED = 0x2000 +"""jump table entries are signed""" + +SWI_CUSTOM = 0x4000 +"""custom jump table - ph.create_switch_xrefs will be called to create code xrefs for the table. it must return 2. custom jump table must be created by the module""" + +SWI_EXTENDED = 0x8000 +"""this is switch_info_ex_t""" + +SWI2_INDIRECT = 0x0001 +"""value table elements are used as indexes into the jump table""" +SWI2_SUBTRACT = 0x0002 +"""table values are subtracted from the elbase instead of being addded""" + +# -------------------------------------------------------------------------- +class switch_info_ex_t(py_clinked_object_t): + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + def _create_clink(self): + return _idaapi.switch_info_ex_t_create() + + def _del_clink(self, lnk): + return _idaapi.switch_info_ex_t_destroy(lnk) + + def assign(self, other): + return _idaapi.switch_info_ex_t_assign(self, other) + + def is_indirect(self): + return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_INDIRECT) != 0 + + def is_subtract(self): + return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_SUBTRACT) != 0 + + def get_jtable_size(self): + return self.jcases if self.is_indirect() else self.ncases + + def get_lowcase(self): + return self.ind_lowcase if self.is_indirect() else self.lowcase + + def set_expr(self, r, dt): + self.regnum = r + self.regdtyp = dt + + def get_shift(self): + return (self.flags & SWI_SHIFT_MASK) >> 7 + + def set_shift(self, shift): + self.flags &= ~SWI_SHIFT_MASK + self.flags |= ((shift & 3) << 7) + + def get_jtable_element_size(self): + code = self.flags & (SWI_J32|SWI_JSIZE) + if code == 0: return 2 + elif code == SWI_J32: return 4 + elif code == SWI_JSIZE: return 1 + else: return 8 + + def set_jtable_element_size(self, size): + self.flags &= ~(SWI_J32|SWI_JSIZE) + if size == 4: self.flags |= SWI_J32 + elif size == 1: self.flags |= SWI_JSIZE + elif size == 8: self.flags |= SWI_J32|SWI_JSIZE + elif size != 2: return False + return True + + def get_vtable_element_size(self): + code = self.flags & (SWI_V32|SWI_VSIZE) + if code == 0: return 2 + elif code == SWI_V32: return 4 + elif code == SWI_VSIZE: return 1 + return 8 + + def set_vtable_element_size(self, size): + self.flags &= ~SWI_V32|SWI_VSIZE + if size == 4: self.flags |= SWI_V32 + elif size == 1: self.flags |= SWI_VSIZE + elif size == 8: self.flags |= SWI_V32|SWI_VSIZE + elif size != 2: return False + return True + + # + # Autogenerated + # + def __get_regdtyp__(self): + return _idaapi.switch_info_ex_t_get_regdtyp(self) + def __set_regdtyp__(self, v): + _idaapi.switch_info_ex_t_set_regdtyp(self, v) + def __get_flags2__(self): + return _idaapi.switch_info_ex_t_get_flags2(self) + def __set_flags2__(self, v): + _idaapi.switch_info_ex_t_set_flags2(self, v) + def __get_jcases__(self): + return _idaapi.switch_info_ex_t_get_jcases(self) + def __set_jcases__(self, v): + _idaapi.switch_info_ex_t_set_jcases(self, v) + def __get_regnum__(self): + return _idaapi.switch_info_ex_t_get_regnum(self) + def __set_regnum__(self, v): + _idaapi.switch_info_ex_t_set_regnum(self, v) + def __get_flags__(self): + return _idaapi.switch_info_ex_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.switch_info_ex_t_set_flags(self, v) + def __get_ncases__(self): + return _idaapi.switch_info_ex_t_get_ncases(self) + def __set_ncases__(self, v): + _idaapi.switch_info_ex_t_set_ncases(self, v) + def __get_defjump__(self): + return _idaapi.switch_info_ex_t_get_defjump(self) + def __set_defjump__(self, v): + _idaapi.switch_info_ex_t_set_defjump(self, v) + def __get_jumps__(self): + return _idaapi.switch_info_ex_t_get_jumps(self) + def __set_jumps__(self, v): + _idaapi.switch_info_ex_t_set_jumps(self, v) + def __get_elbase__(self): + return _idaapi.switch_info_ex_t_get_elbase(self) + def __set_elbase__(self, v): + _idaapi.switch_info_ex_t_set_elbase(self, v) + def __get_startea__(self): + return _idaapi.switch_info_ex_t_get_startea(self) + def __set_startea__(self, v): + _idaapi.switch_info_ex_t_set_startea(self, v) + def __get_custom__(self): + return _idaapi.switch_info_ex_t_get_custom(self) + def __set_custom__(self, v): + _idaapi.switch_info_ex_t_set_custom(self, v) + def __get_ind_lowcase__(self): + return _idaapi.switch_info_ex_t_get_ind_lowcase(self) + def __set_ind_lowcase__(self, v): + _idaapi.switch_info_ex_t_set_ind_lowcase(self, v) + def __get_values_lowcase__(self): + return _idaapi.switch_info_ex_t_get_values_lowcase(self) + def __set_values_lowcase__(self, v): + _idaapi.switch_info_ex_t_set_values_lowcase(self, v) + regdtyp = property(__get_regdtyp__, __set_regdtyp__) + """size of the switch expression register as dtyp""" + flags2 = property(__get_flags2__, __set_flags2__) + jcases = property(__get_jcases__, __set_jcases__) + """number of entries in the jump table (SWI2_INDIRECT)""" + regnum = property(__get_regnum__, __set_regnum__) + """the switch expression as a register number""" + flags = property(__get_flags__, __set_flags__) + """the switch expression as a register number""" + ncases = property(__get_ncases__, __set_ncases__) + """number of cases (excluding default)""" + defjump = property(__get_defjump__, __set_defjump__) + """default jump address""" + jumps = property(__get_jumps__, __set_jumps__) + """jump table address""" + elbase = property(__get_elbase__, __set_elbase__) + """element base""" + startea = property(__get_startea__, __set_startea__) + """start of switch idiom""" + custom = property(__get_custom__, __set_custom__) + """information for custom tables (filled and used by modules)""" + ind_lowcase = property(__get_ind_lowcase__, __set_ind_lowcase__) + values = property(__get_values_lowcase__, __set_values_lowcase__) + lowcase = property(__get_values_lowcase__, __set_values_lowcase__) + +# +%} diff --git a/swig/name.i b/swig/name.i index 1dbf34db..d005877c 100644 --- a/swig/name.i +++ b/swig/name.i @@ -1,114 +1,167 @@ -%cstring_output_maxstr_none(char *buf, int bufsize); - -%cstring_bounded_output(char *dstname, MAXSTR); -%cstring_bounded_output(char *buf, MAXSTR); - -// This is for get_name_value's output value -%apply unsigned long *OUTPUT { uval_t *value }; - -// FIXME: These should be fixed -%ignore append_struct_fields; -%ignore get_struct_operand; -%ignore set_debug_names; -%ignore get_debug_name; -%ignore nameVa; - -// Unexported & kernel-only -%ignore get_short_name; -%ignore get_long_name; -%ignore get_colored_short_name; -%ignore get_colored_long_name; -%ignore addDummyName; -%ignore convert_debug_names_to_normal; -%ignore convert_name_formats; -%ignore showhide_name; -%ignore clear_lname_bit; -%ignore fix_new_name; -%ignore rename; -%ignore move_names; -%ignore is_noret_name; -%ignore is_exit_name; -%ignore dummy_name_ea; - -%ignore get_debug_names; -%rename (get_debug_names) py_get_debug_names; -%inline %{ -// -//------------------------------------------------------------------------ -PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) -{ - // Get debug names - ea_name_vec_t names; - PYW_GIL_CHECK_LOCKED_SCOPE(); - Py_BEGIN_ALLOW_THREADS; - get_debug_names(ea1, ea2, names); - Py_END_ALLOW_THREADS; - PyObject *dict = Py_BuildValue("{}"); - if (dict != NULL) - { - for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) - { - PyDict_SetItem(dict, - Py_BuildValue(PY_FMT64, it->ea), - PyString_FromString(it->name.c_str())); - } - } - return dict; -} -//------------------------------------------------------------------------ -// -%} - -%pythoncode %{ -# - -class NearestName: - """ - Utility class to help find the nearest name in a given ea/name dictionary - """ - def __init__(self, ea_names): - self.update(ea_names) - - - def update(self, ea_names): - """Updates the ea/names map""" - self._names = ea_names - self._addrs = ea_names.keys() - self._addrs.sort() - - - def find(self, ea): - """ - Returns a tupple (ea, name, pos) that is the nearest to the passed ea - If no name is matched then None is returned - """ - pos = bisect.bisect_left(self._addrs, ea) - # no match - if pos >= len(self._addrs): - return None - # exact match? - if self._addrs[pos] != ea: - pos -= 1 # go to previous element - if pos < 0: - return None - return self[pos] - - - def _get_item(self, index): - ea = self._addrs[index] - return (ea, self._names[ea], index) - - - def __iter__(self): - return (self._get_item(index) for index in xrange(0, len(self._addrs))) - - - def __getitem__(self, index): - """Returns the tupple (ea, name, index)""" - if index > len(self._addrs): - raise StopIteration - return self._get_item(index) - -# -%} -%include "name.hpp" +%cstring_output_maxstr_none(char *buf, int bufsize); + +%cstring_bounded_output(char *dstname, MAXSTR); +%cstring_bounded_output(char *buf, MAXSTR); + +// This is for get_name_value's output value +%apply unsigned long *OUTPUT { uval_t *value }; + +// FIXME: These should be fixed +%ignore get_struct_operand; +%ignore set_debug_names; +%ignore nameVa; + +// Unexported & kernel-only +%ignore addDummyName; +%ignore convert_debug_names_to_normal; +%ignore convert_name_formats; +%ignore showhide_name; +%ignore clear_lname_bit; +%ignore fix_new_name; +%ignore rename; +%ignore move_names; +%ignore is_noret_name; +%ignore is_exit_name; +%ignore dummy_name_ea; +%ignore calc_gtn_flags; +%ignore detect_compiler_using_demangler; +%ignore getname_info_t; +%ignore get_ea_name; +%rename (get_ea_name) py_get_ea_name; + +%ignore make_visible_name2; + + +// Deprecated functions, w/ duplicate names. +// Some are simply aliased (see py_name.py) +%ignore get_debug_name(ea_t *, debug_name_how_t, char *, size_t); +%ignore append_struct_fields(int, const tid_t *, int, flags_t, char *, char *, adiff_t *, adiff_t, bool); + + +// Duplicate names, in-out qstring w/ existing +// qstring-returning alternatives. +%ignore get_visible_name(qstring *, ea_t, int); +%ignore get_short_name(qstring *, ea_t, int); +%ignore get_long_name(qstring *, ea_t, int); + +// get_true_name & get_colored_name have prototypes such that, +// once converted to IDAPython, would be problematic because it'd +// be impossible for SWiG to tell apart the (ea_t, ea_t) version +// from the (ea_t, int) one. +// Therefore, we cannot allow access to the (ea_t, int) one (and +// we keep the other for bw-compat reasons). If users want to use +// 'flags' versions, they can still rely on get_ea_name(). +%define %restrict_ambiguous_name_function(FNAME) +%ignore FNAME(qstring *, ea_t, int); +%ignore FNAME(ea_t, int); +%rename (FNAME) py_ ## FNAME; + +%inline %{ +inline qstring py_## FNAME(ea_t ea) { return FNAME(ea); } +%} +%enddef + +%restrict_ambiguous_name_function(get_true_name); +%restrict_ambiguous_name_function(get_colored_name); + + +%ignore get_debug_names; +%rename (get_debug_names) py_get_debug_names; + +%{ +// +// +%} + +%include "name.hpp" + +%inline %{ +// +//------------------------------------------------------------------------ +PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) +{ + // Get debug names + ea_name_vec_t names; + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_BEGIN_ALLOW_THREADS; + get_debug_names(ea1, ea2, names); + Py_END_ALLOW_THREADS; + PyObject *dict = Py_BuildValue("{}"); + if (dict != NULL) + { + for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + { + PyDict_SetItem(dict, + Py_BuildValue(PY_FMT64, it->ea), + PyString_FromString(it->name.c_str())); + } + } + return dict; +} + +//------------------------------------------------------------------------- +inline qstring py_get_ea_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, gtn_flags); + return out; +} +//------------------------------------------------------------------------ +// +%} + +%pythoncode %{ +# + +class NearestName: + """ + Utility class to help find the nearest name in a given ea/name dictionary + """ + def __init__(self, ea_names): + self.update(ea_names) + + + def update(self, ea_names): + """Updates the ea/names map""" + self._names = ea_names + self._addrs = ea_names.keys() + self._addrs.sort() + + + def find(self, ea): + """ + Returns a tupple (ea, name, pos) that is the nearest to the passed ea + If no name is matched then None is returned + """ + pos = bisect.bisect_left(self._addrs, ea) + # no match + if pos >= len(self._addrs): + return None + # exact match? + if self._addrs[pos] != ea: + pos -= 1 # go to previous element + if pos < 0: + return None + return self[pos] + + + def _get_item(self, index): + ea = self._addrs[index] + return (ea, self._names[ea], index) + + + def __iter__(self): + return (self._get_item(index) for index in xrange(0, len(self._addrs))) + + + def __getitem__(self, index): + """Returns the tupple (ea, name, index)""" + if index > len(self._addrs): + raise StopIteration + return self._get_item(index) + +extract_name = extract_name2 + +# +%} + diff --git a/swig/netnode.i b/swig/netnode.i index 5646fcdf..d7a99dee 100644 --- a/swig/netnode.i +++ b/swig/netnode.i @@ -14,6 +14,7 @@ %ignore netnode_next; %ignore netnode_prev; %ignore netnode_name; +%ignore netnode_get_name; %ignore netnode_rename; %ignore netnode_valobj; %ignore netnode_valstr; @@ -94,13 +95,13 @@ %rename (hashset_idx) netnode::hashset(const char *idx, nodeidx_t value, char tag=htag); %include "netnode.hpp" - -%extend netnode -{ - nodeidx_t index() - { - return self->operator nodeidx_t(); - } + +%extend netnode +{ + nodeidx_t index() + { + return self->operator nodeidx_t(); + } PyObject *getblob(nodeidx_t start, const char *tag) { diff --git a/swig/pro.i b/swig/pro.i index 9c95d5b9..025260b8 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -1,173 +1,173 @@ -//--------------------------------------------------------------------- -%typemap(out) uint64 { -$result = PyLong_FromUnsignedLongLong((unsigned long long) $1); -} - -//--------------------------------------------------------------------- -%typemap(in) uint64 -{ - uint64 $1_temp; - if ( !PyW_GetNumber($input, &$1_temp) ) - { - PyErr_SetString(PyExc_TypeError, "Expected an uint64 type"); - return NULL; - } - $1 = $1_temp; -} - -//--------------------------------------------------------------------- - -%ignore wchar2char; -%ignore hit_counter_t; -%ignore reg_hit_counter; -%ignore create_hit_counter; -%ignore hit_counter_timer; -%ignore print_all_counters; -%ignore incrementer_t; -%ignore reloc_info_t; // swig under mac chokes on this -%ignore qmutex_create; -%ignore qiterator; -%ignore qmutex_free; -%ignore qmutex_lock; -%ignore qmutex_t; -%ignore qmutex_unlock; -%ignore qsem_create; -%ignore qsem_free; -%ignore qsem_post; -%ignore qsem_wait; -%ignore qsemaphore_t; -%ignore qthread_cb_t; -%ignore qthread_create; -%ignore qthread_free; -%ignore qthread_join; -%ignore qthread_kill; -%ignore qthread_self; -%ignore qthread_same; -%ignore qthread_t; -%ignore qhandle_t; -%ignore qpipe_create; -%ignore qpipe_read; -%ignore qpipe_write; -%ignore qpipe_close; -%ignore qwait_for_handles; -%ignore qstrlen; -%ignore qstrcmp; -%ignore qstrstr; -%ignore qstrchr; -%ignore qstrrchr; -%ignore bytevec_t; -%ignore qstrvec_t; -%ignore reloc_info_t; -%ignore relobj_t; -%ignore wchar2char; -%ignore u2cstr; -%ignore c2ustr; -%ignore base64_encode; -%ignore base64_decode; -%ignore utf8_unicode; -%ignore unicode_utf8; -%ignore win_utf2idb; -%ignore char2oem; -%ignore oem2char; -%ignore set_codepages; -%ignore get_codepages; -%ignore convert_codepage; -%ignore test_bit; -%ignore set_bit; -%ignore clear_bit; -%ignore set_all_bits; -%ignore clear_all_bits; -%ignore interval::overlap; -%ignore interval::includes; -%ignore interval::contains; -%ignore qrotl; -%ignore qrotr; -%ignore setflag; -%ignore read2bytes; -%ignore rotate_left; -//%ignore qswap; -%ignore swap32; -%ignore swap16; -%ignore swap_value; -%ignore qalloc_or_throw; -%ignore qrealloc_or_throw; -%ignore get_buffer_for_sysdir; -%ignore get_buffer_for_winerr; -%ignore call_atexits; -%ignore launch_process_params_t; -%ignore launch_process; -%ignore term_process; -%ignore get_process_exit_code; -%ignore BELOW_NORMAL_PRIORITY_CLASS; -%ignore parse_command_line; -%ignore parse_command_line2; -%ignore parse_command_line3; -%rename (parse_command_line3) py_parse_command_line; -%ignore qgetenv; -%ignore qsetenv; -%ignore qctime; -%ignore qlocaltime; -%ignore qstrftime; -%ignore qstrftime64; -%ignore qstrtok; -%ignore qstrlwr; -%ignore qstrupr; - -void qvector::grow(const unsigned int &x=0); -%ignore qvector::grow; -%ignore qvector::at(size_t); - -// simpleline_t doesn't implement '=='. Therefore, all these cannot be present in the instantiated template. -%ignore qvector::operator==; -%ignore qvector::operator!=; -%ignore qvector::find; -%ignore qvector::has; -%ignore qvector::del; -%ignore qvector::add_unique; - -%include "pro.h" - -//--------------------------------------------------------------------- -%extend qvector { - inline size_t __len__() const { return $self->size(); } - - // The fact that we are returning a const version of a reference to the - // type is what allows SWIG to generate a wrapper for this method, that - // will build an proper object (int, unsigned int, ...) instead - // of a pointer. Remove the 'const', and you'll see that, in - // SWIGINTERN PyObject *_wrap_uvalvec_t___getitem__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { - // it will produce this: - // resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_unsigned_int, 0 | 0 ); - // instead of that: - // resultobj = SWIG_From_unsigned_SS_int(static_cast< unsigned int >(*result)); - inline const T& __getitem__(size_t i) const throw(std::out_of_range) { - if (i >= $self->size() || i < 0) - throw std::out_of_range("out of bounds access"); - return $self->at(i); - } - - inline void __setitem__(size_t i, const T& v) throw(std::out_of_range) { - if (i >= $self->size() || i < 0) - throw std::out_of_range("out of bounds access"); - $self->at(i) = v; - } - - %pythoncode { - __iter__ = _bounded_getitem_iterator - } -} - -//--------------------------------------------------------------------- -%template(uvalvec_t) qvector; // unsigned values -%template(intvec_t) qvector; -%template(boolvec_t) qvector; -%template(casevec_t) qvector >; // signed values -%template(strvec_t) qvector; - -%pythoncode %{ -_listify_types(uvalvec_t, - intvec_t, - boolvec_t, - casevec_t, - strvec_t) -%} +//--------------------------------------------------------------------- +%typemap(out) uint64 { +$result = PyLong_FromUnsignedLongLong((unsigned long long) $1); +} + +//--------------------------------------------------------------------- +%typemap(in) uint64 +{ + uint64 $1_temp; + if ( !PyW_GetNumber($input, &$1_temp) ) + { + PyErr_SetString(PyExc_TypeError, "Expected an uint64 type"); + return NULL; + } + $1 = $1_temp; +} + +//--------------------------------------------------------------------- + +%ignore wchar2char; +%ignore hit_counter_t; +%ignore reg_hit_counter; +%ignore create_hit_counter; +%ignore hit_counter_timer; +%ignore print_all_counters; +%ignore incrementer_t; +%ignore reloc_info_t; // swig under mac chokes on this +%ignore qmutex_create; +%ignore qiterator; +%ignore qmutex_free; +%ignore qmutex_lock; +%ignore qmutex_t; +%ignore qmutex_unlock; +%ignore qsem_create; +%ignore qsem_free; +%ignore qsem_post; +%ignore qsem_wait; +%ignore qsemaphore_t; +%ignore qthread_cb_t; +%ignore qthread_create; +%ignore qthread_free; +%ignore qthread_join; +%ignore qthread_kill; +%ignore qthread_self; +%ignore qthread_same; +%ignore qthread_t; +%ignore qhandle_t; +%ignore qpipe_create; +%ignore qpipe_read; +%ignore qpipe_write; +%ignore qpipe_close; +%ignore qwait_for_handles; +%ignore qstrlen; +%ignore qstrcmp; +%ignore qstrstr; +%ignore qstrchr; +%ignore qstrrchr; +%ignore bytevec_t; +%ignore qstrvec_t; +%ignore reloc_info_t; +%ignore relobj_t; +%ignore wchar2char; +%ignore u2cstr; +%ignore c2ustr; +%ignore base64_encode; +%ignore base64_decode; +%ignore utf8_unicode; +%ignore unicode_utf8; +%ignore win_utf2idb; +%ignore char2oem; +%ignore oem2char; +%ignore set_codepages; +%ignore get_codepages; +%ignore convert_codepage; +%ignore test_bit; +%ignore set_bit; +%ignore clear_bit; +%ignore set_all_bits; +%ignore clear_all_bits; +%ignore interval::overlap; +%ignore interval::includes; +%ignore interval::contains; +%ignore qrotl; +%ignore qrotr; +%ignore setflag; +%ignore read2bytes; +%ignore rotate_left; +//%ignore qswap; +%ignore swap32; +%ignore swap16; +%ignore swap_value; +%ignore qalloc_or_throw; +%ignore qrealloc_or_throw; +%ignore get_buffer_for_sysdir; +%ignore get_buffer_for_winerr; +%ignore call_atexits; +%ignore launch_process_params_t; +%ignore launch_process; +%ignore term_process; +%ignore get_process_exit_code; +%ignore BELOW_NORMAL_PRIORITY_CLASS; +%ignore parse_command_line; +%ignore parse_command_line2; +%ignore parse_command_line3; +%rename (parse_command_line3) py_parse_command_line; +%ignore qgetenv; +%ignore qsetenv; +%ignore qctime; +%ignore qlocaltime; +%ignore qstrftime; +%ignore qstrftime64; +%ignore qstrtok; +%ignore qstrlwr; +%ignore qstrupr; + +void qvector::grow(const unsigned int &x=0); +%ignore qvector::grow; +%ignore qvector::at(size_t); + +// simpleline_t doesn't implement '=='. Therefore, all these cannot be present in the instantiated template. +%ignore qvector::operator==; +%ignore qvector::operator!=; +%ignore qvector::find; +%ignore qvector::has; +%ignore qvector::del; +%ignore qvector::add_unique; + +%include "pro.h" + +//--------------------------------------------------------------------- +%extend qvector { + inline size_t __len__() const { return $self->size(); } + + // The fact that we are returning a const version of a reference to the + // type is what allows SWIG to generate a wrapper for this method, that + // will build an proper object (int, unsigned int, ...) instead + // of a pointer. Remove the 'const', and you'll see that, in + // SWIGINTERN PyObject *_wrap_uvalvec_t___getitem__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + // it will produce this: + // resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_unsigned_int, 0 | 0 ); + // instead of that: + // resultobj = SWIG_From_unsigned_SS_int(static_cast< unsigned int >(*result)); + inline const T& __getitem__(size_t i) const throw(std::out_of_range) { + if (i >= $self->size() || i < 0) + throw std::out_of_range("out of bounds access"); + return $self->at(i); + } + + inline void __setitem__(size_t i, const T& v) throw(std::out_of_range) { + if (i >= $self->size() || i < 0) + throw std::out_of_range("out of bounds access"); + $self->at(i) = v; + } + + %pythoncode { + __iter__ = _bounded_getitem_iterator + } +} + +//--------------------------------------------------------------------- +%template(uvalvec_t) qvector; // unsigned values +%template(intvec_t) qvector; +%template(boolvec_t) qvector; +%template(casevec_t) qvector >; // signed values +%template(strvec_t) qvector; + +%pythoncode %{ +_listify_types(uvalvec_t, + intvec_t, + boolvec_t, + casevec_t, + strvec_t) +%} diff --git a/swig/queue.i b/swig/queue.i index 62a97c91..8e401c9b 100644 --- a/swig/queue.i +++ b/swig/queue.i @@ -14,4 +14,4 @@ %ignore mark_ida_decision; %ignore unmark_ida_decision; -%include "queue.hpp" \ No newline at end of file +%include "queue.hpp" diff --git a/swig/registry.i b/swig/registry.i index 0553d2aa..e71bf93f 100644 --- a/swig/registry.i +++ b/swig/registry.i @@ -1,145 +1,145 @@ - -%ignore reg_bin_op; -%ignore reg_str_op; -%ignore reg_int_op; -%ignore _RVN_; -%ignore REG_VAL_NAME; -%ignore REG_BOOL_FUNC; -%ignore REG_INT_FUNC; -%ignore MAX_HISTORY_FILES_DEF; -%ignore regkey_history; -%ignore max_history_files; -%ignore regget_history; -%ignore reg_update_history; -%ignore reg_history_size_truncate; - -%ignore reg_read_string; -%rename (reg_read_string) py_reg_read_string; - -%ignore reg_data_type; -%rename (reg_data_type) py_reg_data_type; - -%ignore reg_read_binary; -%rename (reg_read_binary) py_reg_read_binary; -%ignore reg_write_binary; -%rename (reg_write_binary) py_reg_write_binary; - -%ignore reg_read_binary_part; - -/* inline bool reg_subkey_subkeys(qstrvec_t *out, const char *name) */ -%ignore reg_subkey_subkeys; -%rename (reg_subkey_subkeys) py_reg_subkey_subkeys; -%ignore reg_subkey_values; -%rename (reg_subkey_values) py_reg_subkey_values; -%ignore reg_subkey_children; - -%{ -// -//------------------------------------------------------------------------- -static PyObject *_py_reg_subkey_children(const char *name, bool subkeys) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - PyObject *result = NULL; - qstrvec_t children; - Py_BEGIN_ALLOW_THREADS; - if ( reg_subkey_children(&children, name, subkeys) ) - { - result = PyList_New(children.size()); - if ( result != NULL ) - for ( size_t i = 0, n = children.size(); i < n; ++i ) - PyList_SET_ITEM(result, i, PyString_FromString(children[i].c_str())); - } - Py_END_ALLOW_THREADS; - if ( result == NULL ) - Py_RETURN_NONE; - else - return result; -} -// -%} - -%inline %{ -// -//------------------------------------------------------------------------- -PyObject *py_reg_read_string(const char *name, const char *subkey = NULL, const char *def = NULL) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - char utf8[MAXSTR * 10]; - bool ok; - Py_BEGIN_ALLOW_THREADS; - if ( def == NULL ) - { - ok = reg_read_string(name, utf8, sizeof(utf8), subkey); - } - else - { - reg_read_string(name, sizeof(utf8), utf8, def, subkey); - ok = true; - } - Py_END_ALLOW_THREADS; - return PyString_FromString(ok ? utf8 : ""); -} - -//------------------------------------------------------------------------- -regval_type_t py_reg_data_type(const char *name, const char *subkey = NULL) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - regval_type_t rt = reg_unknown; - Py_BEGIN_ALLOW_THREADS; - reg_data_type(&rt, name, subkey); - Py_END_ALLOW_THREADS; - return rt; -} - -//------------------------------------------------------------------------- -PyObject *py_reg_read_binary(const char *name, const char *subkey = NULL) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - bytevec_t bytes; - bool ok; - Py_BEGIN_ALLOW_THREADS; - ok = reg_read_binary(name, &bytes, subkey); - Py_END_ALLOW_THREADS; - if ( ok ) - return PyString_FromStringAndSize((const char *) bytes.begin(), bytes.size()); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -void py_reg_write_binary(const char *name, PyObject *py_bytes, const char *subkey = NULL) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyString_Check(py_bytes) ) - { - char *py_bytes_raw = NULL; - Py_ssize_t py_size = 0; - PyString_AsStringAndSize(py_bytes, &py_bytes_raw, &py_size); - bytevec_t bytes; - bytes.append(py_bytes_raw, py_size); - Py_BEGIN_ALLOW_THREADS; - reg_write_binary(name, bytes.begin(), bytes.size(), subkey); - Py_END_ALLOW_THREADS; - } - else - { - PyErr_SetString(PyExc_ValueError, "Bytes string expected!"); - } -} - -//------------------------------------------------------------------------- -PyObject *py_reg_subkey_subkeys(const char *name) -{ - return _py_reg_subkey_children(name, true); -} - -//------------------------------------------------------------------------- -PyObject *py_reg_subkey_values(const char *name) -{ - return _py_reg_subkey_children(name, false); -} - -// -%} - -%include "registry.hpp" + +%ignore reg_bin_op; +%ignore reg_str_op; +%ignore reg_int_op; +%ignore _RVN_; +%ignore REG_VAL_NAME; +%ignore REG_BOOL_FUNC; +%ignore REG_INT_FUNC; +%ignore MAX_HISTORY_FILES_DEF; +%ignore regkey_history; +%ignore max_history_files; +%ignore regget_history; +%ignore reg_update_history; +%ignore reg_history_size_truncate; + +%ignore reg_read_string; +%rename (reg_read_string) py_reg_read_string; + +%ignore reg_data_type; +%rename (reg_data_type) py_reg_data_type; + +%ignore reg_read_binary; +%rename (reg_read_binary) py_reg_read_binary; +%ignore reg_write_binary; +%rename (reg_write_binary) py_reg_write_binary; + +%ignore reg_read_binary_part; + +/* inline bool reg_subkey_subkeys(qstrvec_t *out, const char *name) */ +%ignore reg_subkey_subkeys; +%rename (reg_subkey_subkeys) py_reg_subkey_subkeys; +%ignore reg_subkey_values; +%rename (reg_subkey_values) py_reg_subkey_values; +%ignore reg_subkey_children; + +%{ +// +//------------------------------------------------------------------------- +static PyObject *_py_reg_subkey_children(const char *name, bool subkeys) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *result = NULL; + qstrvec_t children; + Py_BEGIN_ALLOW_THREADS; + if ( reg_subkey_children(&children, name, subkeys) ) + { + result = PyList_New(children.size()); + if ( result != NULL ) + for ( size_t i = 0, n = children.size(); i < n; ++i ) + PyList_SET_ITEM(result, i, PyString_FromString(children[i].c_str())); + } + Py_END_ALLOW_THREADS; + if ( result == NULL ) + Py_RETURN_NONE; + else + return result; +} +// +%} + +%inline %{ +// +//------------------------------------------------------------------------- +PyObject *py_reg_read_string(const char *name, const char *subkey = NULL, const char *def = NULL) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + char utf8[MAXSTR * 10]; + bool ok; + Py_BEGIN_ALLOW_THREADS; + if ( def == NULL ) + { + ok = reg_read_string(name, utf8, sizeof(utf8), subkey); + } + else + { + reg_read_string(name, sizeof(utf8), utf8, def, subkey); + ok = true; + } + Py_END_ALLOW_THREADS; + return PyString_FromString(ok ? utf8 : ""); +} + +//------------------------------------------------------------------------- +regval_type_t py_reg_data_type(const char *name, const char *subkey = NULL) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + regval_type_t rt = reg_unknown; + Py_BEGIN_ALLOW_THREADS; + reg_data_type(&rt, name, subkey); + Py_END_ALLOW_THREADS; + return rt; +} + +//------------------------------------------------------------------------- +PyObject *py_reg_read_binary(const char *name, const char *subkey = NULL) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + bytevec_t bytes; + bool ok; + Py_BEGIN_ALLOW_THREADS; + ok = reg_read_binary(name, &bytes, subkey); + Py_END_ALLOW_THREADS; + if ( ok ) + return PyString_FromStringAndSize((const char *) bytes.begin(), bytes.size()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void py_reg_write_binary(const char *name, PyObject *py_bytes, const char *subkey = NULL) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(py_bytes) ) + { + char *py_bytes_raw = NULL; + Py_ssize_t py_size = 0; + PyString_AsStringAndSize(py_bytes, &py_bytes_raw, &py_size); + bytevec_t bytes; + bytes.append(py_bytes_raw, py_size); + Py_BEGIN_ALLOW_THREADS; + reg_write_binary(name, bytes.begin(), bytes.size(), subkey); + Py_END_ALLOW_THREADS; + } + else + { + PyErr_SetString(PyExc_ValueError, "Bytes string expected!"); + } +} + +//------------------------------------------------------------------------- +PyObject *py_reg_subkey_subkeys(const char *name) +{ + return _py_reg_subkey_children(name, true); +} + +//------------------------------------------------------------------------- +PyObject *py_reg_subkey_values(const char *name) +{ + return _py_reg_subkey_children(name, false); +} + +// +%} + +%include "registry.hpp" diff --git a/swig/struct.i b/swig/struct.i index b97c40cf..cd790c25 100644 --- a/swig/struct.i +++ b/swig/struct.i @@ -2,6 +2,8 @@ %ignore init_structs; %ignore save_structs; %ignore term_structs; +%ignore get_struc_name(tid_t); +%ignore get_member_name2(tid_t); %ignore sync_from_struc; diff --git a/swig/typeconv.i b/swig/typeconv.i index 45b76a44..679578f8 100644 --- a/swig/typeconv.i +++ b/swig/typeconv.i @@ -1,338 +1,339 @@ -//------------------------------------------------------------------------- -// For some reason, SWIG converts char arrays by computing the size -// from the end of the array, and stops when it encounters a '\0'. -// That doesn't work for us, as our API doesn't guarantee that -// bytes past the length we are interested in will be zeroed-out. -// In other words, the following code should *never* be present -// in idaapi_include.cpp: -// ------------------------- -// while (size && ([size - 1] == '\0')) --size; -// ------------------------- -// -%typemap(out) char [ANY], const char[ANY] -{ - %set_output(SWIG_FromCharPtrAndSize($1, strnlen($1, $1_dim0))); -} - -%typemap(varout) char [ANY], const char[ANY] -{ - %set_output(SWIG_FromCharPtrAndSize($1, strnlen($1, $1_dim0))); -} - - -%typemap(out) ssize_t -{ - $result = PyLong_FromLongLong($1); -} - -//--------------------------------------------------------------------- -// Convert an incoming Python list to a tid_t[] array -%typemap(in) tid_t[ANY](tid_t temp[$1_dim0]) { - int i, len; - - if (!PySequence_Check($input)) - { - PyErr_SetString(PyExc_TypeError,"Expecting a sequence"); - return NULL; - } - - /* Cap the number of elements to copy */ - len = PySequence_Length($input) < $1_dim0 ? PySequence_Length($input) : $1_dim0; - - for (i =0; i < len; i++) - { - PyObject *o = PySequence_GetItem($input,i); - if (!PyLong_Check(o)) - { - Py_XDECREF(o); - PyErr_SetString(PyExc_ValueError,"Expecting a sequence of long integers"); - return NULL; - } - - temp[i] = PyLong_AsUnsignedLong(o); - Py_DECREF(o); - } - $1 = &temp[0]; -} - -//--------------------------------------------------------------------- -%define %cstring_output_maxstr_none(TYPEMAP, SIZE) - -%typemap (default) SIZE { - $1 = MAXSTR; - } - -%typemap(in,numinputs=0) (TYPEMAP, SIZE) { - $1 = ($1_ltype) qalloc(MAXSTR+1); -} - -%typemap(argout) (TYPEMAP,SIZE) { - Py_XDECREF(resultobj); - if (result > 0) - { - resultobj = PyString_FromString($1); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } - qfree($1); -} -%enddef - -//--------------------------------------------------------------------- -%define %cstring_bounded_output_none(TYPEMAP,MAX) -%typemap(in, numinputs=0) TYPEMAP(char temp[MAX+1]) { - $1 = ($1_ltype) temp; -} -%typemap(argout,fragment="t_output_helper") TYPEMAP { - PyObject *o; - $1[MAX] = 0; - - if ($1 > 0) - { - o = PyString_FromString($1); - } - else - { - o = Py_None; - Py_INCREF(Py_None); - } - $result = t_output_helper($result,o); -} -%enddef - -//--------------------------------------------------------------------- -%define %binary_output_or_none(TYPEMAP, SIZE) -%typemap (default) SIZE { - $1 = MAXSPECSIZE; -} -%typemap(in,numinputs=0) (TYPEMAP, SIZE) { - $1 = (char *) qalloc(MAXSPECSIZE+1); -} -%typemap(argout) (TYPEMAP,SIZE) { - Py_XDECREF(resultobj); - if (result > 0) - { - resultobj = PyString_FromStringAndSize((char *)$1, result); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } - qfree((void *)$1); -} -%enddef - -//--------------------------------------------------------------------- -%define %binary_output_with_size(TYPEMAP, SIZE) -%typemap (default) SIZE { - size_t ressize = MAXSPECSIZE; - $1 = &ressize; -} -%typemap(in,numinputs=0) (TYPEMAP, SIZE) { - $1 = (char *) qalloc(MAXSPECSIZE+1); -} -%typemap(argout) (TYPEMAP,SIZE) { - Py_XDECREF(resultobj); - if (result) - { - resultobj = PyString_FromStringAndSize((char *)$1, *$2); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } - qfree((void *)$1); -} -%enddef - -//--------------------------------------------------------------------- -// IN/OUT qstring -//--------------------------------------------------------------------- -%typemap(in,numinputs=0) qstring *result (qstring temp) { - $1 = &temp; -} -%typemap(argout) qstring *result { - Py_XDECREF(resultobj); - if (result) - { - resultobj = PyString_FromStringAndSize($1->begin(), $1->length()); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } -} -%typemap(freearg) qstring* result -{ - // Nothing. We certainly don't want 'temp' to be deleted. -} - -//--------------------------------------------------------------------- -// Check that the argument is a callable Python object -//--------------------------------------------------------------------- -%typemap(in) PyObject *pyfunc { - if (!PyCallable_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Expected a callable object"); - return NULL; - } - $1 = $input; -} -%typemap(in) ea_t -{ - uint64 $1_temp; - if ( !PyW_GetNumber($input, &$1_temp) ) - { - PyErr_SetString(PyExc_TypeError, "Expected an ea_t type"); - return NULL; - } - $1 = ea_t($1_temp); -} -// Use PyLong_FromUnsignedLongLong, because 'long' is 4 bytes on -// windows, and thus the ea_t would be truncated at the -// PyLong_FromUnsignedLong(unsigned int) call time. -%typemap(out) ea_t "$result = PyLong_FromUnsignedLongLong($1);" - -//--------------------------------------------------------------------- -// IN qstring -//--------------------------------------------------------------------- -// This is used to set/retrieve qstring that are structure members. -%typemap(in) qstring* -{ - char *buf; - Py_ssize_t length; - int success = PyString_AsStringAndSize($input, &buf, &length); - if ( success > -1 ) - { - $1 = new qstring(buf, length); - } -} -%typemap(freearg) qstring* -{ - delete $1; -} -%typemap(out) qstring* -{ - $result = PyString_FromStringAndSize($1->c_str(), $1->length()); -} -%typemap(out) qstring -{ - $result = PyString_FromStringAndSize($1.c_str(), $1.length()); -} -%apply qstring { _qstring } -%apply qstring* { _qstring* } - -//--------------------------------------------------------------------- -// varargs (mostly kernwin.hpp) -//--------------------------------------------------------------------- -// This is used for functions like warning(), info() and so on -%typemap(in) (const char *format, ...) -{ - $1 = "%s"; /* Fix format string to %s */ - $2 = (void *) PyString_AsString($input); /* Get string argument */ -}; - -#ifdef __EA64__ -%apply longlong *INOUT { sval_t *value }; -%apply ulonglong *INOUT { ea_t *addr }; -%apply ulonglong *INOUT { sel_t *sel }; -%apply ulonglong *OUTPUT { ea_t *ea1, ea_t *ea2 }; // read_selection() -#else -%apply int *INOUT { sval_t *value }; -%apply unsigned int *INOUT { ea_t *addr }; -%apply unsigned int *INOUT { sel_t *sel }; -%apply unsigned int *OUTPUT { ea_t *ea1, ea_t *ea2 }; // read_selection() -#endif - -%apply qstring *result { qstring *label }; -%apply qstring *result { qstring *shortcut }; -%apply qstring *result { qstring *tooltip }; -%apply int *OUTPUT { int *icon }; -%apply int *OUTPUT { action_state_t *state }; -%apply bool *OUTPUT { bool *checkable }; -%apply bool *OUTPUT { bool *checked }; -%apply bool *OUTPUT { bool *visibility }; - -//------------------------------------------------------------------------- -// The following is to be used to expose an array of items -// to IDAPython. This will not make a copy (on purpose!). -//------------------------------------------------------------------------- -// -// (Very) heavily inspired by: -// http://stackoverflow.com/questions/7713318/nested-structure-array-access-in-python-using-swig?rq=1 -// -%immutable; -%inline %{ -template -struct wrapped_array_t { - Type (&data)[N]; - wrapped_array_t(Type (&data)[N]) : data(data) { } -}; -%} -%mutable; - -%extend wrapped_array_t { - inline size_t __len__() const { return N; } - - inline const Type& __getitem__(size_t i) const throw(std::out_of_range) { - if (i >= N || i < 0) - throw std::out_of_range("out of bounds access"); - return $self->data[i]; - } - - inline void __setitem__(size_t i, const Type& v) throw(std::out_of_range) { - if (i >= N || i < 0) - throw std::out_of_range("out of bounds access"); - $self->data[i] = v; - } - - %pythoncode { - __iter__ = _bounded_getitem_iterator - } -} - -//------------------------------------------------------------------------- -#if SWIG_VERSION == 0x20012 -%typemap(out) tinfo_t {} -%typemap(ret) tinfo_t -{ - // ret tinfo_t - tinfo_t *ni = new tinfo_t($1); - til_register_python_tinfo_t_instance(ni); - $result = SWIG_NewPointerObj(ni, $&1_descriptor, SWIG_POINTER_OWN | 0); -} - - -// KLUDGE: We'll let the compiler (or at worse the runtime) -// decide of the flags to use, depending on the method we are currently -// wrapping: at new-time, a SWIG_POINTER_NEW is required. -%typemap(out) tinfo_t* {} -%typemap(ret) tinfo_t* -{ - // ret tinfo_t* - tinfo_t *ni = new tinfo_t(*($1)); - til_register_python_tinfo_t_instance(ni); - if ( strcmp("new_tinfo_t", "$symname") == 0 ) - { - $result = SWIG_NewPointerObj(SWIG_as_voidptr(ni), $1_descriptor, SWIG_POINTER_NEW | 0); - delete $1; - } - else - { - $result = SWIG_NewPointerObj(SWIG_as_voidptr(ni), $1_descriptor, SWIG_POINTER_OWN | 0); - } -} - -%typemap(check) tinfo_t* -{ - if ( $1 == NULL ) - SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "$symname" "', argument " "$argnum"" of type '" "$1_type""'"); -} -#else -#error Ensure tinfo_t wrapping is compatible with this version of SWIG -#endif +//------------------------------------------------------------------------- +// For some reason, SWIG converts char arrays by computing the size +// from the end of the array, and stops when it encounters a '\0'. +// That doesn't work for us, as our API doesn't guarantee that +// bytes past the length we are interested in will be zeroed-out. +// In other words, the following code should *never* be present +// in idaapi_include.cpp: +// ------------------------- +// while (size && ([size - 1] == '\0')) --size; +// ------------------------- +// +%typemap(out) char [ANY], const char[ANY] +{ + %set_output(SWIG_FromCharPtrAndSize($1, strnlen($1, $1_dim0))); +} + +%typemap(varout) char [ANY], const char[ANY] +{ + %set_output(SWIG_FromCharPtrAndSize($1, strnlen($1, $1_dim0))); +} + + +%typemap(out) ssize_t +{ + $result = PyLong_FromLongLong($1); +} + +//--------------------------------------------------------------------- +// Convert an incoming Python list to a tid_t[] array +%typemap(in) tid_t[ANY](tid_t temp[$1_dim0]) { + int i, len; + + if (!PySequence_Check($input)) + { + PyErr_SetString(PyExc_TypeError,"Expecting a sequence"); + return NULL; + } + + /* Cap the number of elements to copy */ + len = PySequence_Length($input) < $1_dim0 ? PySequence_Length($input) : $1_dim0; + + for (i =0; i < len; i++) + { + PyObject *o = PySequence_GetItem($input,i); + if (!PyLong_Check(o)) + { + Py_XDECREF(o); + PyErr_SetString(PyExc_ValueError,"Expecting a sequence of long integers"); + return NULL; + } + + temp[i] = PyLong_AsUnsignedLong(o); + Py_DECREF(o); + } + $1 = &temp[0]; +} + +//--------------------------------------------------------------------- +%define %cstring_output_maxstr_none(TYPEMAP, SIZE) + +%typemap (default) SIZE { + $1 = MAXSTR; + } + +%typemap(in,numinputs=0) (TYPEMAP, SIZE) { + $1 = ($1_ltype) qalloc(MAXSTR+1); +} + +%typemap(argout) (TYPEMAP,SIZE) { + Py_XDECREF(resultobj); + if (result > 0) + { + resultobj = PyString_FromString($1); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree($1); +} +%enddef + +//--------------------------------------------------------------------- +%define %cstring_bounded_output_none(TYPEMAP,MAX) +%typemap(in, numinputs=0) TYPEMAP(char temp[MAX+1]) { + $1 = ($1_ltype) temp; +} +%typemap(argout,fragment="t_output_helper") TYPEMAP { + PyObject *o; + $1[MAX] = 0; + + if ($1 > 0) + { + o = PyString_FromString($1); + } + else + { + o = Py_None; + Py_INCREF(Py_None); + } + $result = t_output_helper($result,o); +} +%enddef + +//--------------------------------------------------------------------- +%define %binary_output_or_none(TYPEMAP, SIZE) +%typemap (default) SIZE { + $1 = MAXSPECSIZE; +} +%typemap(in,numinputs=0) (TYPEMAP, SIZE) { + $1 = (char *) qalloc(MAXSPECSIZE+1); +} +%typemap(argout) (TYPEMAP,SIZE) { + Py_XDECREF(resultobj); + if (result > 0) + { + resultobj = PyString_FromStringAndSize((char *)$1, result); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree((void *)$1); +} +%enddef + +//--------------------------------------------------------------------- +%define %binary_output_with_size(TYPEMAP, SIZE) +%typemap (default) SIZE { + size_t ressize = MAXSPECSIZE; + $1 = &ressize; +} +%typemap(in,numinputs=0) (TYPEMAP, SIZE) { + $1 = (char *) qalloc(MAXSPECSIZE+1); +} +%typemap(argout) (TYPEMAP,SIZE) { + Py_XDECREF(resultobj); + if (result) + { + resultobj = PyString_FromStringAndSize((char *)$1, *$2); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree((void *)$1); +} +%enddef + +//--------------------------------------------------------------------- +// IN/OUT qstring +//--------------------------------------------------------------------- +%typemap(in,numinputs=0) qstring *result (qstring temp) { + $1 = &temp; +} +%typemap(argout) qstring *result { + Py_XDECREF(resultobj); + if (result) + { + resultobj = PyString_FromStringAndSize($1->begin(), $1->length()); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } +} +%typemap(freearg) qstring* result +{ + // Nothing. We certainly don't want 'temp' to be deleted. +} + +//--------------------------------------------------------------------- +// Check that the argument is a callable Python object +//--------------------------------------------------------------------- +%typemap(in) PyObject *pyfunc { + if (!PyCallable_Check($input)) { + PyErr_SetString(PyExc_TypeError, "Expected a callable object"); + return NULL; + } + $1 = $input; +} +%typemap(in) ea_t +{ + uint64 $1_temp; + if ( !PyW_GetNumber($input, &$1_temp) ) + { + PyErr_SetString(PyExc_TypeError, "Expected an ea_t type"); + return NULL; + } + $1 = ea_t($1_temp); +} +// Use PyLong_FromUnsignedLongLong, because 'long' is 4 bytes on +// windows, and thus the ea_t would be truncated at the +// PyLong_FromUnsignedLong(unsigned int) call time. +%typemap(out) ea_t "$result = PyLong_FromUnsignedLongLong($1);" + +//--------------------------------------------------------------------- +// IN qstring +//--------------------------------------------------------------------- +// This is used to set/retrieve qstring that are structure members. +%typemap(in) qstring* +{ + char *buf; + Py_ssize_t length; + int success = PyString_AsStringAndSize($input, &buf, &length); + if ( success > -1 ) + { + $1 = new qstring(buf, length); + } +} +%typemap(freearg) qstring* +{ + delete $1; +} +%typemap(out) qstring* +{ + $result = PyString_FromStringAndSize($1->c_str(), $1->length()); +} +%typemap(out) qstring +{ + $result = PyString_FromStringAndSize($1.c_str(), $1.length()); +} +%apply qstring { _qstring } +%apply qstring* { _qstring* } + +//--------------------------------------------------------------------- +// varargs (mostly kernwin.hpp) +//--------------------------------------------------------------------- +// This is used for functions like warning(), info() and so on +%typemap(in) (const char *format, ...) +{ + $1 = "%s"; /* Fix format string to %s */ + $2 = (void *) PyString_AsString($input); /* Get string argument */ +}; + +#ifdef __EA64__ +%apply longlong *INOUT { sval_t *value }; +%apply ulonglong *INOUT { ea_t *addr }; +%apply ulonglong *INOUT { sel_t *sel }; +%apply ulonglong *OUTPUT { ea_t *ea1, ea_t *ea2 }; // read_selection() +#else +%apply int *INOUT { sval_t *value }; +%apply unsigned int *INOUT { ea_t *addr }; +%apply unsigned int *INOUT { sel_t *sel }; +%apply unsigned int *OUTPUT { ea_t *ea1, ea_t *ea2 }; // read_selection() +#endif + +%apply qstring *result { qstring *label }; +%apply qstring *result { qstring *shortcut }; +%apply qstring *result { qstring *tooltip }; +%apply qstring *result { qstring *out }; +%apply int *OUTPUT { int *icon }; +%apply int *OUTPUT { action_state_t *state }; +%apply bool *OUTPUT { bool *checkable }; +%apply bool *OUTPUT { bool *checked }; +%apply bool *OUTPUT { bool *visibility }; + +//------------------------------------------------------------------------- +// The following is to be used to expose an array of items +// to IDAPython. This will not make a copy (on purpose!). +//------------------------------------------------------------------------- +// +// (Very) heavily inspired by: +// http://stackoverflow.com/questions/7713318/nested-structure-array-access-in-python-using-swig?rq=1 +// +%immutable; +%inline %{ +template +struct wrapped_array_t { + Type (&data)[N]; + wrapped_array_t(Type (&data)[N]) : data(data) { } +}; +%} +%mutable; + +%extend wrapped_array_t { + inline size_t __len__() const { return N; } + + inline const Type& __getitem__(size_t i) const throw(std::out_of_range) { + if (i >= N || i < 0) + throw std::out_of_range("out of bounds access"); + return $self->data[i]; + } + + inline void __setitem__(size_t i, const Type& v) throw(std::out_of_range) { + if (i >= N || i < 0) + throw std::out_of_range("out of bounds access"); + $self->data[i] = v; + } + + %pythoncode { + __iter__ = _bounded_getitem_iterator + } +} + +//------------------------------------------------------------------------- +#if SWIG_VERSION == 0x20012 +%typemap(out) tinfo_t {} +%typemap(ret) tinfo_t +{ + // ret tinfo_t + tinfo_t *ni = new tinfo_t($1); + til_register_python_tinfo_t_instance(ni); + $result = SWIG_NewPointerObj(ni, $&1_descriptor, SWIG_POINTER_OWN | 0); +} + + +// KLUDGE: We'll let the compiler (or at worse the runtime) +// decide of the flags to use, depending on the method we are currently +// wrapping: at new-time, a SWIG_POINTER_NEW is required. +%typemap(out) tinfo_t* {} +%typemap(ret) tinfo_t* +{ + // ret tinfo_t* + tinfo_t *ni = new tinfo_t(*($1)); + til_register_python_tinfo_t_instance(ni); + if ( strcmp("new_tinfo_t", "$symname") == 0 ) + { + $result = SWIG_NewPointerObj(SWIG_as_voidptr(ni), $1_descriptor, SWIG_POINTER_NEW | 0); + delete $1; + } + else + { + $result = SWIG_NewPointerObj(SWIG_as_voidptr(ni), $1_descriptor, SWIG_POINTER_OWN | 0); + } +} + +%typemap(check) tinfo_t* +{ + if ( $1 == NULL ) + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "$symname" "', argument " "$argnum"" of type '" "$1_type""'"); +} +#else +#error Ensure tinfo_t wrapping is compatible with this version of SWIG +#endif diff --git a/swig/typeinf.i b/swig/typeinf.i index 259b0608..d13ce872 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -1,923 +1,1047 @@ -// Most of these could be wrapped if needed -%ignore get_cc; -%ignore get_cc_type_size; -%ignore set_argloc; -%ignore set_dt; -%ignore set_da; -%ignore set_de; -%ignore get_dt; -%ignore get_da; -%ignore get_de; -%ignore skip_ptr_type_header; -%ignore skip_array_type_header; -%ignore unpack_object_from_idb; -%ignore unpack_object_from_bv; -%ignore pack_object_to_idb; -%ignore pack_object_to_bv; -%ignore typend; -%ignore typlen; -%ignore typncpy; -%ignore tppncpy; -%ignore typcmp; -%ignore typdup; -%ignore equal_types; -%ignore resolve_typedef; -%ignore is_resolved_type_const; -%ignore is_resolved_type_void; -%ignore is_resolved_type_ptr; -%ignore is_resolved_type_func; -%ignore is_resolved_type_array; -%ignore is_resolved_type_complex; -%ignore is_resolved_type_struct; -%ignore is_resolved_type_union; -%ignore is_resolved_type_enum; -%ignore is_resolved_type_bitfld; -%ignore is_castable; -%ignore remove_constness; -%ignore remove_pointerness; -%ignore get_int_type_bit; -%ignore get_unk_type_bit; -%ignore tns; - -%ignore til_t::base; -%ignore til_t::syms; -%ignore til_t::types; -%ignore til_t::macros; - -%ignore add_base_tils; -%ignore sort_til; -%ignore til_add_macro; -%ignore til_next_macro; - -%ignore parse_subtype; -%ignore calc_type_size; -%ignore get_type_size; -%ignore get_type_size0; -%ignore skip_type; -%ignore get_pointer_object_size; - -%ignore descr_t; - -%ignore unpack_type; -%ignore print_type_to_one_line; -%ignore print_type_to_many_lines; -%ignore print_type; -%ignore show_type; -%ignore show_plist; -%ignore show_bytes; -%ignore skip_function_arg_names; -%ignore perform_funcarg_conversion; -%ignore get_argloc_info; -%ignore argloc_t::dstr; - -%ignore extract_pstr; -%ignore skipName; -%ignore extract_comment; -%ignore skipComment; -%ignore extract_fargcmt; -%ignore skip_argloc; -%ignore extract_argloc; - -%ignore h2ti; -%ignore h2ti_warning; -%ignore parse_type; -%ignore parse_types; -%ignore get_named_type; - -%ignore set_named_type; -%ignore get_named_type_size; - -%ignore decorate_name; -%ignore decorate_name3; -%ignore gen_decorate_name; -%ignore calc_bare_name; -%ignore calc_bare_name3; -%ignore calc_cpp_name; -%ignore calc_c_cpp_name; -%ignore calc_c_cpp_name3; -%ignore predicate_t; -%ignore local_predicate_t; -%ignore tinfo_predicate_t; -%ignore local_tinfo_predicate_t; -%ignore choose_named_type; -%ignore get_default_align; -%ignore align_size; -%ignore align_size; -%ignore get_default_enum_size; -%ignore max_ptr_size; -%ignore based_ptr_name_and_size; -%ignore calc_arglocs; - -%ignore apply_type; -%ignore apply_callee_type; -%ignore guess_func_type; -%ignore guess_type; - -%ignore build_funcarg_arrays; -%ignore free_funcarg_arrays; -%ignore extract_func_ret_type; -%ignore calc_names_cmts; -%ignore resolve_complex_type; -%ignore visit_strmems; -%ignore foreach_strmem; -%ignore is_type_scalar; -%ignore get_type_signness; -%ignore is_type_signed; -%ignore is_type_unsigned; -%ignore get_struct_member; -%ignore idb_type_to_til; -%ignore get_idb_type; - -%ignore apply_type_to_stkarg; -%rename (apply_type_to_stkarg) py_apply_type_to_stkarg; -%ignore print_type; -%rename (print_type) py_print_type; -%rename (calc_type_size) py_calc_type_size; -%rename (apply_type) py_apply_type; - -%ignore use_regarg_type_cb; -%ignore set_op_type_t; -%ignore is_stkarg_load_t; -%ignore has_delay_slot_t; -%ignore gen_use_arg_types; -%ignore enable_numbered_types; -%ignore compact_numbered_types; - -%ignore type_pair_vec_t::add_names; - -%ignore format_data_info_t; -%ignore valinfo_t; -%ignore print_c_data; -%ignore print_cdata; -%ignore format_c_data; -%ignore format_cdata; -%ignore format_cdata2; -%ignore format_c_number; -%ignore get_enum_member_expr; -%ignore extend_sign; - -// Kernel-only symbols -%ignore build_anon_type_name; -%ignore enum_type_data_t::is_signed; -%ignore enum_type_data_t::is_unsigned; -%ignore enum_type_data_t::get_sign; -%ignore bitfield_type_data_t::serialize; -%ignore func_type_data_t::serialize; -%ignore func_type_data_t::deserialize; -%ignore enum_type_data_t::get_enum_base_type; -%ignore enum_type_data_t::deserialize_enum; -%ignore valstr_deprecated_t; -%ignore valinfo_deprecated_t; -%ignore valstr_deprecated2_t; -%ignore valinfo_deprecated2_t; - -%ignore custloc_desc_t; -%ignore install_custom_argloc; -%ignore remove_custom_argloc; -%ignore retrieve_custom_argloc; - -%{ -// -//------------------------------------------------------------------------- -// A set of tinfo_t & details objects that were created from IDAPython. -// This is necessary in order to clear all the "type details" that are -// associated, in the kernel, with the tinfo_t instances. -// -// Unfortunately the IDAPython plugin has to terminate _after_ the IDB is -// closed, but the "type details" must be cleared _before_ the IDB is closed. -static qvector py_tinfo_t_vec; -static qvector py_ptr_type_data_t_vec; -static qvector py_array_type_data_t_vec; -static qvector py_func_type_data_t_vec; -static qvector py_udt_type_data_t_vec; - -static void __clear(tinfo_t *inst) { inst->clear(); } -static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); } -static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); } -static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); } -static void __clear(udt_type_data_t *inst) { inst->clear(); } - -void til_clear_python_tinfo_t_instances(void) -{ - // Pre-emptive strike: clear all the python-exposed tinfo_t - // (& related types) instances: if that were not done here, - // ~tinfo_t() calls happening as part of the python shutdown - // process will try and clear() their details. ..but the kernel's - // til-related functions will already have deleted those details - // at that point. - // - // NOTE: Don't clear() the arrays of pointers. All the python-exposed - // instances will be deleted through the python shutdown/ref-decrementing - // process anyway (which will cause til_deregister_..() calls), and the - // entries will be properly pulled out of the vector when that happens. -#define BATCH_CLEAR(Type) \ - do \ - { \ - for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \ - __clear(py_##Type##_vec[i]); \ - } while ( false ) - - BATCH_CLEAR(tinfo_t); - BATCH_CLEAR(ptr_type_data_t); - BATCH_CLEAR(array_type_data_t); - BATCH_CLEAR(func_type_data_t); - BATCH_CLEAR(udt_type_data_t); -#undef BATCH_CLEAR -} - -#define DEF_REG_UNREG_REFCOUNTED(Type) \ - void til_register_python_##Type##_instance(Type *inst) \ - { \ - /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \ - /* to an object's tinfo_t property will end up trying to register it. */ \ - py_##Type##_vec.add_unique(inst); \ - } \ - \ - void til_deregister_python_##Type##_instance(Type *inst) \ - { \ - qvector::iterator found = py_##Type##_vec.find(inst); \ - if ( found != py_##Type##_vec.end() ) \ - { \ - __clear(inst); \ - /* tif->clear();*/ \ - py_##Type##_vec.erase(found); \ - } \ - } - -DEF_REG_UNREG_REFCOUNTED(tinfo_t); -DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t); -DEF_REG_UNREG_REFCOUNTED(array_type_data_t); -DEF_REG_UNREG_REFCOUNTED(func_type_data_t); -DEF_REG_UNREG_REFCOUNTED(udt_type_data_t); - -#undef DEF_REG_UNREG_REFCOUNTED - -// -%} - -%extend tinfo_t { - - bool deserialize( - const til_t *til, - const type_t *type, - const p_list *fields, - const p_list *cmts = NULL) - { - return $self->deserialize(til, &type, &fields, cmts == NULL ? NULL : &cmts); - } - - // The typemap in typeconv.i will take care of registering newly-constructed - // tinfo_t instances. However, there's no such thing as a destructor typemap. - // Therefore, we need to do the grunt work of de-registering ourselves. - // Note: The 'void' here is important: Without it, SWIG considers it to - // be a different destructor (which, of course, makes a ton of sense.) - ~tinfo_t(void) - { - til_deregister_python_tinfo_t_instance($self); - delete $self; - } -} -%ignore tinfo_t::~tinfo_t(void); - -//--------------------------------------------------------------------- -// NOTE: This will ***NOT*** work for tinfo_t objects. Those must -// be created and owned (or not) according to the kind of access. -// To implement that, we use typemaps (see typeconv.i). -%define %simple_tinfo_t_container_lifecycle(Type, CtorSig, ParamsList) -%extend Type { - Type CtorSig - { - Type *inst = new Type ParamsList; - til_register_python_##Type##_instance(inst); - return inst; - } - - ~Type(void) - { - til_deregister_python_##Type##_instance($self); - delete $self; - } -} -%enddef -%simple_tinfo_t_container_lifecycle(ptr_type_data_t, (tinfo_t c=tinfo_t(), uchar bps=0), (c, bps)); -%simple_tinfo_t_container_lifecycle(array_type_data_t, (size_t b=0, size_t n=0), (b, n)); -%simple_tinfo_t_container_lifecycle(func_type_data_t, (), ()); -%simple_tinfo_t_container_lifecycle(udt_type_data_t, (), ()); - -%template(funcargvec_t) qvector; -%template(udtmembervec_t) qvector; - -%include "typeinf.hpp" - -// Custom wrappers - -%rename (load_til) load_til_wrap; -%rename (get_type_size0) py_get_type_size0; -%rename (idc_get_type_raw) py_idc_get_type_raw; -%rename (idc_get_local_type_raw) py_idc_get_local_type_raw; -%rename (unpack_object_from_idb) py_unpack_object_from_idb; -%rename (unpack_object_from_bv) py_unpack_object_from_bv; -%rename (pack_object_to_idb) py_pack_object_to_idb; -%rename (pack_object_to_bv) py_pack_object_to_bv; -%inline %{ -// -//------------------------------------------------------------------------- -PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) -{ - tinfo_t tif; - qstring name; - qtype fields, type; - bool ok = parse_decl2(ti, decl, &name, &tif, flags); - if ( ok ) - ok = tif.serialize(&type, &fields, NULL, SUDT_FAST); - - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("(sss)", - name.c_str(), - (char *)type.c_str(), - (char *)fields.c_str()); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -/* -# -def calc_type_size(ti, tp): - """ - Returns the size of a type - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @return: - - None on failure - - The size of the type - """ - pass -# -*/ -PyObject *py_calc_type_size(const til_t *ti, PyObject *tp) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( PyString_Check(tp) ) - { - // To avoid release of 'data' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t tpref(tp); - const type_t *data = (type_t *)PyString_AsString(tp); - size_t sz; - Py_BEGIN_ALLOW_THREADS; - tinfo_t tif; - tif.deserialize(ti, &data, NULL, NULL); - sz = tif.get_size(); - Py_END_ALLOW_THREADS; - if ( sz != BADSIZE ) - return PyInt_FromLong(sz); - Py_RETURN_NONE; - } - else - { - PyErr_SetString(PyExc_ValueError, "String expected!"); - return NULL; - } -} - -//------------------------------------------------------------------------- -/* -# -def apply_type(ti, ea, tp_name, py_type, py_fields, flags) - """ - Apply the specified type to the address - @param ti: Type info library. 'idaapi.cvar.idati' can be used. - @param py_type: type string - @param py_fields: fields string (may be empty or None) - @param ea: the address of the object - @param flags: combination of TINFO_... constants or 0 - @return: Boolean - """ - pass -# -*/ -static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - const type_t *type = (const type_t *) PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - bool rc; - Py_BEGIN_ALLOW_THREADS; - struc_t *sptr; - member_t *mptr = get_member_by_id(ea, &sptr); - if ( type[0] == '\0' ) - { - if ( mptr != NULL ) - { - rc = mptr->has_ti(); - if ( rc ) - del_member_tinfo(sptr, mptr); - } - else - { - rc = has_ti(ea); - if ( rc ) - del_tinfo2(ea); - } - } - else - { - tinfo_t tif; - rc = tif.deserialize(ti, &type, &fields, NULL); - if ( rc ) - { - if ( mptr != NULL ) - rc = set_member_tinfo2(sptr, mptr, 0, tif, 0); - else - rc = apply_tinfo2(ea, tif, flags); - } - } - Py_END_ALLOW_THREADS; - return rc; -} - -//------------------------------------------------------------------------- -/* -# -def print_type(ea, one_line): - """ - Returns the type of an item - @return: - - None on failure - - The type string with a semicolon. Can be used directly with idc.SetType() - """ - pass -# -*/ -static PyObject *py_print_type(ea_t ea, bool one_line) -{ - char buf[64*MAXSTR]; - int flags = PRTYPE_SEMI | (one_line ? PRTYPE_1LINE : PRTYPE_MULTI); - bool ok = print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return PyString_FromString(buf); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -/* -# -def py_unpack_object_from_idb(ti, tp, fields, ea, pio_flags = 0): - """ - Unpacks from the database at 'ea' to an object. - Please refer to unpack_object_from_bv() - """ - pass -# -*/ -PyObject *py_unpack_object_from_idb( - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - ea_t ea, - int pio_flags = 0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Unpack - type_t *type = (type_t *) PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - idc_value_t idc_obj; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = unpack_object_from_idb( - &idc_obj, - ti, - type, - fields, - ea, - NULL, - pio_flags); - Py_END_ALLOW_THREADS; - - // Unpacking failed? - if ( err != eOk ) - return Py_BuildValue("(ii)", 0, err); - - // Convert - ref_t py_ret; - err = idcvar_to_pyvar(idc_obj, &py_ret); - - // Conversion failed? - if ( err != CIP_OK ) - return Py_BuildValue("(ii)", 0, err); - else - return Py_BuildValue("(iO)", 1, py_ret.o); -} - -//------------------------------------------------------------------------- -/* -# -def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0): - """ - Unpacks a buffer into an object. - Returns the error_t returned by idaapi.pack_object_to_idb - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @param fields: fields string (may be empty or None) - @param bytes: the bytes to unpack - @param pio_flags: flags used while unpacking - @return: - - tuple(0, err) on failure - - tuple(1, obj) on success - """ - pass -# -*/ -PyObject *py_unpack_object_from_bv( - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - PyObject *py_bytes, - int pio_flags = 0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) || !PyString_Check(py_bytes) ) - { - PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); - return NULL; - } - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Get type strings - type_t *type = (type_t *) PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - - // Make a byte vector - bytevec_t bytes; - bytes.resize(PyString_Size(py_bytes)); - memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); - - idc_value_t idc_obj; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = unpack_object_from_bv( - &idc_obj, - ti, - type, - fields, - bytes, - pio_flags); - Py_END_ALLOW_THREADS; - - // Unpacking failed? - if ( err != eOk ) - return Py_BuildValue("(ii)", 0, err); - - // Convert - ref_t py_ret; - err = idcvar_to_pyvar(idc_obj, &py_ret); - - // Conversion failed? - if ( err != CIP_OK ) - return Py_BuildValue("(ii)", 0, err); - - return Py_BuildValue("(iO)", 1, py_ret.o); -} - -//------------------------------------------------------------------------- -/* -# -def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0): - """ - Write a typed object to the database. - Raises an exception if wrong parameters were passed or conversion fails - Returns the error_t returned by idaapi.pack_object_to_idb - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @param fields: fields string (may be empty or None) - @param ea: ea to be used while packing - @param pio_flags: flags used while unpacking - """ - pass -# -*/ -PyObject *py_pack_object_to_idb( - PyObject *py_obj, - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - ea_t ea, - int pio_flags = 0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // Convert Python object to IDC object - idc_value_t idc_obj; - borref_t py_obj_ref(py_obj); - if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) - return NULL; - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Get type strings - type_t *type = (type_t *)PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - - // Pack - // error_t err; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); - Py_END_ALLOW_THREADS; - return PyInt_FromLong(err); -} - -//------------------------------------------------------------------------- -/* -# -def pack_object_to_bv(obj, ti, tp, fields, base_ea, pio_flags = 0): - """ - Packs a typed object to a string - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @param fields: fields string (may be empty or None) - @param base_ea: base ea used to relocate the pointers in the packed object - @param pio_flags: flags used while unpacking - @return: - tuple(0, err_code) on failure - tuple(1, packed_buf) on success - """ - pass -# -*/ -// Returns a tuple(Boolean, PackedBuffer or Error Code) -PyObject *py_pack_object_to_bv( - PyObject *py_obj, - til_t *ti, - PyObject *py_type, - PyObject *py_fields, - ea_t base_ea, - int pio_flags=0) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // Convert Python object to IDC object - idc_value_t idc_obj; - borref_t py_obj_ref(py_obj); - if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) - return NULL; - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - // Get type strings - type_t *type = (type_t *)PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - - // Pack - relobj_t bytes; - error_t err; - Py_BEGIN_ALLOW_THREADS; - err = pack_object_to_bv( - &idc_obj, - ti, - type, - fields, - &bytes, - NULL, - pio_flags); - if ( err == eOk && !bytes.relocate(base_ea, inf.mf) ) - err = -1; - Py_END_ALLOW_THREADS; - if ( err == eOk ) - return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); - else - return Py_BuildValue("(ii)", 0, err); -} - -//------------------------------------------------------------------------- -/* Parse types from a string or file. See ParseTypes() in idc.py */ -int idc_parse_types(const char *input, int flags) -{ - int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; - - if ((flags & 1) != 0) - hti |= HTI_FIL; - - return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); -} - -//------------------------------------------------------------------------- -PyObject *py_idc_get_type_raw(ea_t ea) -{ - qtype type, fields; - bool ok = get_tinfo(ea, &type, &fields); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -PyObject *py_idc_get_local_type_raw(int ordinal) -{ - const type_t *type; - const p_list *fields; - bool ok = get_numbered_type(idati, ordinal, &type, &fields); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("(ss)", (char *)type, (char *)fields); - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) -{ - tinfo_t tif; - if ( guess_tinfo2(ea, &tif) ) - { - qstring out; - if ( tif.print(&out) ) - return qstrncpy(buf, out.begin(), bufsize); - } - return NULL; -} - -//------------------------------------------------------------------------- -char *idc_get_type(ea_t ea, char *buf, size_t bufsize) -{ - tinfo_t tif; - if ( get_tinfo2(ea, &tif) ) - { - qstring out; - if ( tif.print(&out) ) - { - qstrncpy(buf, out.c_str(), bufsize); - return buf; - } - } - return NULL; -} - -//------------------------------------------------------------------------- -int idc_set_local_type(int ordinal, const char *dcl, int flags) -{ - if (dcl == NULL || dcl[0] == '\0') - { - if ( !del_numbered_type(idati, ordinal) ) - return 0; - } - else - { - tinfo_t tif; - qstring name; - if ( !parse_decl2(idati, dcl, &name, &tif, flags) ) - return 0; - - if ( ordinal <= 0 ) - { - if ( !name.empty() ) - ordinal = get_type_ordinal(idati, name.begin()); - - if ( ordinal <= 0 ) - ordinal = alloc_type_ordinal(idati); - } - - if ( tif.set_numbered_type(idati, ordinal, 0, name.c_str()) != TERR_OK ) - return 0; - } - return ordinal; -} - -//------------------------------------------------------------------------- -int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) -{ - tinfo_t tif; - if ( !tif.get_numbered_type(idati, ordinal) ) - { - buf[0] = 0; - return false; - } - - qstring res; - const char *name = get_numbered_type_name(idati, ordinal); - if ( !tif.print(&res, name, flags, 2, 40) ) - { - buf[0] = 0; - return false; - } - - qstrncpy(buf, res.begin(), maxsize); - return true; -} - -//------------------------------------------------------------------------- -PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) - { - PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); - return NULL; - } - - // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. - borref_t py_type_ref(py_type); - borref_t py_fields_ref(py_fields); - - qstring res; - const type_t *type = (type_t *)PyString_AsString(py_type); - const p_list *fields = PyW_Fields(py_fields); - bool ok; - Py_BEGIN_ALLOW_THREADS; - tinfo_t tif; - ok = tif.deserialize(idati, &type, &fields, NULL) - && tif.print(&res, name, flags, 2, 40); - Py_END_ALLOW_THREADS; - if ( ok ) - return PyString_FromString(res.begin()); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) -{ - const char *name = get_numbered_type_name(idati, ordinal); - if ( name == NULL ) - return false; - - qstrncpy(buf, name, bufsize); - return true; -} -// -til_t *load_til(const char *tildir, const char *name) -{ - char errbuf[MAXSTR]; - til_t *res = load_til(tildir, name, errbuf, sizeof(errbuf)); - if ( res == NULL ) - PyErr_SetString(PyExc_RuntimeError, errbuf); - return res; -} -%} - -%rename (load_til_header) load_til_header_wrap; -%inline %{ -til_t *load_til_header_wrap(const char *tildir, const char *name) -{ - char errbuf[MAXSTR]; - til_t *res = load_til_header(tildir, name, errbuf, sizeof(errbuf));; - if ( res == NULL ) - PyErr_SetString(PyExc_RuntimeError, errbuf); - return res; -} -%} - -%cstring_output_maxsize(char *buf, size_t maxsize); - -%pythoncode %{ -# - -def get_type_size0(ti, tp): - """ - DEPRECATED. Please use calc_type_size instead - Returns the size of a type - @param ti: Type info. 'idaapi.cvar.idati' can be passed. - @param tp: type string - @return: - - None on failure - - The size of the type - """ - return calc_type_size(ti, tp) - -# - -%} +// Most of these could be wrapped if needed +%ignore get_cc; +%ignore get_cc_type_size; +%ignore set_argloc; +%ignore set_dt; +%ignore set_da; +%ignore set_de; +%ignore get_dt; +%ignore get_da; +%ignore get_de; +%ignore skip_ptr_type_header; +%ignore skip_array_type_header; +%ignore unpack_object_from_idb; +%ignore unpack_object_from_bv; +%ignore pack_object_to_idb; +%ignore pack_object_to_bv; +%ignore typend; +%ignore typlen; +%ignore typncpy; +%ignore tppncpy; +%ignore typcmp; +%ignore typdup; +%ignore equal_types; +%ignore resolve_typedef; +%ignore is_resolved_type_const; +%ignore is_resolved_type_void; +%ignore is_resolved_type_ptr; +%ignore is_resolved_type_func; +%ignore is_resolved_type_array; +%ignore is_resolved_type_complex; +%ignore is_resolved_type_struct; +%ignore is_resolved_type_union; +%ignore is_resolved_type_enum; +%ignore is_resolved_type_bitfld; +%ignore is_castable; +%ignore remove_constness; +%ignore remove_pointerness; +%ignore get_int_type_bit; +%ignore get_unk_type_bit; +%ignore tns; + +%ignore til_t::syms; +%ignore til_t::types; +%ignore til_t::macros; + +%ignore add_base_tils; +%ignore sort_til; +%ignore til_add_macro; +%ignore til_next_macro; + +%ignore parse_subtype; +%ignore calc_type_size; +%ignore get_type_size; +%ignore get_type_size0; +%ignore skip_type; +%ignore get_pointer_object_size; + +%ignore descr_t; + +%ignore unpack_type; +%ignore print_type_to_one_line; +%ignore print_type_to_many_lines; +%ignore print_type; +%ignore show_type; +%ignore show_plist; +%ignore show_bytes; +%ignore skip_function_arg_names; +%ignore perform_funcarg_conversion; +%ignore get_argloc_info; +%ignore argloc_t::dstr; + +%ignore extract_pstr; +%ignore skipName; +%ignore extract_comment; +%ignore skipComment; +%ignore extract_fargcmt; +%ignore skip_argloc; +%ignore extract_argloc; + +%ignore h2ti; +%ignore h2ti_warning; +%ignore parse_type; +%ignore parse_types; +// We want to handle 'get_named_type()' in a special way, +// but not tinfo_t::get_named_type(). +// http://stackoverflow.com/questions/27417884/how-do-i-un-ignore-a-specific-method-on-a-templated-class-in-swig +%ignore get_named_type; +%rename (get_named_type) py_get_named_type; +%ignore get_named_type64; +%rename (get_named_type64) py_get_named_type64; +%rename ("%s") tinfo_t::get_named_type; + +%rename (print_decls) py_print_decls; +%ignore print_decls; + +%ignore set_named_type; +%ignore get_named_type_size; + +%ignore decorate_name; +%ignore decorate_name3; +%ignore gen_decorate_name; +%ignore calc_bare_name; +%ignore calc_bare_name3; +%ignore calc_cpp_name; +%ignore calc_c_cpp_name; +%ignore calc_c_cpp_name3; +%ignore predicate_t; +%ignore local_predicate_t; +%ignore tinfo_predicate_t; +%ignore local_tinfo_predicate_t; +%ignore choose_named_type; +%ignore get_default_align; +%ignore align_size; +%ignore align_size; +%ignore get_default_enum_size; +%ignore max_ptr_size; +%ignore based_ptr_name_and_size; +%ignore calc_arglocs; + +%ignore apply_type; +%ignore apply_callee_type; +%ignore guess_func_type; +%ignore guess_type; + +%ignore build_funcarg_arrays; +%ignore free_funcarg_arrays; +%ignore extract_func_ret_type; +%ignore calc_names_cmts; +%ignore resolve_complex_type; +%ignore visit_strmems; +%ignore foreach_strmem; +%ignore is_type_scalar; +%ignore get_type_signness; +%ignore is_type_signed; +%ignore is_type_unsigned; +%ignore get_struct_member; +%ignore idb_type_to_til; +%ignore get_idb_type; + +%ignore apply_type_to_stkarg; +%rename (apply_type_to_stkarg) py_apply_type_to_stkarg; +%ignore print_type; +%rename (print_type) py_print_type; +%rename (calc_type_size) py_calc_type_size; +%rename (apply_type) py_apply_type; + +%ignore use_regarg_type_cb; +%ignore set_op_type_t; +%ignore is_stkarg_load_t; +%ignore has_delay_slot_t; +%ignore gen_use_arg_types; +%ignore enable_numbered_types; +%ignore compact_numbered_types; + +%ignore type_pair_vec_t::add_names; + +%ignore format_data_info_t; +%ignore valinfo_t; +%ignore print_c_data; +%ignore print_cdata; +%ignore format_c_data; +%ignore format_cdata; +%ignore format_cdata2; +%ignore format_c_number; +%ignore get_enum_member_expr; +%ignore extend_sign; + +// Kernel-only symbols +%ignore build_anon_type_name; +%ignore enum_type_data_t::is_signed; +%ignore enum_type_data_t::is_unsigned; +%ignore enum_type_data_t::get_sign; +%ignore bitfield_type_data_t::serialize; +%ignore func_type_data_t::serialize; +%ignore func_type_data_t::deserialize; +%ignore enum_type_data_t::get_enum_base_type; +%ignore enum_type_data_t::deserialize_enum; +%ignore valstr_deprecated_t; +%ignore valinfo_deprecated_t; +%ignore valstr_deprecated2_t; +%ignore valinfo_deprecated2_t; + +%ignore custloc_desc_t; +%ignore install_custom_argloc; +%ignore remove_custom_argloc; +%ignore retrieve_custom_argloc; + +%{ +// +//------------------------------------------------------------------------- +// A set of tinfo_t & details objects that were created from IDAPython. +// This is necessary in order to clear all the "type details" that are +// associated, in the kernel, with the tinfo_t instances. +// +// Unfortunately the IDAPython plugin has to terminate _after_ the IDB is +// closed, but the "type details" must be cleared _before_ the IDB is closed. +static qvector py_tinfo_t_vec; +static qvector py_ptr_type_data_t_vec; +static qvector py_array_type_data_t_vec; +static qvector py_func_type_data_t_vec; +static qvector py_udt_type_data_t_vec; + +static void __clear(tinfo_t *inst) { inst->clear(); } +static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); } +static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); } +static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); } +static void __clear(udt_type_data_t *inst) { inst->clear(); } + +void til_clear_python_tinfo_t_instances(void) +{ + // Pre-emptive strike: clear all the python-exposed tinfo_t + // (& related types) instances: if that were not done here, + // ~tinfo_t() calls happening as part of the python shutdown + // process will try and clear() their details. ..but the kernel's + // til-related functions will already have deleted those details + // at that point. + // + // NOTE: Don't clear() the arrays of pointers. All the python-exposed + // instances will be deleted through the python shutdown/ref-decrementing + // process anyway (which will cause til_deregister_..() calls), and the + // entries will be properly pulled out of the vector when that happens. +#define BATCH_CLEAR(Type) \ + do \ + { \ + for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \ + __clear(py_##Type##_vec[i]); \ + } while ( false ) + + BATCH_CLEAR(tinfo_t); + BATCH_CLEAR(ptr_type_data_t); + BATCH_CLEAR(array_type_data_t); + BATCH_CLEAR(func_type_data_t); + BATCH_CLEAR(udt_type_data_t); +#undef BATCH_CLEAR +} + +#define DEF_REG_UNREG_REFCOUNTED(Type) \ + void til_register_python_##Type##_instance(Type *inst) \ + { \ + /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \ + /* to an object's tinfo_t property will end up trying to register it. */ \ + py_##Type##_vec.add_unique(inst); \ + } \ + \ + void til_deregister_python_##Type##_instance(Type *inst) \ + { \ + qvector::iterator found = py_##Type##_vec.find(inst); \ + if ( found != py_##Type##_vec.end() ) \ + { \ + __clear(inst); \ + /* tif->clear();*/ \ + py_##Type##_vec.erase(found); \ + } \ + } + +DEF_REG_UNREG_REFCOUNTED(tinfo_t); +DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t); +DEF_REG_UNREG_REFCOUNTED(array_type_data_t); +DEF_REG_UNREG_REFCOUNTED(func_type_data_t); +DEF_REG_UNREG_REFCOUNTED(udt_type_data_t); + +#undef DEF_REG_UNREG_REFCOUNTED + +// +%} + +%extend til_t { + + til_t *base(int n) + { + return (n < 0 || n >= $self->nbases) ? NULL : $self->base[n]; + } +} + + +%extend tinfo_t { + + bool deserialize( + const til_t *til, + const type_t *type, + const p_list *fields, + const p_list *cmts = NULL) + { + return $self->deserialize(til, &type, &fields, cmts == NULL ? NULL : &cmts); + } + + bool deserialize( + const til_t *til, + const char *_type, + const char *_fields, + const char *_cmts = NULL) + { + const type_t *type = (const type_t *) _type; + const p_list *fields = (const p_list *) _fields; + const p_list *cmts = (const p_list *) _cmts; + return $self->deserialize(til, &type, &fields, cmts == NULL ? NULL : &cmts); + } + + // The typemap in typeconv.i will take care of registering newly-constructed + // tinfo_t instances. However, there's no such thing as a destructor typemap. + // Therefore, we need to do the grunt work of de-registering ourselves. + // Note: The 'void' here is important: Without it, SWIG considers it to + // be a different destructor (which, of course, makes a ton of sense.) + ~tinfo_t(void) + { + til_deregister_python_tinfo_t_instance($self); + delete $self; + } +} +%ignore tinfo_t::~tinfo_t(void); + +//--------------------------------------------------------------------- +// NOTE: This will ***NOT*** work for tinfo_t objects. Those must +// be created and owned (or not) according to the kind of access. +// To implement that, we use typemaps (see typeconv.i). +%define %simple_tinfo_t_container_lifecycle(Type, CtorSig, ParamsList) +%extend Type { + Type CtorSig + { + Type *inst = new Type ParamsList; + til_register_python_##Type##_instance(inst); + return inst; + } + + ~Type(void) + { + til_deregister_python_##Type##_instance($self); + delete $self; + } +} +%enddef +%simple_tinfo_t_container_lifecycle(ptr_type_data_t, (tinfo_t c=tinfo_t(), uchar bps=0), (c, bps)); +%simple_tinfo_t_container_lifecycle(array_type_data_t, (size_t b=0, size_t n=0), (b, n)); +%simple_tinfo_t_container_lifecycle(func_type_data_t, (), ()); +%simple_tinfo_t_container_lifecycle(udt_type_data_t, (), ()); + +%template(funcargvec_t) qvector; +%template(udtmembervec_t) qvector; + +%feature("director") text_sink_t; +%warnfilter(514) text_sink_t; // Director base class 'x' has no virtual destructor. + +%include "typeinf.hpp" + +// Custom wrappers + +%rename (load_til) load_til_wrap; +%rename (get_type_size0) py_get_type_size0; +%rename (idc_get_type_raw) py_idc_get_type_raw; +%rename (idc_get_local_type_raw) py_idc_get_local_type_raw; +%rename (unpack_object_from_idb) py_unpack_object_from_idb; +%rename (unpack_object_from_bv) py_unpack_object_from_bv; +%rename (pack_object_to_idb) py_pack_object_to_idb; +%rename (pack_object_to_bv) py_pack_object_to_bv; +%inline %{ +// +//------------------------------------------------------------------------- +PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) +{ + tinfo_t tif; + qstring name; + qtype fields, type; + bool ok = parse_decl2(ti, decl, &name, &tif, flags); + if ( ok ) + ok = tif.serialize(&type, &fields, NULL, SUDT_FAST); + + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(sss)", + name.c_str(), + (char *)type.c_str(), + (char *)fields.c_str()); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +def calc_type_size(ti, tp): + """ + Returns the size of a type + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @return: + - None on failure + - The size of the type + """ + pass +# +*/ +PyObject *py_calc_type_size(const til_t *ti, PyObject *tp) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(tp) ) + { + // To avoid release of 'data' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t tpref(tp); + const type_t *data = (type_t *)PyString_AsString(tp); + size_t sz; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + tif.deserialize(ti, &data, NULL, NULL); + sz = tif.get_size(); + Py_END_ALLOW_THREADS; + if ( sz != BADSIZE ) + return PyInt_FromLong(sz); + Py_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_ValueError, "String expected!"); + return NULL; + } +} + +//------------------------------------------------------------------------- +/* +# +def apply_type(ti, ea, tp_name, py_type, py_fields, flags) + """ + Apply the specified type to the address + @param ti: Type info library. 'idaapi.cvar.idati' can be used. + @param py_type: type string + @param py_fields: fields string (may be empty or None) + @param ea: the address of the object + @param flags: combination of TINFO_... constants or 0 + @return: Boolean + """ + pass +# +*/ +static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + const type_t *type = (const type_t *) PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + bool rc; + Py_BEGIN_ALLOW_THREADS; + struc_t *sptr; + member_t *mptr = get_member_by_id(ea, &sptr); + if ( type[0] == '\0' ) + { + if ( mptr != NULL ) + { + rc = mptr->has_ti(); + if ( rc ) + del_member_tinfo(sptr, mptr); + } + else + { + rc = has_ti(ea); + if ( rc ) + del_tinfo2(ea); + } + } + else + { + tinfo_t tif; + rc = tif.deserialize(ti, &type, &fields, NULL); + if ( rc ) + { + if ( mptr != NULL ) + rc = set_member_tinfo2(sptr, mptr, 0, tif, 0); + else + rc = apply_tinfo2(ea, tif, flags); + } + } + Py_END_ALLOW_THREADS; + return rc; +} + +//------------------------------------------------------------------------- +/* +# +def print_type(ea, one_line): + """ + Returns the type of an item + @return: + - None on failure + - The type string with a semicolon. Can be used directly with idc.SetType() + """ + pass +# +*/ +static PyObject *py_print_type(ea_t ea, bool one_line) +{ + char buf[64*MAXSTR]; + int flags = PRTYPE_SEMI | (one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + bool ok = print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return PyString_FromString(buf); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +def py_unpack_object_from_idb(ti, tp, fields, ea, pio_flags = 0): + """ + Unpacks from the database at 'ea' to an object. + Please refer to unpack_object_from_bv() + """ + pass +# +*/ +PyObject *py_unpack_object_from_idb( + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t ea, + int pio_flags = 0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Unpack + type_t *type = (type_t *) PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + idc_value_t idc_obj; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_idb( + &idc_obj, + ti, + type, + fields, + ea, + NULL, + pio_flags); + Py_END_ALLOW_THREADS; + + // Unpacking failed? + if ( err != eOk ) + return Py_BuildValue("(ii)", 0, err); + + // Convert + ref_t py_ret; + err = idcvar_to_pyvar(idc_obj, &py_ret); + + // Conversion failed? + if ( err != CIP_OK ) + return Py_BuildValue("(ii)", 0, err); + else + return Py_BuildValue("(iO)", 1, py_ret.o); +} + +//------------------------------------------------------------------------- +/* +# +def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0): + """ + Unpacks a buffer into an object. + Returns the error_t returned by idaapi.pack_object_to_idb + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: fields string (may be empty or None) + @param bytes: the bytes to unpack + @param pio_flags: flags used while unpacking + @return: + - tuple(0, err) on failure + - tuple(1, obj) on success + """ + pass +# +*/ +PyObject *py_unpack_object_from_bv( + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + PyObject *py_bytes, + int pio_flags = 0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) || !PyString_Check(py_bytes) ) + { + PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + + // Make a byte vector + bytevec_t bytes; + bytes.resize(PyString_Size(py_bytes)); + memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); + + idc_value_t idc_obj; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_bv( + &idc_obj, + ti, + type, + fields, + bytes, + pio_flags); + Py_END_ALLOW_THREADS; + + // Unpacking failed? + if ( err != eOk ) + return Py_BuildValue("(ii)", 0, err); + + // Convert + ref_t py_ret; + err = idcvar_to_pyvar(idc_obj, &py_ret); + + // Conversion failed? + if ( err != CIP_OK ) + return Py_BuildValue("(ii)", 0, err); + + return Py_BuildValue("(iO)", 1, py_ret.o); +} + +//------------------------------------------------------------------------- +/* +# +def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0): + """ + Write a typed object to the database. + Raises an exception if wrong parameters were passed or conversion fails + Returns the error_t returned by idaapi.pack_object_to_idb + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: fields string (may be empty or None) + @param ea: ea to be used while packing + @param pio_flags: flags used while unpacking + """ + pass +# +*/ +PyObject *py_pack_object_to_idb( + PyObject *py_obj, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t ea, + int pio_flags = 0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) + return NULL; + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Get type strings + type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + + // Pack + // error_t err; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + Py_END_ALLOW_THREADS; + return PyInt_FromLong(err); +} + +//------------------------------------------------------------------------- +/* +# +def pack_object_to_bv(obj, ti, tp, fields, base_ea, pio_flags = 0): + """ + Packs a typed object to a string + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: fields string (may be empty or None) + @param base_ea: base ea used to relocate the pointers in the packed object + @param pio_flags: flags used while unpacking + @return: + tuple(0, err_code) on failure + tuple(1, packed_buf) on success + """ + pass +# +*/ +// Returns a tuple(Boolean, PackedBuffer or Error Code) +PyObject *py_pack_object_to_bv( + PyObject *py_obj, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t base_ea, + int pio_flags=0) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) + return NULL; + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + // Get type strings + type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + + // Pack + relobj_t bytes; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_bv( + &idc_obj, + ti, + type, + fields, + &bytes, + NULL, + pio_flags); + if ( err == eOk && !bytes.relocate(base_ea, inf.mf) ) + err = -1; + Py_END_ALLOW_THREADS; + if ( err == eOk ) + return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); + else + return Py_BuildValue("(ii)", 0, err); +} + +//------------------------------------------------------------------------- +/* Parse types from a string or file. See ParseTypes() in idc.py */ +int idc_parse_types(const char *input, int flags) +{ + int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; + + if ((flags & 1) != 0) + hti |= HTI_FIL; + + return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_type_raw(ea_t ea) +{ + qtype type, fields; + bool ok = get_tinfo(ea, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_local_type_raw(int ordinal) +{ + const type_t *type; + const p_list *fields; + bool ok = get_numbered_type(idati, ordinal, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type, (char *)fields); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( guess_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + return qstrncpy(buf, out.begin(), bufsize); + } + return NULL; +} + +//------------------------------------------------------------------------- +char *idc_get_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( get_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + { + qstrncpy(buf, out.c_str(), bufsize); + return buf; + } + } + return NULL; +} + +//------------------------------------------------------------------------- +int idc_set_local_type(int ordinal, const char *dcl, int flags) +{ + if (dcl == NULL || dcl[0] == '\0') + { + if ( !del_numbered_type(idati, ordinal) ) + return 0; + } + else + { + tinfo_t tif; + qstring name; + if ( !parse_decl2(idati, dcl, &name, &tif, flags) ) + return 0; + + if ( ordinal <= 0 ) + { + if ( !name.empty() ) + ordinal = get_type_ordinal(idati, name.begin()); + + if ( ordinal <= 0 ) + ordinal = alloc_type_ordinal(idati); + } + + if ( tif.set_numbered_type(idati, ordinal, 0, name.c_str()) != TERR_OK ) + return 0; + } + return ordinal; +} + +//------------------------------------------------------------------------- +int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) +{ + tinfo_t tif; + if ( !tif.get_numbered_type(idati, ordinal) ) + { + buf[0] = 0; + return false; + } + + qstring res; + const char *name = get_numbered_type_name(idati, ordinal); + if ( !tif.print(&res, name, flags, 2, 40) ) + { + buf[0] = 0; + return false; + } + + qstrncpy(buf, res.begin(), maxsize); + return true; +} + +//------------------------------------------------------------------------- +PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + qstring res; + const type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = PyW_Fields(py_fields); + bool ok; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + ok = tif.deserialize(idati, &type, &fields, NULL) + && tif.print(&res, name, flags, 2, 40); + Py_END_ALLOW_THREADS; + if ( ok ) + return PyString_FromString(res.begin()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) +{ + const char *name = get_numbered_type_name(idati, ordinal); + if ( name == NULL ) + return false; + + qstrncpy(buf, name, bufsize); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def get_named_type(til, name, ntf_flags): + """ + Get a type data by its name. + @param til: the type library + @param name: the type name + @param ntf_flags: a combination of NTF_* constants + @return: + None on failure + tuple(code, type_str, fields_str, cmt, field_cmts, sclass, value) on success + """ + pass +# +*/ +PyObject *py_get_named_type(const til_t *til, const char *name, int ntf_flags) +{ + const type_t *type = NULL; + const p_list *fields = NULL, *field_cmts = NULL; + const char *cmt = NULL; + sclass_t sclass = sc_unk; + uint32 value = 0; + int code = get_named_type(til, name, ntf_flags, &type, &fields, &cmt, &field_cmts, &sclass, &value); + if ( code == 0 ) + Py_RETURN_NONE; + PyObject *tuple = PyTuple_New(7); + int idx = 0; +#define ADD(Expr) PyTuple_SetItem(tuple, idx++, (Expr)) +#define ADD_OR_NONE(Cond, Expr) \ + do \ + { \ + if ( Cond ) \ + { \ + ADD(Expr); \ + } \ + else \ + { \ + Py_INCREF(Py_None); \ + ADD(Py_None); \ + } \ + } while ( false ) + + ADD(PyInt_FromLong(long(code))); + ADD(PyString_FromString((const char *) type)); + ADD_OR_NONE(fields != NULL, PyString_FromString((const char *) fields)); + ADD_OR_NONE(cmt != NULL, PyString_FromString(cmt)); + ADD_OR_NONE(field_cmts != NULL, PyString_FromString((const char *) field_cmts)); + ADD(PyInt_FromLong(long(sclass))); + ADD(PyLong_FromUnsignedLong(long(value))); +#undef ADD_OR_NONE +#undef ADD + return tuple; +} + +//------------------------------------------------------------------------- +PyObject *py_get_named_type64(const til_t *til, const char *name, int ntf_flags) +{ + return py_get_named_type(til, name, ntf_flags | NTF_64BIT); +} + +//------------------------------------------------------------------------- +int py_print_decls(text_sink_t &printer, til_t *til, PyObject *py_ordinals, uint32 flags) +{ + if ( !PyList_Check(py_ordinals) ) + { + PyErr_SetString(PyExc_ValueError, "'ordinals' must be a list"); + return 0; + } + + Py_ssize_t nords = PyList_Size(py_ordinals); + ordvec_t ordinals; + ordinals.reserve(size_t(nords)); + for ( Py_ssize_t i = 0; i < nords; ++i ) + { + borref_t item(PyList_GetItem(py_ordinals, i)); + if ( item == NULL + || (!PyInt_Check(item.o) && !PyLong_Check(item.o)) ) + { + qstring msg; + msg.sprnt("ordinals[%d] is not a valid value", int(i)); + PyErr_SetString(PyExc_ValueError, msg.begin()); + return 0; + } + uint32 ord = PyInt_Check(item.o) ? PyInt_AsLong(item.o) : PyLong_AsLong(item.o); + ordinals.push_back(ord); + } + return print_decls(printer, til, ordinals.empty() ? NULL : &ordinals, flags); +} + +// +til_t *load_til(const char *tildir, const char *name) +{ + char errbuf[MAXSTR]; + til_t *res = load_til(tildir, name, errbuf, sizeof(errbuf)); + if ( res == NULL ) + PyErr_SetString(PyExc_RuntimeError, errbuf); + return res; +} +%} + +%rename (load_til_header) load_til_header_wrap; +%inline %{ +til_t *load_til_header_wrap(const char *tildir, const char *name) +{ + char errbuf[MAXSTR]; + til_t *res = load_til_header(tildir, name, errbuf, sizeof(errbuf));; + if ( res == NULL ) + PyErr_SetString(PyExc_RuntimeError, errbuf); + return res; +} +%} + +%cstring_output_maxsize(char *buf, size_t maxsize); + +%pythoncode %{ +# + +def get_type_size0(ti, tp): + """ + DEPRECATED. Please use calc_type_size instead + Returns the size of a type + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @return: + - None on failure + - The size of the type + """ + return calc_type_size(ti, tp) + +# + +%} diff --git a/swig/ua.i b/swig/ua.i index c1a45e92..0f4ef0d1 100644 --- a/swig/ua.i +++ b/swig/ua.i @@ -1,1486 +1,1474 @@ -%ignore insn_t; -%ignore op_t; -%ignore cmd; -%ignore ua_out; -%ignore showAsChar; -%ignore out_real; -%ignore init_output_buffer; -%ignore term_output_buffer; -%ignore OutValue; -%ignore OutImmChar; -%ignore out_name_expr; -%ignore ua_stkvar2; -%ignore ua_add_off_drefs; -%ignore ua_add_off_drefs2; -%ignore out_snprintf; -%ignore set_output_ptr; -%ignore get_output_ptr; -%ignore out_insert; -%ignore get_immval; -%ignore get_spoiled_reg; -%ignore decode_preceding_insn; -%ignore init_ua; -%ignore term_ua; -%ignore term_uaterm_ua; -%ignore get_equal_items; -%ignore get_equal_itemsget_equal_items; -%ignore ua_use_fixup; - -%ignore get_immval; -%ignore ua_stkvar; - -%ignore construct_macro; -%rename (construct_macro) py_construct_macro; - -%include "ua.hpp" - -%rename (init_output_buffer) py_init_output_buffer; -%rename (term_output_buffer) py_term_output_buffer; -%rename (OutValue) py_OutValue; -%rename (OutImmChar) py_OutImmChar; -%rename (out_name_expr) py_out_name_expr; -%rename (ua_stkvar2) py_ua_stkvar2; -%rename (ua_add_off_drefs) py_ua_add_off_drefs; -%rename (ua_add_off_drefs2) py_ua_add_off_drefs2; -%rename (decode_preceding_insn) py_decode_preceding_insn; - -%inline %{ -// - -//------------------------------------------------------------------------- -/* -# -def init_output_buffer(size = MAXSTR): - """ - This function initialize an output buffer with the given size. - It should be called before using any out_xxxx() functions. - @return: It returns a string. This string should then be passed to MakeLine(). - This function could return None if it failed to create a buffer with the given size. - """ - pass -# -*/ -PyObject *py_init_output_buffer(size_t size = MAXSTR) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - // Let Python allocate a writable string buffer for us - PyObject *py_str = PyString_FromStringAndSize(NULL, size); - if ( py_str == NULL ) - Py_RETURN_NONE; - - init_output_buffer(PyString_AsString(py_str), size); - return py_str; -} - -//------------------------------------------------------------------------- -/* -# -def term_output_buffer(): - """Use this function to terminate an output buffer.""" - pass -# -*/ -void py_term_output_buffer() -{ - term_output_buffer(); -} - -//------------------------------------------------------------------------- -/* -# -def decode_preceding_insn(ea): - """ - Decodes the preceding instruction. Please check ua.hpp / decode_preceding_insn() - @param ea: current ea - @return: tuple(preceeding_ea or BADADDR, farref = Boolean) - """ - pass -# -*/ -PyObject *py_decode_preceding_insn(ea_t ea) -{ - bool farref; - ea_t r = decode_preceding_insn(ea, &farref); - PYW_GIL_CHECK_LOCKED_SCOPE(); - return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); -} - -//------------------------------------------------------------------------- -/* -# -def OutValue(op, outflags = 0): - """ - Output immediate value - @param op: operand (of type op_t) - @return: flags of the output value - -1: value is output with COLOR_ERROR - 0: value is output as a number or character or segment - """ - pass -# -*/ -flags_t py_OutValue(PyObject *x, int outflags=0) -{ - op_t *op = op_t_get_clink(x); - if ( op == NULL ) - return 0; - - return OutValue(*op, outflags); -} - -//------------------------------------------------------------------------- -/* -# -def get_stkvar(op, v): - """ - Get pointer to stack variable - @param op: reference to instruction operand - @param v: immediate value in the operand (usually op.addr) - @return: - - None on failure - - tuple(member_t, actval) - where actval: actual value used to fetch stack variable - """ - pass -# -*/ -PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - uint64 v; - if ( op == NULL || !PyW_GetNumber(py_v, &v) ) - Py_RETURN_NONE; - - sval_t actval; - member_t *member = get_stkvar(*op, sval_t(v), &actval); - if ( member == NULL ) - Py_RETURN_NONE; - - return Py_BuildValue("(O" PY_SFMT64 ")", - SWIG_NewPointerObj(SWIG_as_voidptr(member), SWIGTYPE_p_member_t, 0), - pyl_t(actval)); -} - -//------------------------------------------------------------------------- -/* -header: frame.hpp -# -def add_stkvar3(op, v, flags): - """ - Automatically add stack variable if doesn't exist - Processor modules should use ua_stkvar2() - @param op: reference to instruction operand - @param v: immediate value in the operand (usually op.addr) - @param flags: combination of STKVAR_... constants - @return: Boolean - """ - pass -# -*/ -bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - uint64 v; - return ( op == NULL || !PyW_GetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; -} - -//------------------------------------------------------------------------- -/* -header: frame.hpp -// Calculate offset of stack variable in the frame structure -// pfn - pointer to function (can't be NULL!) -// x - reference to instruction operand -// v - value of variable offset in the instruction -// returns: offset of stack variable in the frame structure (0..n) - -ea_t calc_frame_offset(func_t *pfn, const op_t *x, sval_t v); -*/ - -//------------------------------------------------------------------------- -/* -header: typeinf.hpp -# -def apply_type_to_stkarg(op, v, type, name): - """ - Apply type information to a stack variable - - @param op: reference to instruction operand - @param v: immediate value in the operand (usually op.addr) - @param type: type string. Retrieve from idc.ParseType("type string", flags)[1] - @param name: stack variable name - - @return: Boolean - """ - pass -# -*/ -bool py_apply_type_to_stkarg( - PyObject *py_op, - PyObject *py_uv, - PyObject *py_type, - const char *name) -{ - uint64 v; - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - if ( op == NULL || !PyW_GetNumber(py_uv, &v) || !PyString_Check(py_type)) - { - return false; - } - else - { - const type_t *t = (type_t *) PyString_AsString(py_type); - tinfo_t tif; - tif.deserialize(idati, &t); - borref_t br(py_op); - bool rc; - Py_BEGIN_ALLOW_THREADS; - rc = apply_tinfo_to_stkarg(*op, uval_t(v), tif, name); - Py_END_ALLOW_THREADS; - return rc; - } -} - -//------------------------------------------------------------------------- -/* -# -def OutImmChar(op, outflags = 0): - """ - Output operand value as a commented character constant - @param op: operand (of type op_t) - @return: None - """ - pass -# -*/ -static void py_OutImmChar(PyObject *x) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(x); - if ( op != NULL ) - OutImmChar(*op); -} - -//------------------------------------------------------------------------- -/* -# -def ua_stkvar2(op, outflags = 0): - """ - Create or modify a stack variable in the function frame. - Please check ua.hpp / ua_stkvar2() - @param op: operand (of type op_t) - @return: None - """ - pass -# -*/ -static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(x); - return op == NULL ? false : ua_stkvar2(*op, v, flags); -} - -//------------------------------------------------------------------------- -/* -# -def ua_add_off_drefs(op, type): - """ - Add xrefs for offset operand of the current instruction - Please check ua.hpp / ua_add_off_drefs() - @param op: operand (of type op_t) - @return: None - """ - pass -# -*/ -ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); -} - -//------------------------------------------------------------------------- -/* -# -def ua_add_off_drefs2(op, type, outf): - """ - Add xrefs for offset operand of the current instruction - Please check ua.hpp / ua_add_off_drefs2() - @return: ea_t - """ - pass -# -*/ -ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); -} - -//------------------------------------------------------------------------- -/* -# -def out_name_expr(op, ea, off): - """ - Output a name expression - @param op: operand (of type op_t) - @param ea: address of expression - @param off: the value of name expression. this parameter is used only to - check that the name expression will have the wanted value. - You may pass BADADDR for this parameter. - @return: true if the name expression has been produced - """ - pass -# -*/ -bool py_out_name_expr( - PyObject *py_op, - ea_t ea, - PyObject *py_off) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *op = op_t_get_clink(py_op); - uint64 v(0); - adiff_t off; - if ( PyW_GetNumber(py_off, &v) ) - off = adiff_t(v); - else - off = BADADDR; - - return op == NULL ? false : out_name_expr(*op, ea, off); -} - -//------------------------------------------------------------------------- -/* -# -def construct_macro(insn): - """ - See ua.hpp's construct_macro(). - """ - pass -# -*/ -bool py_construct_macro(bool enable, PyObject *build_macro) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - - if ( !PyCallable_Check(build_macro) ) - return false; - - static qstack macro_builders; - - macro_builders.push(newref_t(build_macro)); - struct ida_local lambda_t - { - static bool idaapi call_build_macro(insn_t &s, bool may_go_forward) - { - PyObject *py_builder = macro_builders.top().o; - newref_t pyres = PyObject_CallFunction( - py_builder, "O", - may_go_forward ? Py_True : Py_False); - PyW_ShowCbErr("build_macro"); - if ( pyres.o == NULL || pyres.o == Py_None ) - return false; - insn_t *_s = insn_t_get_clink(pyres.o); - if ( _s == NULL ) - return false; - s = *_s; - return true; - } - }; - bool res = construct_macro(enable, lambda_t::call_build_macro); - macro_builders.pop(); - return res; -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) - Py_RETURN_NONE; - - // Extract C link - insn_t *insn = (insn_t *)PyCObject_AsVoidPtr(py_insn_lnk); - - // Return a link to the operand - return PyCObject_FromVoidPtr(&insn->Operands[i], NULL); -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_create() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new insn_t(), NULL); -} - -//------------------------------------------------------------------------- -static PyObject *op_t_create() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(new op_t(), NULL); -} - -//------------------------------------------------------------------------- -static bool op_t_assign(PyObject *self, PyObject *other) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *lhs = op_t_get_clink(self); - op_t *rhs = op_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -static bool insn_t_assign(PyObject *self, PyObject *other) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *lhs = insn_t_get_clink(self); - insn_t *rhs = insn_t_get_clink(other); - if (lhs == NULL || rhs == NULL) - return false; - - *lhs = *rhs; - return true; -} - -//------------------------------------------------------------------------- -static bool op_t_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - - op_t *op = (op_t *)PyCObject_AsVoidPtr(py_obj); - delete op; - - return true; -} - -//------------------------------------------------------------------------- -static bool insn_t_destroy(PyObject *py_obj) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !PyCObject_Check(py_obj) ) - return false; - - delete (insn_t *)PyCObject_AsVoidPtr(py_obj); - return true; -} - -//------------------------------------------------------------------------- -// Returns a C link to the global 'cmd' variable -static PyObject *py_get_global_cmd_link() -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - return PyCObject_FromVoidPtr(&::cmd, NULL); -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_is_canon_insn(int itype) -{ - bool ok = ph.is_canon_insn(itype); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_canon_feature(int itype) -{ - uint32 v = ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0; - PYW_GIL_CHECK_LOCKED_SCOPE(); - return Py_BuildValue("I", v); -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_canon_mnem(int itype) -{ - bool ok = ph.is_canon_insn(itype); - PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( ok ) - return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static PyObject *insn_t_get_cs(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->cs); -} - -static void insn_t_set_cs(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - - uint64 v(0); - PyW_GetNumber(value, &v); - link->cs = ea_t(v); -} - -static PyObject *insn_t_get_ip(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->ip); -} - -static void insn_t_set_ip(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->ip = ea_t(v); -} - -static PyObject *insn_t_get_ea(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->ea); -} - -static void insn_t_set_ea(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->ea = ea_t(v); -} - -static PyObject *insn_t_get_itype(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->itype); -} - -static void insn_t_set_itype(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->itype = (uint16)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_size(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->size); -} - -static void insn_t_set_size(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->size = (uint16)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_auxpref(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->auxpref); -} - -static void insn_t_set_auxpref(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->auxpref = (uint16)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_segpref(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->segpref); -} - -static void insn_t_set_segpref(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->segpref = (char)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_insnpref(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->insnpref); -} - -static void insn_t_set_insnpref(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->insnpref = (char)PyInt_AsLong(value); -} - -static PyObject *insn_t_get_flags(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->flags); -} - -static void insn_t_set_flags(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - insn_t *link = insn_t_get_clink(self); - if ( link == NULL ) - return; - link->flags = (char)PyInt_AsLong(value); -} - -//------------------------------------------------------------------------- -static PyObject *op_t_get_n(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->n); -} - -static void op_t_set_n(PyObject *self, PyObject *value) -{ - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->n = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_type(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("B", link->type); -} - -static void op_t_set_type(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->type = (optype_t)PyInt_AsLong(value); -} - -static PyObject *op_t_get_offb(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->offb); -} - -static void op_t_set_offb(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->offb = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_offo(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->offo); -} - -static void op_t_set_offo(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->offo = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_flags(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("B", link->flags); -} - -static void op_t_set_flags(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->flags = (uchar)PyInt_AsLong(value); -} - -static PyObject *op_t_get_dtyp(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->dtyp); -} - -static void op_t_set_dtyp(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->dtyp = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_reg_phrase(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("H", link->reg); -} -static void op_t_set_reg_phrase(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->reg = (uint16)PyInt_AsLong(value); -} - -static PyObject *op_t_get_value(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->value); -} - -static void op_t_set_value(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->value = uval_t(v); -} - -static PyObject *op_t_get_addr(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->addr); -} - -static void op_t_set_addr(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->addr = ea_t(v); -} - -static PyObject *op_t_get_specval(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, (pyul_t)link->specval); -} - -static void op_t_set_specval(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - uint64 v(0); - PyW_GetNumber(value, &v); - link->specval = ea_t(v); -} - -static PyObject *op_t_get_specflag1(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag1); -} - -static void op_t_set_specflag1(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag1 = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_specflag2(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag2); -} - -static void op_t_set_specflag2(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag2 = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_specflag3(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag3); -} - -static void op_t_set_specflag3(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag3 = (char)PyInt_AsLong(value); -} - -static PyObject *op_t_get_specflag4(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("b", link->specflag4); -} - -static void op_t_set_specflag4(PyObject *self, PyObject *value) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - op_t *link = op_t_get_clink(self); - if ( link == NULL ) - return; - link->specflag4 = (char)PyInt_AsLong(value); -} - -// -%} - -%{ -// -//------------------------------------------------------------------------- -insn_t *insn_t_get_clink(PyObject *self) -{ - return (insn_t *)pyobj_get_clink(self); -} - -//------------------------------------------------------------------------- -op_t *op_t_get_clink(PyObject *self) -{ - return (op_t *)pyobj_get_clink(self); -} -// -%} - -%pythoncode %{ -# - -# ----------------------------------------------------------------------- -class op_t(py_clinked_object_t): - """Class representing operands""" - def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) - - def _create_clink(self): - return _idaapi.op_t_create() - - def _del_clink(self, lnk): - return _idaapi.op_t_destroy(lnk) - - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.op_t_assign(self, other) - - def __eq__(self, other): - """Checks if two register operands are equal by checking the register number and its dtype""" - return (self.reg == other.reg) and (self.dtyp == other.dtyp) - - def is_reg(self, r): - """Checks if the register operand is the given processor register""" - return self.type == o_reg and self.reg == r - - def has_reg(self, r): - """Checks if the operand accesses the given processor register""" - return self.reg == r.reg - - # - # Autogenerated - # - def __get_n__(self): - return _idaapi.op_t_get_n(self) - def __set_n__(self, v): - _idaapi.op_t_set_n(self, v) - def __get_type__(self): - return _idaapi.op_t_get_type(self) - def __set_type__(self, v): - _idaapi.op_t_set_type(self, v) - def __get_offb__(self): - return _idaapi.op_t_get_offb(self) - def __set_offb__(self, v): - _idaapi.op_t_set_offb(self, v) - def __get_offo__(self): - return _idaapi.op_t_get_offo(self) - def __set_offo__(self, v): - _idaapi.op_t_set_offo(self, v) - def __get_flags__(self): - return _idaapi.op_t_get_flags(self) - def __set_flags__(self, v): - _idaapi.op_t_set_flags(self, v) - def __get_dtyp__(self): - return _idaapi.op_t_get_dtyp(self) - def __set_dtyp__(self, v): - _idaapi.op_t_set_dtyp(self, v) - def __get_reg_phrase__(self): - return _idaapi.op_t_get_reg_phrase(self) - def __set_reg_phrase__(self, v): - _idaapi.op_t_set_reg_phrase(self, v) - def __get_value__(self): - return _idaapi.op_t_get_value(self) - def __set_value__(self, v): - _idaapi.op_t_set_value(self, v) - def __get_addr__(self): - return _idaapi.op_t_get_addr(self) - def __set_addr__(self, v): - _idaapi.op_t_set_addr(self, v) - def __get_specval__(self): - return _idaapi.op_t_get_specval(self) - def __set_specval__(self, v): - _idaapi.op_t_set_specval(self, v) - def __get_specflag1__(self): - return _idaapi.op_t_get_specflag1(self) - def __set_specflag1__(self, v): - _idaapi.op_t_set_specflag1(self, v) - def __get_specflag2__(self): - return _idaapi.op_t_get_specflag2(self) - def __set_specflag2__(self, v): - _idaapi.op_t_set_specflag2(self, v) - def __get_specflag3__(self): - return _idaapi.op_t_get_specflag3(self) - def __set_specflag3__(self, v): - _idaapi.op_t_set_specflag3(self, v) - def __get_specflag4__(self): - return _idaapi.op_t_get_specflag4(self) - def __set_specflag4__(self, v): - _idaapi.op_t_set_specflag4(self, v) - - n = property(__get_n__, __set_n__) - type = property(__get_type__, __set_type__) - offb = property(__get_offb__, __set_offb__) - offo = property(__get_offo__, __set_offo__) - flags = property(__get_flags__, __set_flags__) - dtyp = property(__get_dtyp__, __set_dtyp__) - reg = property(__get_reg_phrase__, __set_reg_phrase__) - phrase = property(__get_reg_phrase__, __set_reg_phrase__) - value = property(__get_value__, __set_value__) - addr = property(__get_addr__, __set_addr__) - specval = property(__get_specval__, __set_specval__) - specflag1 = property(__get_specflag1__, __set_specflag1__) - specflag2 = property(__get_specflag2__, __set_specflag2__) - specflag3 = property(__get_specflag3__, __set_specflag3__) - specflag4 = property(__get_specflag4__, __set_specflag4__) - -# ----------------------------------------------------------------------- -class insn_t(py_clinked_object_t): - """Class representing instructions""" - def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) - - # Create linked operands - self.Operands = [] - for i in xrange(0, UA_MAXOP): - self.Operands.append(op_t(insn_t_get_op_link(self.clink, i))) - - # Convenience operand reference objects - self.Op1 = self.Operands[0] - self.Op2 = self.Operands[1] - self.Op3 = self.Operands[2] - self.Op4 = self.Operands[3] - self.Op5 = self.Operands[4] - self.Op6 = self.Operands[5] - - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.insn_t_assign(self, other) - -# -# def copy(self): -# """Returns a new copy of this class""" -# pass -# - - def _create_clink(self): - return _idaapi.insn_t_create() - - - def _del_clink(self, lnk): - return _idaapi.insn_t_destroy(lnk) - - - def __iter__(self): - return (self.Operands[idx] for idx in xrange(0, UA_MAXOP)) - - - def __getitem__(self, idx): - """ - Operands can be accessed directly as indexes - @return op_t: Returns an operand of type op_t - """ - if idx >= UA_MAXOP: - raise KeyError - else: - return self.Operands[idx] - - def is_macro(self): - return self.flags & INSN_MACRO != 0 - - def is_canon_insn(self): - return _idaapi.insn_t_is_canon_insn(self.itype) - - def get_canon_feature(self): - return _idaapi.insn_t_get_canon_feature(self.itype) - - def get_canon_mnem(self): - return _idaapi.insn_t_get_canon_mnem(self.itype) - - # - # Autogenerated - # - def __get_cs__(self): - return _idaapi.insn_t_get_cs(self) - def __set_cs__(self, v): - _idaapi.insn_t_set_cs(self, v) - def __get_ip__(self): - return _idaapi.insn_t_get_ip(self) - def __set_ip__(self, v): - _idaapi.insn_t_set_ip(self, v) - def __get_ea__(self): - return _idaapi.insn_t_get_ea(self) - def __set_ea__(self, v): - _idaapi.insn_t_set_ea(self, v) - def __get_itype__(self): - return _idaapi.insn_t_get_itype(self) - def __set_itype__(self, v): - _idaapi.insn_t_set_itype(self, v) - def __get_size__(self): - return _idaapi.insn_t_get_size(self) - def __set_size__(self, v): - _idaapi.insn_t_set_size(self, v) - def __get_auxpref__(self): - return _idaapi.insn_t_get_auxpref(self) - def __set_auxpref__(self, v): - _idaapi.insn_t_set_auxpref(self, v) - def __get_segpref__(self): - return _idaapi.insn_t_get_segpref(self) - def __set_segpref__(self, v): - _idaapi.insn_t_set_segpref(self, v) - def __get_insnpref__(self): - return _idaapi.insn_t_get_insnpref(self) - def __set_insnpref__(self, v): - _idaapi.insn_t_set_insnpref(self, v) - def __get_flags__(self): - return _idaapi.insn_t_get_flags(self) - def __set_flags__(self, v): - _idaapi.insn_t_set_flags(self, v) - - cs = property(__get_cs__, __set_cs__) - ip = property(__get_ip__, __set_ip__) - ea = property(__get_ea__, __set_ea__) - itype = property(__get_itype__, __set_itype__) - size = property(__get_size__, __set_size__) - auxpref = property(__get_auxpref__, __set_auxpref__) - segpref = property(__get_segpref__, __set_segpref__) - insnpref = property(__get_insnpref__, __set_insnpref__) - flags = property(__get_flags__, __set_flags__) - - -#---------------------------------------------------------------------------- -# P R O C E S S O R M O D U L E S C O N S T A N T S -#---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------- -# processor_t related constants - -CUSTOM_CMD_ITYPE = 0x8000 -REG_SPOIL = 0x80000000 - -REAL_ERROR_FORMAT = -1 # not supported format for current .idp -REAL_ERROR_RANGE = -2 # number too big (small) for store (mem NOT modifyed) -REAL_ERROR_BADDATA = -3 # illegal real data for load (IEEE data not filled) - -# -# Check whether the operand is relative to stack pointer or frame pointer. -# This function is used to determine how to output a stack variable -# This function may be absent. If it is absent, then all operands -# are sp based by default. -# Define this function only if some stack references use frame pointer -# instead of stack pointer. -# returns flags: -OP_FP_BASED = 0x00000000 # operand is FP based -OP_SP_BASED = 0x00000001 # operand is SP based -OP_SP_ADD = 0x00000000 # operand value is added to the pointer -OP_SP_SUB = 0x00000002 # operand value is substracted from the pointer - -# processor_t.id -PLFM_386 = 0 # Intel 80x86 -PLFM_Z80 = 1 # 8085, Z80 -PLFM_I860 = 2 # Intel 860 -PLFM_8051 = 3 # 8051 -PLFM_TMS = 4 # Texas Instruments TMS320C5x -PLFM_6502 = 5 # 6502 -PLFM_PDP = 6 # PDP11 -PLFM_68K = 7 # Motoroal 680x0 -PLFM_JAVA = 8 # Java -PLFM_6800 = 9 # Motorola 68xx -PLFM_ST7 = 10 # SGS-Thomson ST7 -PLFM_MC6812 = 11 # Motorola 68HC12 -PLFM_MIPS = 12 # MIPS -PLFM_ARM = 13 # Advanced RISC Machines -PLFM_TMSC6 = 14 # Texas Instruments TMS320C6x -PLFM_PPC = 15 # PowerPC -PLFM_80196 = 16 # Intel 80196 -PLFM_Z8 = 17 # Z8 -PLFM_SH = 18 # Renesas (formerly Hitachi) SuperH -PLFM_NET = 19 # Microsoft Visual Studio.Net -PLFM_AVR = 20 # Atmel 8-bit RISC processor(s) -PLFM_H8 = 21 # Hitachi H8/300, H8/2000 -PLFM_PIC = 22 # Microchip's PIC -PLFM_SPARC = 23 # SPARC -PLFM_ALPHA = 24 # DEC Alpha -PLFM_HPPA = 25 # Hewlett-Packard PA-RISC -PLFM_H8500 = 26 # Hitachi H8/500 -PLFM_TRICORE = 27 # Tasking Tricore -PLFM_DSP56K = 28 # Motorola DSP5600x -PLFM_C166 = 29 # Siemens C166 family -PLFM_ST20 = 30 # SGS-Thomson ST20 -PLFM_IA64 = 31 # Intel Itanium IA64 -PLFM_I960 = 32 # Intel 960 -PLFM_F2MC = 33 # Fujistu F2MC-16 -PLFM_TMS320C54 = 34 # Texas Instruments TMS320C54xx -PLFM_TMS320C55 = 35 # Texas Instruments TMS320C55xx -PLFM_TRIMEDIA = 36 # Trimedia -PLFM_M32R = 37 # Mitsubishi 32bit RISC -PLFM_NEC_78K0 = 38 # NEC 78K0 -PLFM_NEC_78K0S = 39 # NEC 78K0S -PLFM_M740 = 40 # Mitsubishi 8bit -PLFM_M7700 = 41 # Mitsubishi 16bit -PLFM_ST9 = 42 # ST9+ -PLFM_FR = 43 # Fujitsu FR Family -PLFM_MC6816 = 44 # Motorola 68HC16 -PLFM_M7900 = 45 # Mitsubishi 7900 -PLFM_TMS320C3 = 46 # Texas Instruments TMS320C3 -PLFM_KR1878 = 47 # Angstrem KR1878 -PLFM_AD218X = 48 # Analog Devices ADSP 218X -PLFM_OAKDSP = 49 # Atmel OAK DSP -PLFM_TLCS900 = 50 # Toshiba TLCS-900 -PLFM_C39 = 51 # Rockwell C39 -PLFM_CR16 = 52 # NSC CR16 -PLFM_MN102L00 = 53 # Panasonic MN10200 -PLFM_TMS320C1X = 54 # Texas Instruments TMS320C1x -PLFM_NEC_V850X = 55 # NEC V850 and V850ES/E1/E2 -PLFM_SCR_ADPT = 56 # Processor module adapter for processor modules written in scripting languages -PLFM_EBC = 57 # EFI Bytecode -PLFM_MSP430 = 58 # Texas Instruments MSP430 -PLFM_SPU = 59 # Cell Broadband Engine Synergistic Processor Unit - -# -# processor_t.flag -# -PR_SEGS = 0x000001 # has segment registers? -PR_USE32 = 0x000002 # supports 32-bit addressing? -PR_DEFSEG32 = 0x000004 # segments are 32-bit by default -PR_RNAMESOK = 0x000008 # allow to user register names for location names -PR_ADJSEGS = 0x000020 # IDA may adjust segments moving their starting/ending addresses. -PR_DEFNUM = 0x0000C0 # default number representation: -PRN_HEX = 0x000000 # hex -PRN_OCT = 0x000040 # octal -PRN_DEC = 0x000080 # decimal -PRN_BIN = 0x0000C0 # binary -PR_WORD_INS = 0x000100 # instruction codes are grouped 2bytes in binrary line prefix -PR_NOCHANGE = 0x000200 # The user can't change segments and code/data attributes (display only) -PR_ASSEMBLE = 0x000400 # Module has a built-in assembler and understands IDP_ASSEMBLE -PR_ALIGN = 0x000800 # All data items should be aligned properly -PR_TYPEINFO = 0x001000 # the processor module supports - # type information callbacks - # ALL OF THEM SHOULD BE IMPLEMENTED! - # (the ones >= decorate_name) -PR_USE64 = 0x002000 # supports 64-bit addressing? -PR_SGROTHER = 0x004000 # the segment registers don't contain - # the segment selectors, something else -PR_STACK_UP = 0x008000 # the stack grows up -PR_BINMEM = 0x010000 # the processor module provides correct - # segmentation for binary files - # (i.e. it creates additional segments) - # The kernel will not ask the user - # to specify the RAM/ROM sizes -PR_SEGTRANS = 0x020000 # the processor module supports - # the segment translation feature - # (it means it calculates the code - # addresses using the codeSeg() function) -PR_CHK_XREF = 0x040000 # don't allow near xrefs between segments - # with different bases -PR_NO_SEGMOVE = 0x080000 # the processor module doesn't support move_segm() - # (i.e. the user can't move segments) -PR_FULL_HIFXP = 0x100000 # REF_VHIGH operand value contains full operand - # not only the high bits. Meaningful if ph.high_fixup_bits -PR_USE_ARG_TYPES = 0x200000 # use ph.use_arg_types callback -PR_SCALE_STKVARS = 0x400000 # use ph.get_stkvar_scale callback -PR_DELAYED = 0x800000 # has delayed jumps and calls -PR_ALIGN_INSN = 0x1000000 # allow ida to create alignment instructions - # arbirtrarily. Since these instructions - # might lead to other wrong instructions - # and spoil the listing, IDA does not create - # them by default anymore -PR_PURGING = 0x2000000 # there are calling conventions which may - # purge bytes from the stack -PR_CNDINSNS = 0x4000000 # has conditional instructions -PR_USE_TBYTE = 0x8000000 # BTMT_SPECFLT means _TBYTE type -PR_DEFSEG64 = 0x10000000 # segments are 64-bit by default - - -# ---------------------------------------------------------------------- -# -# Misc constants -# -UA_MAXOP = 6 -"""The maximum number of operands in the insn_t structure""" - -# Create 'cmd' into the global scope -cmd = insn_t(_idaapi.py_get_global_cmd_link()) -"""cmd is a global variable of type insn_t. It is contains information about the last decoded instruction. -This variable is also filled by processor modules when they decode instructions.""" - -# ---------------------------------------------------------------------- -# instruc_t related constants - -# -# instruc_t.feature -# -CF_STOP = 0x00001 # Instruction doesn't pass execution to the next instruction -CF_CALL = 0x00002 # CALL instruction (should make a procedure here) -CF_CHG1 = 0x00004 # The instruction modifies the first operand -CF_CHG2 = 0x00008 # The instruction modifies the second operand -CF_CHG3 = 0x00010 # The instruction modifies the third operand -CF_CHG4 = 0x00020 # The instruction modifies 4 operand -CF_CHG5 = 0x00040 # The instruction modifies 5 operand -CF_CHG6 = 0x00080 # The instruction modifies 6 operand -CF_USE1 = 0x00100 # The instruction uses value of the first operand -CF_USE2 = 0x00200 # The instruction uses value of the second operand -CF_USE3 = 0x00400 # The instruction uses value of the third operand -CF_USE4 = 0x00800 # The instruction uses value of the 4 operand -CF_USE5 = 0x01000 # The instruction uses value of the 5 operand -CF_USE6 = 0x02000 # The instruction uses value of the 6 operand -CF_JUMP = 0x04000 # The instruction passes execution using indirect jump or call (thus needs additional analysis) -CF_SHFT = 0x08000 # Bit-shift instruction (shl,shr...) -CF_HLL = 0x10000 # Instruction may be present in a high level language function. - -# ---------------------------------------------------------------------- -# op_t related constants - -# -# op_t.type -# Description Data field -o_void = 0 # No Operand ---------- -o_reg = 1 # General Register (al,ax,es,ds...) reg -o_mem = 2 # Direct Memory Reference (DATA) addr -o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase -o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr -o_imm = 5 # Immediate Value value -o_far = 6 # Immediate Far Address (CODE) addr -o_near = 7 # Immediate Near Address (CODE) addr -o_idpspec0 = 8 # Processor specific type -o_idpspec1 = 9 # Processor specific type -o_idpspec2 = 10 # Processor specific type -o_idpspec3 = 11 # Processor specific type -o_idpspec4 = 12 # Processor specific type -o_idpspec5 = 13 # Processor specific type - # There can be more processor specific types - -# -# op_t.dtyp -# -dt_byte = 0 # 8 bit -dt_word = 1 # 16 bit -dt_dword = 2 # 32 bit -dt_float = 3 # 4 byte -dt_double = 4 # 8 byte -dt_tbyte = 5 # variable size (ph.tbyte_size) -dt_packreal = 6 # packed real format for mc68040 -dt_qword = 7 # 64 bit -dt_byte16 = 8 # 128 bit -dt_code = 9 # ptr to code (not used?) -dt_void = 10 # none -dt_fword = 11 # 48 bit -dt_bitfild = 12 # bit field (mc680x0) -dt_string = 13 # pointer to asciiz string -dt_unicode = 14 # pointer to unicode string -dt_3byte = 15 # 3-byte data -dt_ldbl = 16 # long double (which may be different from tbyte) -dt_byte32 = 17 # 256 bit -dt_byte64 = 18 # 512 bit - -# -# op_t.flags -# -OF_NO_BASE_DISP = 0x80 # o_displ: base displacement doesn't exist meaningful only for o_displ type if set, base displacement (x.addr) doesn't exist. -OF_OUTER_DISP = 0x40 # o_displ: outer displacement exists meaningful only for o_displ type if set, outer displacement (x.value) exists. -PACK_FORM_DEF = 0x20 # !o_reg + dt_packreal: packed factor defined -OF_NUMBER = 0x10 # can be output as number only if set, the operand can be converted to a number only -OF_SHOW = 0x08 # should the operand be displayed? if clear, the operand is hidden and should not be displayed - -# -# insn_t.flags -# -INSN_MACRO = 0x01 # macro instruction -INSN_MODMAC = 0x02 # macros: may modify the database to make room for the macro insn - -# -# Set IDP options constants -# -IDPOPT_STR = 1 # string constant -IDPOPT_NUM = 2 # number -IDPOPT_BIT = 3 # bit, yes/no -IDPOPT_FLT = 4 # float -IDPOPT_I64 = 5 # 64bit number - -IDPOPT_OK = 0 # ok -IDPOPT_BADKEY = 1 # illegal keyword -IDPOPT_BADTYPE = 2 # illegal type of value -IDPOPT_BADVALUE = 3 # illegal value (bad range, for example) - -# ---------------------------------------------------------------------- -class processor_t(pyidc_opaque_object_t): - """Base class for all processor module scripts""" - def __init__(self): - # Take a reference to 'cmd' - self.cmd = cmd - - def get_idpdesc(self): - """ - This function must be present and should return the list of - short processor names similar to the one in ph.psnames. - This method can be overridden to return to the kernel a different IDP description. - """ - return self.plnames[0] + ':' + ':'.join(self.psnames) - - def get_uFlag(self): - """Use this utility function to retrieve the 'uFlag' global variable""" - return _idaapi.cvar.uFlag - - def get_auxpref(self): - """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() - -# -%} +%ignore insn_t; +%ignore op_t; +%ignore cmd; +%ignore ua_out; +%ignore showAsChar; +%ignore out_real; +%ignore init_output_buffer; +%ignore term_output_buffer; +%ignore OutValue; +%ignore OutImmChar; +%ignore out_name_expr; +%ignore ua_stkvar2; +%ignore ua_add_off_drefs; +%ignore ua_add_off_drefs2; +%ignore out_snprintf; +%ignore set_output_ptr; +%ignore get_output_ptr; +%ignore out_insert; +%ignore get_immval; +%ignore get_spoiled_reg; +%ignore decode_preceding_insn; +%ignore init_ua; +%ignore term_ua; +%ignore term_uaterm_ua; +%ignore get_equal_items; +%ignore get_equal_itemsget_equal_items; +%ignore ua_use_fixup; + +%ignore get_immval; +%ignore ua_stkvar; + +%ignore construct_macro; +%rename (construct_macro) py_construct_macro; + +%include "ua.hpp" + +%rename (init_output_buffer) py_init_output_buffer; +%rename (term_output_buffer) py_term_output_buffer; +%rename (OutValue) py_OutValue; +%rename (OutImmChar) py_OutImmChar; +%rename (out_name_expr) py_out_name_expr; +%rename (ua_stkvar2) py_ua_stkvar2; +%rename (ua_add_off_drefs) py_ua_add_off_drefs; +%rename (ua_add_off_drefs2) py_ua_add_off_drefs2; +%rename (decode_preceding_insn) py_decode_preceding_insn; + +%inline %{ +// + +//------------------------------------------------------------------------- +/* +# +def init_output_buffer(size = MAXSTR): + """ + This function initialize an output buffer with the given size. + It should be called before using any out_xxxx() functions. + @return: It returns a string. This string should then be passed to MakeLine(). + This function could return None if it failed to create a buffer with the given size. + """ + pass +# +*/ +PyObject *py_init_output_buffer(size_t size = MAXSTR) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Let Python allocate a writable string buffer for us + PyObject *py_str = PyString_FromStringAndSize(NULL, size); + if ( py_str == NULL ) + Py_RETURN_NONE; + + init_output_buffer(PyString_AsString(py_str), size); + return py_str; +} + +//------------------------------------------------------------------------- +/* +# +def term_output_buffer(): + """Use this function to terminate an output buffer.""" + pass +# +*/ +void py_term_output_buffer() +{ + term_output_buffer(); +} + +//------------------------------------------------------------------------- +/* +# +def decode_preceding_insn(ea): + """ + Decodes the preceding instruction. Please check ua.hpp / decode_preceding_insn() + @param ea: current ea + @return: tuple(preceeding_ea or BADADDR, farref = Boolean) + """ + pass +# +*/ +PyObject *py_decode_preceding_insn(ea_t ea) +{ + bool farref; + ea_t r = decode_preceding_insn(ea, &farref); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); +} + +//------------------------------------------------------------------------- +/* +# +def OutValue(op, outflags = 0): + """ + Output immediate value + @param op: operand (of type op_t) + @return: flags of the output value + -1: value is output with COLOR_ERROR + 0: value is output as a number or character or segment + """ + pass +# +*/ +flags_t py_OutValue(PyObject *x, int outflags=0) +{ + op_t *op = op_t_get_clink(x); + if ( op == NULL ) + return 0; + + return OutValue(*op, outflags); +} + +//------------------------------------------------------------------------- +/* +# +def get_stkvar(op, v): + """ + Get pointer to stack variable + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @return: + - None on failure + - tuple(member_t, actval) + where actval: actual value used to fetch stack variable + """ + pass +# +*/ +PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + uint64 v; + if ( op == NULL || !PyW_GetNumber(py_v, &v) ) + Py_RETURN_NONE; + + sval_t actval; + member_t *member = get_stkvar(*op, sval_t(v), &actval); + if ( member == NULL ) + Py_RETURN_NONE; + + return Py_BuildValue("(O" PY_SFMT64 ")", + SWIG_NewPointerObj(SWIG_as_voidptr(member), SWIGTYPE_p_member_t, 0), + pyl_t(actval)); +} + +//------------------------------------------------------------------------- +/* +header: frame.hpp +# +def add_stkvar3(op, v, flags): + """ + Automatically add stack variable if doesn't exist + Processor modules should use ua_stkvar2() + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @param flags: combination of STKVAR_... constants + @return: Boolean + """ + pass +# +*/ +bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + uint64 v; + return ( op == NULL || !PyW_GetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; +} + +//------------------------------------------------------------------------- +/* +header: typeinf.hpp +# +def apply_type_to_stkarg(op, v, type, name): + """ + Apply type information to a stack variable + + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @param type: type string. Retrieve from idc.ParseType("type string", flags)[1] + @param name: stack variable name + + @return: Boolean + """ + pass +# +*/ +bool py_apply_type_to_stkarg( + PyObject *py_op, + PyObject *py_uv, + PyObject *py_type, + const char *name) +{ + uint64 v; + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + if ( op == NULL || !PyW_GetNumber(py_uv, &v) || !PyString_Check(py_type)) + { + return false; + } + else + { + const type_t *t = (type_t *) PyString_AsString(py_type); + tinfo_t tif; + tif.deserialize(idati, &t); + borref_t br(py_op); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = apply_tinfo_to_stkarg(*op, uval_t(v), tif, name); + Py_END_ALLOW_THREADS; + return rc; + } +} + +//------------------------------------------------------------------------- +/* +# +def OutImmChar(op, outflags = 0): + """ + Output operand value as a commented character constant + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +static void py_OutImmChar(PyObject *x) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(x); + if ( op != NULL ) + OutImmChar(*op); +} + +//------------------------------------------------------------------------- +/* +# +def ua_stkvar2(op, outflags = 0): + """ + Create or modify a stack variable in the function frame. + Please check ua.hpp / ua_stkvar2() + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(x); + return op == NULL ? false : ua_stkvar2(*op, v, flags); +} + +//------------------------------------------------------------------------- +/* +# +def ua_add_off_drefs(op, type): + """ + Add xrefs for offset operand of the current instruction + Please check ua.hpp / ua_add_off_drefs() + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); +} + +//------------------------------------------------------------------------- +/* +# +def ua_add_off_drefs2(op, type, outf): + """ + Add xrefs for offset operand of the current instruction + Please check ua.hpp / ua_add_off_drefs2() + @return: ea_t + """ + pass +# +*/ +ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); +} + +//------------------------------------------------------------------------- +/* +# +def out_name_expr(op, ea, off): + """ + Output a name expression + @param op: operand (of type op_t) + @param ea: address of expression + @param off: the value of name expression. this parameter is used only to + check that the name expression will have the wanted value. + You may pass BADADDR for this parameter. + @return: true if the name expression has been produced + """ + pass +# +*/ +bool py_out_name_expr( + PyObject *py_op, + ea_t ea, + PyObject *py_off) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *op = op_t_get_clink(py_op); + uint64 v(0); + adiff_t off; + if ( PyW_GetNumber(py_off, &v) ) + off = adiff_t(v); + else + off = BADADDR; + + return op == NULL ? false : out_name_expr(*op, ea, off); +} + +//------------------------------------------------------------------------- +/* +# +def construct_macro(insn): + """ + See ua.hpp's construct_macro(). + """ + pass +# +*/ +bool py_construct_macro(bool enable, PyObject *build_macro) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !PyCallable_Check(build_macro) ) + return false; + + static qstack macro_builders; + + macro_builders.push(newref_t(build_macro)); + struct ida_local lambda_t + { + static bool idaapi call_build_macro(insn_t &s, bool may_go_forward) + { + PyObject *py_builder = macro_builders.top().o; + newref_t pyres = PyObject_CallFunction( + py_builder, "O", + may_go_forward ? Py_True : Py_False); + PyW_ShowCbErr("build_macro"); + if ( pyres.o == NULL || pyres.o == Py_None ) + return false; + insn_t *_s = insn_t_get_clink(pyres.o); + if ( _s == NULL ) + return false; + s = *_s; + return true; + } + }; + bool res = construct_macro(enable, lambda_t::call_build_macro); + macro_builders.pop(); + return res; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) + Py_RETURN_NONE; + + // Extract C link + insn_t *insn = (insn_t *)PyCObject_AsVoidPtr(py_insn_lnk); + + // Return a link to the operand + return PyCObject_FromVoidPtr(&insn->Operands[i], NULL); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_create() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new insn_t(), NULL); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_create() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(new op_t(), NULL); +} + +//------------------------------------------------------------------------- +static bool op_t_assign(PyObject *self, PyObject *other) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *lhs = op_t_get_clink(self); + op_t *rhs = op_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool insn_t_assign(PyObject *self, PyObject *other) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *lhs = insn_t_get_clink(self); + insn_t *rhs = insn_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool op_t_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + + op_t *op = (op_t *)PyCObject_AsVoidPtr(py_obj); + delete op; + + return true; +} + +//------------------------------------------------------------------------- +static bool insn_t_destroy(PyObject *py_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyCObject_Check(py_obj) ) + return false; + + delete (insn_t *)PyCObject_AsVoidPtr(py_obj); + return true; +} + +//------------------------------------------------------------------------- +// Returns a C link to the global 'cmd' variable +static PyObject *py_get_global_cmd_link() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return PyCObject_FromVoidPtr(&::cmd, NULL); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_is_canon_insn(int itype) +{ + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_canon_feature(int itype) +{ + uint32 v = ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("I", v); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_canon_mnem(int itype) +{ + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_cs(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->cs); +} + +static void insn_t_set_cs(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + + uint64 v(0); + PyW_GetNumber(value, &v); + link->cs = ea_t(v); +} + +static PyObject *insn_t_get_ip(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ip); +} + +static void insn_t_set_ip(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->ip = ea_t(v); +} + +static PyObject *insn_t_get_ea(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ea); +} + +static void insn_t_set_ea(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->ea = ea_t(v); +} + +static PyObject *insn_t_get_itype(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->itype); +} + +static void insn_t_set_itype(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->itype = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_size(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->size); +} + +static void insn_t_set_size(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->size = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_auxpref(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->auxpref); +} + +static void insn_t_set_auxpref(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->auxpref = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_segpref(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->segpref); +} + +static void insn_t_set_segpref(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->segpref = (char)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_insnpref(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->insnpref); +} + +static void insn_t_set_insnpref(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->insnpref = (char)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_flags(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->flags); +} + +static void insn_t_set_flags(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (char)PyInt_AsLong(value); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_get_n(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->n); +} + +static void op_t_set_n(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->n = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_type(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("B", link->type); +} + +static void op_t_set_type(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->type = (optype_t)PyInt_AsLong(value); +} + +static PyObject *op_t_get_offb(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->offb); +} + +static void op_t_set_offb(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->offb = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_offo(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->offo); +} + +static void op_t_set_offo(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->offo = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_flags(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("B", link->flags); +} + +static void op_t_set_flags(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (uchar)PyInt_AsLong(value); +} + +static PyObject *op_t_get_dtyp(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->dtyp); +} + +static void op_t_set_dtyp(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->dtyp = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_reg_phrase(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->reg); +} +static void op_t_set_reg_phrase(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->reg = (uint16)PyInt_AsLong(value); +} + +static PyObject *op_t_get_value(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->value); +} + +static void op_t_set_value(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->value = uval_t(v); +} + +static PyObject *op_t_get_addr(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->addr); +} + +static void op_t_set_addr(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->addr = ea_t(v); +} + +static PyObject *op_t_get_specval(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->specval); +} + +static void op_t_set_specval(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyW_GetNumber(value, &v); + link->specval = ea_t(v); +} + +static PyObject *op_t_get_specflag1(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag1); +} + +static void op_t_set_specflag1(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag1 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag2(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag2); +} + +static void op_t_set_specflag2(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag2 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag3(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag3); +} + +static void op_t_set_specflag3(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag3 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag4(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag4); +} + +static void op_t_set_specflag4(PyObject *self, PyObject *value) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag4 = (char)PyInt_AsLong(value); +} + +// +%} + +%{ +// +//------------------------------------------------------------------------- +insn_t *insn_t_get_clink(PyObject *self) +{ + return (insn_t *)pyobj_get_clink(self); +} + +//------------------------------------------------------------------------- +op_t *op_t_get_clink(PyObject *self) +{ + return (op_t *)pyobj_get_clink(self); +} +// +%} + +%pythoncode %{ +# + +# ----------------------------------------------------------------------- +class op_t(py_clinked_object_t): + """Class representing operands""" + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + def _create_clink(self): + return _idaapi.op_t_create() + + def _del_clink(self, lnk): + return _idaapi.op_t_destroy(lnk) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.op_t_assign(self, other) + + def __eq__(self, other): + """Checks if two register operands are equal by checking the register number and its dtype""" + return (self.reg == other.reg) and (self.dtyp == other.dtyp) + + def is_reg(self, r): + """Checks if the register operand is the given processor register""" + return self.type == o_reg and self.reg == r + + def has_reg(self, r): + """Checks if the operand accesses the given processor register""" + return self.reg == r.reg + + # + # Autogenerated + # + def __get_n__(self): + return _idaapi.op_t_get_n(self) + def __set_n__(self, v): + _idaapi.op_t_set_n(self, v) + def __get_type__(self): + return _idaapi.op_t_get_type(self) + def __set_type__(self, v): + _idaapi.op_t_set_type(self, v) + def __get_offb__(self): + return _idaapi.op_t_get_offb(self) + def __set_offb__(self, v): + _idaapi.op_t_set_offb(self, v) + def __get_offo__(self): + return _idaapi.op_t_get_offo(self) + def __set_offo__(self, v): + _idaapi.op_t_set_offo(self, v) + def __get_flags__(self): + return _idaapi.op_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.op_t_set_flags(self, v) + def __get_dtyp__(self): + return _idaapi.op_t_get_dtyp(self) + def __set_dtyp__(self, v): + _idaapi.op_t_set_dtyp(self, v) + def __get_reg_phrase__(self): + return _idaapi.op_t_get_reg_phrase(self) + def __set_reg_phrase__(self, v): + _idaapi.op_t_set_reg_phrase(self, v) + def __get_value__(self): + return _idaapi.op_t_get_value(self) + def __set_value__(self, v): + _idaapi.op_t_set_value(self, v) + def __get_addr__(self): + return _idaapi.op_t_get_addr(self) + def __set_addr__(self, v): + _idaapi.op_t_set_addr(self, v) + def __get_specval__(self): + return _idaapi.op_t_get_specval(self) + def __set_specval__(self, v): + _idaapi.op_t_set_specval(self, v) + def __get_specflag1__(self): + return _idaapi.op_t_get_specflag1(self) + def __set_specflag1__(self, v): + _idaapi.op_t_set_specflag1(self, v) + def __get_specflag2__(self): + return _idaapi.op_t_get_specflag2(self) + def __set_specflag2__(self, v): + _idaapi.op_t_set_specflag2(self, v) + def __get_specflag3__(self): + return _idaapi.op_t_get_specflag3(self) + def __set_specflag3__(self, v): + _idaapi.op_t_set_specflag3(self, v) + def __get_specflag4__(self): + return _idaapi.op_t_get_specflag4(self) + def __set_specflag4__(self, v): + _idaapi.op_t_set_specflag4(self, v) + + n = property(__get_n__, __set_n__) + type = property(__get_type__, __set_type__) + offb = property(__get_offb__, __set_offb__) + offo = property(__get_offo__, __set_offo__) + flags = property(__get_flags__, __set_flags__) + dtyp = property(__get_dtyp__, __set_dtyp__) + reg = property(__get_reg_phrase__, __set_reg_phrase__) + phrase = property(__get_reg_phrase__, __set_reg_phrase__) + value = property(__get_value__, __set_value__) + addr = property(__get_addr__, __set_addr__) + specval = property(__get_specval__, __set_specval__) + specflag1 = property(__get_specflag1__, __set_specflag1__) + specflag2 = property(__get_specflag2__, __set_specflag2__) + specflag3 = property(__get_specflag3__, __set_specflag3__) + specflag4 = property(__get_specflag4__, __set_specflag4__) + +# ----------------------------------------------------------------------- +class insn_t(py_clinked_object_t): + """Class representing instructions""" + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + # Create linked operands + self.Operands = [] + for i in xrange(0, UA_MAXOP): + self.Operands.append(op_t(insn_t_get_op_link(self.clink, i))) + + # Convenience operand reference objects + self.Op1 = self.Operands[0] + self.Op2 = self.Operands[1] + self.Op3 = self.Operands[2] + self.Op4 = self.Operands[3] + self.Op5 = self.Operands[4] + self.Op6 = self.Operands[5] + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.insn_t_assign(self, other) + +# +# def copy(self): +# """Returns a new copy of this class""" +# pass +# + + def _create_clink(self): + return _idaapi.insn_t_create() + + + def _del_clink(self, lnk): + return _idaapi.insn_t_destroy(lnk) + + + def __iter__(self): + return (self.Operands[idx] for idx in xrange(0, UA_MAXOP)) + + + def __getitem__(self, idx): + """ + Operands can be accessed directly as indexes + @return op_t: Returns an operand of type op_t + """ + if idx >= UA_MAXOP: + raise KeyError + else: + return self.Operands[idx] + + def is_macro(self): + return self.flags & INSN_MACRO != 0 + + def is_canon_insn(self): + return _idaapi.insn_t_is_canon_insn(self.itype) + + def get_canon_feature(self): + return _idaapi.insn_t_get_canon_feature(self.itype) + + def get_canon_mnem(self): + return _idaapi.insn_t_get_canon_mnem(self.itype) + + # + # Autogenerated + # + def __get_cs__(self): + return _idaapi.insn_t_get_cs(self) + def __set_cs__(self, v): + _idaapi.insn_t_set_cs(self, v) + def __get_ip__(self): + return _idaapi.insn_t_get_ip(self) + def __set_ip__(self, v): + _idaapi.insn_t_set_ip(self, v) + def __get_ea__(self): + return _idaapi.insn_t_get_ea(self) + def __set_ea__(self, v): + _idaapi.insn_t_set_ea(self, v) + def __get_itype__(self): + return _idaapi.insn_t_get_itype(self) + def __set_itype__(self, v): + _idaapi.insn_t_set_itype(self, v) + def __get_size__(self): + return _idaapi.insn_t_get_size(self) + def __set_size__(self, v): + _idaapi.insn_t_set_size(self, v) + def __get_auxpref__(self): + return _idaapi.insn_t_get_auxpref(self) + def __set_auxpref__(self, v): + _idaapi.insn_t_set_auxpref(self, v) + def __get_segpref__(self): + return _idaapi.insn_t_get_segpref(self) + def __set_segpref__(self, v): + _idaapi.insn_t_set_segpref(self, v) + def __get_insnpref__(self): + return _idaapi.insn_t_get_insnpref(self) + def __set_insnpref__(self, v): + _idaapi.insn_t_set_insnpref(self, v) + def __get_flags__(self): + return _idaapi.insn_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.insn_t_set_flags(self, v) + + cs = property(__get_cs__, __set_cs__) + ip = property(__get_ip__, __set_ip__) + ea = property(__get_ea__, __set_ea__) + itype = property(__get_itype__, __set_itype__) + size = property(__get_size__, __set_size__) + auxpref = property(__get_auxpref__, __set_auxpref__) + segpref = property(__get_segpref__, __set_segpref__) + insnpref = property(__get_insnpref__, __set_insnpref__) + flags = property(__get_flags__, __set_flags__) + + +#---------------------------------------------------------------------------- +# P R O C E S S O R M O D U L E S C O N S T A N T S +#---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------- +# processor_t related constants + +CUSTOM_CMD_ITYPE = 0x8000 +REG_SPOIL = 0x80000000 + +REAL_ERROR_FORMAT = -1 # not supported format for current .idp +REAL_ERROR_RANGE = -2 # number too big (small) for store (mem NOT modifyed) +REAL_ERROR_BADDATA = -3 # illegal real data for load (IEEE data not filled) + +# +# Check whether the operand is relative to stack pointer or frame pointer. +# This function is used to determine how to output a stack variable +# This function may be absent. If it is absent, then all operands +# are sp based by default. +# Define this function only if some stack references use frame pointer +# instead of stack pointer. +# returns flags: +OP_FP_BASED = 0x00000000 # operand is FP based +OP_SP_BASED = 0x00000001 # operand is SP based +OP_SP_ADD = 0x00000000 # operand value is added to the pointer +OP_SP_SUB = 0x00000002 # operand value is substracted from the pointer + +# processor_t.id +PLFM_386 = 0 # Intel 80x86 +PLFM_Z80 = 1 # 8085, Z80 +PLFM_I860 = 2 # Intel 860 +PLFM_8051 = 3 # 8051 +PLFM_TMS = 4 # Texas Instruments TMS320C5x +PLFM_6502 = 5 # 6502 +PLFM_PDP = 6 # PDP11 +PLFM_68K = 7 # Motoroal 680x0 +PLFM_JAVA = 8 # Java +PLFM_6800 = 9 # Motorola 68xx +PLFM_ST7 = 10 # SGS-Thomson ST7 +PLFM_MC6812 = 11 # Motorola 68HC12 +PLFM_MIPS = 12 # MIPS +PLFM_ARM = 13 # Advanced RISC Machines +PLFM_TMSC6 = 14 # Texas Instruments TMS320C6x +PLFM_PPC = 15 # PowerPC +PLFM_80196 = 16 # Intel 80196 +PLFM_Z8 = 17 # Z8 +PLFM_SH = 18 # Renesas (formerly Hitachi) SuperH +PLFM_NET = 19 # Microsoft Visual Studio.Net +PLFM_AVR = 20 # Atmel 8-bit RISC processor(s) +PLFM_H8 = 21 # Hitachi H8/300, H8/2000 +PLFM_PIC = 22 # Microchip's PIC +PLFM_SPARC = 23 # SPARC +PLFM_ALPHA = 24 # DEC Alpha +PLFM_HPPA = 25 # Hewlett-Packard PA-RISC +PLFM_H8500 = 26 # Hitachi H8/500 +PLFM_TRICORE = 27 # Tasking Tricore +PLFM_DSP56K = 28 # Motorola DSP5600x +PLFM_C166 = 29 # Siemens C166 family +PLFM_ST20 = 30 # SGS-Thomson ST20 +PLFM_IA64 = 31 # Intel Itanium IA64 +PLFM_I960 = 32 # Intel 960 +PLFM_F2MC = 33 # Fujistu F2MC-16 +PLFM_TMS320C54 = 34 # Texas Instruments TMS320C54xx +PLFM_TMS320C55 = 35 # Texas Instruments TMS320C55xx +PLFM_TRIMEDIA = 36 # Trimedia +PLFM_M32R = 37 # Mitsubishi 32bit RISC +PLFM_NEC_78K0 = 38 # NEC 78K0 +PLFM_NEC_78K0S = 39 # NEC 78K0S +PLFM_M740 = 40 # Mitsubishi 8bit +PLFM_M7700 = 41 # Mitsubishi 16bit +PLFM_ST9 = 42 # ST9+ +PLFM_FR = 43 # Fujitsu FR Family +PLFM_MC6816 = 44 # Motorola 68HC16 +PLFM_M7900 = 45 # Mitsubishi 7900 +PLFM_TMS320C3 = 46 # Texas Instruments TMS320C3 +PLFM_KR1878 = 47 # Angstrem KR1878 +PLFM_AD218X = 48 # Analog Devices ADSP 218X +PLFM_OAKDSP = 49 # Atmel OAK DSP +PLFM_TLCS900 = 50 # Toshiba TLCS-900 +PLFM_C39 = 51 # Rockwell C39 +PLFM_CR16 = 52 # NSC CR16 +PLFM_MN102L00 = 53 # Panasonic MN10200 +PLFM_TMS320C1X = 54 # Texas Instruments TMS320C1x +PLFM_NEC_V850X = 55 # NEC V850 and V850ES/E1/E2 +PLFM_SCR_ADPT = 56 # Processor module adapter for processor modules written in scripting languages +PLFM_EBC = 57 # EFI Bytecode +PLFM_MSP430 = 58 # Texas Instruments MSP430 +PLFM_SPU = 59 # Cell Broadband Engine Synergistic Processor Unit + +# +# processor_t.flag +# +PR_SEGS = 0x000001 # has segment registers? +PR_USE32 = 0x000002 # supports 32-bit addressing? +PR_DEFSEG32 = 0x000004 # segments are 32-bit by default +PR_RNAMESOK = 0x000008 # allow to user register names for location names +PR_ADJSEGS = 0x000020 # IDA may adjust segments moving their starting/ending addresses. +PR_DEFNUM = 0x0000C0 # default number representation: +PRN_HEX = 0x000000 # hex +PRN_OCT = 0x000040 # octal +PRN_DEC = 0x000080 # decimal +PRN_BIN = 0x0000C0 # binary +PR_WORD_INS = 0x000100 # instruction codes are grouped 2bytes in binrary line prefix +PR_NOCHANGE = 0x000200 # The user can't change segments and code/data attributes (display only) +PR_ASSEMBLE = 0x000400 # Module has a built-in assembler and understands IDP_ASSEMBLE +PR_ALIGN = 0x000800 # All data items should be aligned properly +PR_TYPEINFO = 0x001000 # the processor module supports + # type information callbacks + # ALL OF THEM SHOULD BE IMPLEMENTED! + # (the ones >= decorate_name) +PR_USE64 = 0x002000 # supports 64-bit addressing? +PR_SGROTHER = 0x004000 # the segment registers don't contain + # the segment selectors, something else +PR_STACK_UP = 0x008000 # the stack grows up +PR_BINMEM = 0x010000 # the processor module provides correct + # segmentation for binary files + # (i.e. it creates additional segments) + # The kernel will not ask the user + # to specify the RAM/ROM sizes +PR_SEGTRANS = 0x020000 # the processor module supports + # the segment translation feature + # (it means it calculates the code + # addresses using the codeSeg() function) +PR_CHK_XREF = 0x040000 # don't allow near xrefs between segments + # with different bases +PR_NO_SEGMOVE = 0x080000 # the processor module doesn't support move_segm() + # (i.e. the user can't move segments) +PR_FULL_HIFXP = 0x100000 # REF_VHIGH operand value contains full operand + # not only the high bits. Meaningful if ph.high_fixup_bits +PR_USE_ARG_TYPES = 0x200000 # use ph.use_arg_types callback +PR_SCALE_STKVARS = 0x400000 # use ph.get_stkvar_scale callback +PR_DELAYED = 0x800000 # has delayed jumps and calls +PR_ALIGN_INSN = 0x1000000 # allow ida to create alignment instructions + # arbirtrarily. Since these instructions + # might lead to other wrong instructions + # and spoil the listing, IDA does not create + # them by default anymore +PR_PURGING = 0x2000000 # there are calling conventions which may + # purge bytes from the stack +PR_CNDINSNS = 0x4000000 # has conditional instructions +PR_USE_TBYTE = 0x8000000 # BTMT_SPECFLT means _TBYTE type +PR_DEFSEG64 = 0x10000000 # segments are 64-bit by default + + +# ---------------------------------------------------------------------- +# +# Misc constants +# +UA_MAXOP = 6 +"""The maximum number of operands in the insn_t structure""" + +# Create 'cmd' into the global scope +cmd = insn_t(_idaapi.py_get_global_cmd_link()) +"""cmd is a global variable of type insn_t. It is contains information about the last decoded instruction. +This variable is also filled by processor modules when they decode instructions.""" + +# ---------------------------------------------------------------------- +# instruc_t related constants + +# +# instruc_t.feature +# +CF_STOP = 0x00001 # Instruction doesn't pass execution to the next instruction +CF_CALL = 0x00002 # CALL instruction (should make a procedure here) +CF_CHG1 = 0x00004 # The instruction modifies the first operand +CF_CHG2 = 0x00008 # The instruction modifies the second operand +CF_CHG3 = 0x00010 # The instruction modifies the third operand +CF_CHG4 = 0x00020 # The instruction modifies 4 operand +CF_CHG5 = 0x00040 # The instruction modifies 5 operand +CF_CHG6 = 0x00080 # The instruction modifies 6 operand +CF_USE1 = 0x00100 # The instruction uses value of the first operand +CF_USE2 = 0x00200 # The instruction uses value of the second operand +CF_USE3 = 0x00400 # The instruction uses value of the third operand +CF_USE4 = 0x00800 # The instruction uses value of the 4 operand +CF_USE5 = 0x01000 # The instruction uses value of the 5 operand +CF_USE6 = 0x02000 # The instruction uses value of the 6 operand +CF_JUMP = 0x04000 # The instruction passes execution using indirect jump or call (thus needs additional analysis) +CF_SHFT = 0x08000 # Bit-shift instruction (shl,shr...) +CF_HLL = 0x10000 # Instruction may be present in a high level language function. + +# ---------------------------------------------------------------------- +# op_t related constants + +# +# op_t.type +# Description Data field +o_void = 0 # No Operand ---------- +o_reg = 1 # General Register (al,ax,es,ds...) reg +o_mem = 2 # Direct Memory Reference (DATA) addr +o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase +o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr +o_imm = 5 # Immediate Value value +o_far = 6 # Immediate Far Address (CODE) addr +o_near = 7 # Immediate Near Address (CODE) addr +o_idpspec0 = 8 # Processor specific type +o_idpspec1 = 9 # Processor specific type +o_idpspec2 = 10 # Processor specific type +o_idpspec3 = 11 # Processor specific type +o_idpspec4 = 12 # Processor specific type +o_idpspec5 = 13 # Processor specific type + # There can be more processor specific types + +# +# op_t.dtyp +# +dt_byte = 0 # 8 bit +dt_word = 1 # 16 bit +dt_dword = 2 # 32 bit +dt_float = 3 # 4 byte +dt_double = 4 # 8 byte +dt_tbyte = 5 # variable size (ph.tbyte_size) +dt_packreal = 6 # packed real format for mc68040 +dt_qword = 7 # 64 bit +dt_byte16 = 8 # 128 bit +dt_code = 9 # ptr to code (not used?) +dt_void = 10 # none +dt_fword = 11 # 48 bit +dt_bitfild = 12 # bit field (mc680x0) +dt_string = 13 # pointer to asciiz string +dt_unicode = 14 # pointer to unicode string +dt_3byte = 15 # 3-byte data +dt_ldbl = 16 # long double (which may be different from tbyte) +dt_byte32 = 17 # 256 bit +dt_byte64 = 18 # 512 bit + +# +# op_t.flags +# +OF_NO_BASE_DISP = 0x80 # o_displ: base displacement doesn't exist meaningful only for o_displ type if set, base displacement (x.addr) doesn't exist. +OF_OUTER_DISP = 0x40 # o_displ: outer displacement exists meaningful only for o_displ type if set, outer displacement (x.value) exists. +PACK_FORM_DEF = 0x20 # !o_reg + dt_packreal: packed factor defined +OF_NUMBER = 0x10 # can be output as number only if set, the operand can be converted to a number only +OF_SHOW = 0x08 # should the operand be displayed? if clear, the operand is hidden and should not be displayed + +# +# insn_t.flags +# +INSN_MACRO = 0x01 # macro instruction +INSN_MODMAC = 0x02 # macros: may modify the database to make room for the macro insn + +# +# Set IDP options constants +# +IDPOPT_STR = 1 # string constant +IDPOPT_NUM = 2 # number +IDPOPT_BIT = 3 # bit, yes/no +IDPOPT_FLT = 4 # float +IDPOPT_I64 = 5 # 64bit number + +IDPOPT_OK = 0 # ok +IDPOPT_BADKEY = 1 # illegal keyword +IDPOPT_BADTYPE = 2 # illegal type of value +IDPOPT_BADVALUE = 3 # illegal value (bad range, for example) + +# ---------------------------------------------------------------------- +class processor_t(pyidc_opaque_object_t): + """Base class for all processor module scripts""" + def __init__(self): + # Take a reference to 'cmd' + self.cmd = cmd + + def get_idpdesc(self): + """ + This function must be present and should return the list of + short processor names similar to the one in ph.psnames. + This method can be overridden to return to the kernel a different IDP description. + """ + return '\x01'.join(map(lambda t: '\x01'.join(t), zip(self.plnames, self.psnames))) + + def get_uFlag(self): + """Use this utility function to retrieve the 'uFlag' global variable""" + return _idaapi.cvar.uFlag + + def get_auxpref(self): + """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() + +# +%} diff --git a/swig/view.i b/swig/view.i index 3a5b8f1a..6dddf839 100644 --- a/swig/view.i +++ b/swig/view.i @@ -1,1261 +1,1221 @@ - -%{ -// - -//#define PYGDBG_ENABLED -#ifdef PYGDBG_ENABLED -#define PYGLOG(...) msg(__VA_ARGS__) -#else -#define PYGLOG(...) -#endif - -//------------------------------------------------------------------------- -class py_customidamemo_t; -class lookup_info_t -{ -public: - struct entry_t - { - entry_t() : form(NULL), view(NULL), py_view(NULL) {} - private: - TForm *form; - TCustomControl *view; - py_customidamemo_t *py_view; - friend class lookup_info_t; - }; - - entry_t &new_entry(py_customidamemo_t *py_view) - { - QASSERT(30454, py_view != NULL && !find_by_py_view(NULL, NULL, py_view)); - entry_t &e = entries.push_back(); - e.py_view = py_view; - return e; - } - - void commit(entry_t &e, TForm *form, TCustomControl *view) - { - QASSERT(30455, &e >= entries.begin() && &e < entries.end()); - QASSERT(30456, form != NULL && view != NULL && e.py_view != NULL - && !find_by_form(NULL, NULL, form) - && !find_by_view(NULL, NULL, view) - && find_by_py_view(NULL, NULL, e.py_view)); - e.form = form; - e.view = view; - } - -#define FIND_BY__BODY(crit, res1, res2) \ - { \ - for ( entries_t::const_iterator it = entries.begin(); it != entries.end(); ++it ) \ - { \ - const entry_t &e = *it; \ - if ( e.crit == crit ) \ - { \ - if ( out_##res1 != NULL ) \ - *out_##res1 = e.res1; \ - if ( out_##res2 != NULL ) \ - *out_##res2 = e.res2; \ - return true; \ - } \ - } \ - return false; \ - } - bool find_by_form(TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) const FIND_BY__BODY(form, view, py_view); - bool find_by_view(TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const FIND_BY__BODY(view, form, py_view); - bool find_by_py_view(TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) const FIND_BY__BODY(py_view, view, form); -#undef FIND_BY__BODY - - bool del_by_py_view(const py_customidamemo_t *py_view) - { - for ( entries_t::iterator it = entries.begin(); it != entries.end(); ++it ) - { - if ( it->py_view == py_view ) - { - entries.erase(it); - return true; - } - } - return false; - } - -private: - typedef qvector entries_t; - entries_t entries; -}; - -//------------------------------------------------------------------------- -template -T *view_extract_this(PyObject *self) -{ - PYW_GIL_CHECK_LOCKED_SCOPE(); - ref_t py_this(PyW_TryGetAttrString(self, S_M_THIS)); - if ( py_this == NULL || !PyCObject_Check(py_this.o) ) - return NULL; - return (T*) PyCObject_AsVoidPtr(py_this.o); -} - -//------------------------------------------------------------------------- -class py_customidamemo_t -{ - void convert_node_info( - node_info_t *out, - uint32 *out_flags, - ref_t py_nodeinfo) - { - if ( out_flags != NULL ) - *out_flags = 0; -#define COPY_PROP(checker, converter, pname, flag) \ - do \ - { \ - newref_t pname(PyObject_GetAttrString(py_nodeinfo.o, #pname)); \ - if ( pname != NULL && checker(pname.o) ) \ - { \ - out->pname = converter(pname.o); \ - if ( out_flags != NULL ) \ - *out_flags |= flag; \ - } \ - } while ( false ) -#define COPY_ULONG_PROP(pname, flag) COPY_PROP(PyNumber_Check, PyLong_AsUnsignedLong, pname, flag) -#define COPY_STRING_PROP(pname, flag) COPY_PROP(PyString_Check, PyString_AsString, pname, flag) - COPY_ULONG_PROP(bg_color, NIF_BG_COLOR); - COPY_ULONG_PROP(frame_color, NIF_FRAME_COLOR); - COPY_ULONG_PROP(ea, NIF_EA); - COPY_STRING_PROP(text, NIF_TEXT); -#undef COPY_STRING_PROP -#undef COPY_ULONG_PROP -#undef COPY_PROP - } - - enum - { - GRBASE_HAVE_VIEW_ACTIVATED = 0x001, - GRBASE_HAVE_VIEW_DEACTIVATED = 0x002, - GRBASE_HAVE_KEYDOWN = 0x004, - GRBASE_HAVE_POPUP = 0x008, - GRBASE_HAVE_VIEW_CLICK = 0x010, - GRBASE_HAVE_VIEW_DBLCLICK = 0x020, - GRBASE_HAVE_VIEW_CURPOS = 0x040, - GRBASE_HAVE_CLOSE = 0x080, - GRBASE_HAVE_VIEW_SWITCHED = 0x100, - GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200, - GRBASE_HAVE_VIEW_MOUSE_MOVED = 0x400, - }; - - static void ensure_view_callbacks_installed(); - int cb_flags; - // number of arguments for: - int ovc_num_args; // OnViewClick implementation - int ovdc_num_args; // OnViewDblclick implementation - int ovmo_num_args; // OnViewMouseOver implementation - int ovmm_num_args; // OnViewMouseMoved implementation - -protected: - ref_t self; - TCustomControl *view; - // This is called after having modified the - // node properties in the IDB. In case an - // implementation is performing some caching, - // this is a chance to update that cache. - // If 'ni' is NULL, then the node info was deleted. - virtual void node_info_modified( - int /*n*/, - const node_info_t * /*ni*/, - uint32 /*flags*/) {} - - struct callback_id_t - { - qstring name; - int have; - }; - struct callbacks_ids_t : public qvector - { - void add(const char *_n, int _h) - { - callback_id_t &o = push_back(); - o.name = _n; - o.have = _h; - } - }; - callbacks_ids_t cbids; - - bool collect_pyobject_callbacks(PyObject *self); - virtual void collect_class_callbacks_ids(callbacks_ids_t *out); - - void install_custom_viewer_handlers(); - - // Bi-directionally bind/unbind the Python object and this controller. - bool bind(PyObject *_self, TCustomControl *view); - void unbind(); - - static lookup_info_t lookup_info; - friend TForm *pycim_get_tform(PyObject *self); - friend TCustomControl *pycim_get_tcustom_control(PyObject *self); - -public: - py_customidamemo_t(); - virtual ~py_customidamemo_t(); - virtual void refresh() - { - refresh_viewer(view); - } - void set_node_info(PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); - void set_nodes_infos(PyObject *dict); - PyObject *get_node_info(PyObject *py_node_idx); - void del_nodes_infos(PyObject *py_nodes); - PyObject *get_current_renderer_type(); - void set_current_renderer_type(PyObject *py_rto); - PyObject *create_groups(PyObject *groups_infos); - PyObject *delete_groups(PyObject *groups, PyObject *new_current); - PyObject *set_groups_visibility(PyObject *groups, PyObject *expand, PyObject *new_current); - - // View events - void on_view_activated(); - void on_view_deactivated(); - void on_view_keydown(int key, int state); - void on_view_popup(); - void on_view_click(const view_mouse_event_t *event); - void on_view_dblclick(const view_mouse_event_t *event); - void on_view_curpos(); - void on_view_close(); - void on_view_switched(tcc_renderer_type_t rt); - void on_view_mouse_over(const view_mouse_event_t *event); - void on_view_mouse_moved(const view_mouse_event_t *event); - inline bool has_callback(int flag) { return (cb_flags & flag) != 0; } - int get_py_method_arg_count(char *method_name); - - // View events that are bound with 'set_custom_viewer_handler()'. - static void idaapi s_on_view_mouse_moved( - TCustomControl *cv, - int shift, - view_mouse_event_t *e, - void *ud); -}; - -//------------------------------------------------------------------------- -py_customidamemo_t::py_customidamemo_t() - : self(newref_t(NULL)), - view(NULL) -{ - PYGLOG("%p: py_customidamemo_t()\n", this); - ensure_view_callbacks_installed(); - ovc_num_args = -1; - ovdc_num_args = -1; - ovmo_num_args = -1; - ovmm_num_args = -1; -} - -//------------------------------------------------------------------------- -py_customidamemo_t::~py_customidamemo_t() -{ - PYGLOG("%p: ~py_customidamemo_t()\n", this); - unbind(); - lookup_info.del_by_py_view(this); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::ensure_view_callbacks_installed() -{ - static bool installed = false; - if ( !installed ) - { - struct ida_local lambda_t - { - static int idaapi callback(void * /*ud*/, int code, va_list va) - { - py_customidamemo_t *py_view; - if ( lookup_info.find_by_view(NULL, &py_view, va_arg(va, TCustomControl *)) ) - { - PYW_GIL_GET; - switch ( code ) - { - case view_activated: - py_view->on_view_activated(); - break; - case view_deactivated: - py_view->on_view_deactivated(); - break; - case view_keydown: - { - int key = va_arg(va, int); - int state = va_arg(va, int); - py_view->on_view_keydown(key, state); - } - break; - case obsolete_view_popup: - py_view->on_view_popup(); - break; - case view_click: - case view_dblclick: - { - const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); - if ( code == view_click ) - py_view->on_view_click(event); - else - py_view->on_view_dblclick(event); - } - break; - case view_curpos: - py_view->on_view_curpos(); - break; - case view_close: - py_view->on_view_close(); - delete py_view; - break; - case view_switched: - { - tcc_renderer_type_t rt = (tcc_renderer_type_t) va_arg(va, int); - py_view->on_view_switched(rt); - } - break; - case view_mouse_over: - { - const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); - py_view->on_view_mouse_over(event); - } - break; - } - } - return 0; - } - }; - hook_to_notification_point(HT_VIEW, lambda_t::callback, NULL); - installed = true; - } -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::set_node_info( - PyObject *py_node_idx, - PyObject *py_node_info, - PyObject *py_flags) -{ - if ( !PyNumber_Check(py_node_idx) || !PyNumber_Check(py_flags) ) - return; - borref_t py_idx(py_node_idx); - borref_t py_ni(py_node_info); - borref_t py_fl(py_flags); - node_info_t ni; - convert_node_info(&ni, NULL, py_ni); - int idx = PyInt_AsLong(py_idx.o); - uint32 flgs = PyLong_AsLong(py_fl.o); - viewer_set_node_info(view, idx, ni, flgs); - node_info_modified(idx, &ni, flgs); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::set_nodes_infos(PyObject *dict) -{ - if ( !PyDict_Check(dict) ) - return; - Py_ssize_t pos = 0; - PyObject *o_key, *o_value; - while ( PyDict_Next(dict, &pos, &o_key, &o_value) ) - { - borref_t key(o_key); - borref_t value(o_value); - if ( !PyNumber_Check(key.o) ) - continue; - uint32 flags; - node_info_t ni; - convert_node_info(&ni, &flags, value); - int idx = PyInt_AsLong(key.o); - viewer_set_node_info(view, idx, ni, flags); - node_info_modified(idx, &ni, flags); - } -} - -//------------------------------------------------------------------------- -PyObject *py_customidamemo_t::get_node_info(PyObject *py_node_idx) -{ - if ( !PyNumber_Check(py_node_idx) ) - Py_RETURN_NONE; - node_info_t ni; - if ( !viewer_get_node_info(view, &ni, PyInt_AsLong(py_node_idx)) ) - Py_RETURN_NONE; - return Py_BuildValue("(kkks)", ni.bg_color, ni.frame_color, ni.ea, ni.text.c_str()); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::del_nodes_infos(PyObject *py_nodes) -{ - if ( !PySequence_Check(py_nodes) ) - return; - Py_ssize_t sz = PySequence_Size(py_nodes); - for ( Py_ssize_t i = 0; i < sz; ++i ) - { - newref_t item(PySequence_GetItem(py_nodes, i)); - if ( !PyNumber_Check(item.o) ) - continue; - int idx = PyInt_AsLong(item.o); - viewer_del_node_info(view, idx); - node_info_modified(idx, NULL, 0); - } -} - -//------------------------------------------------------------------------- -PyObject *py_customidamemo_t::get_current_renderer_type() -{ - tcc_renderer_type_t rt = get_view_renderer_type(view); - return PyLong_FromLong(long(rt)); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::set_current_renderer_type(PyObject *py_rto) -{ - tcc_renderer_type_t rt = TCCRT_INVALID; - borref_t py_rt(py_rto); - if ( PyNumber_Check(py_rt.o) ) - { - rt = tcc_renderer_type_t(PyLong_AsLong(py_rt.o)); - set_view_renderer_type(view, rt); - } -} - -//------------------------------------------------------------------------- -PyObject *py_customidamemo_t::create_groups(PyObject *_groups_infos) -{ - if ( !PySequence_Check(_groups_infos) ) - Py_RETURN_NONE; - borref_t groups_infos(_groups_infos); - groups_crinfos_t gis; - Py_ssize_t sz = PySequence_Size(groups_infos.o); - for ( Py_ssize_t i = 0; i < sz; ++i ) - { - newref_t item(PySequence_GetItem(groups_infos.o, i)); - if ( !PyDict_Check(item.o) ) - continue; - borref_t nodes(PyDict_GetItemString(item.o, "nodes")); - if ( nodes.o == NULL || !PySequence_Check(nodes.o) ) - continue; - borref_t text(PyDict_GetItemString(item.o, "text")); - if ( text.o == NULL || !PyString_Check(text.o) ) - continue; - group_crinfo_t gi; - Py_ssize_t nodes_cnt = PySequence_Size(nodes.o); - for ( Py_ssize_t k = 0; k < nodes_cnt; ++k ) - { - newref_t node(PySequence_GetItem(nodes.o, k)); - if ( PyInt_Check(node.o) ) - gi.nodes.add_unique(PyInt_AsLong(node.o)); - } - if ( !gi.nodes.empty() ) - { - gi.text = PyString_AsString(text.o); - gis.push_back(gi); - } - } - intvec_t groups; - if ( gis.empty() || !viewer_create_groups(view, &groups, gis) || groups.empty() ) - Py_RETURN_NONE; - - PyObject *py_groups = PyList_New(0); - for ( intvec_t::const_iterator it = groups.begin(); it != groups.end(); ++it ) - PyList_Append(py_groups, PyInt_FromLong(long(*it))); - return py_groups; -} - -//------------------------------------------------------------------------- -static void pynodes_to_idanodes(intvec_t *idanodes, ref_t pynodes) -{ - Py_ssize_t sz = PySequence_Size(pynodes.o); - for ( Py_ssize_t i = 0; i < sz; ++i ) - { - newref_t item(PySequence_GetItem(pynodes.o, i)); - if ( !PyInt_Check(item.o) ) - continue; - idanodes->add_unique(PyInt_AsLong(item.o)); - } -} - -//------------------------------------------------------------------------- -PyObject *py_customidamemo_t::delete_groups(PyObject *_groups, PyObject *_new_current) -{ - if ( !PySequence_Check(_groups) || !PyNumber_Check(_new_current) ) - Py_RETURN_NONE; - borref_t groups(_groups); - borref_t new_current(_new_current); - intvec_t ida_groups; - pynodes_to_idanodes(&ida_groups, groups); - if ( ida_groups.empty() ) - Py_RETURN_NONE; - if ( viewer_delete_groups(view, ida_groups, int(PyInt_AsLong(new_current.o))) ) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -//------------------------------------------------------------------------- -PyObject *py_customidamemo_t::set_groups_visibility(PyObject *_groups, PyObject *_expand, PyObject *_new_current) -{ - if ( !PySequence_Check(_groups) - || !PyBool_Check(_expand) - || !PyNumber_Check(_new_current) ) - Py_RETURN_NONE; - borref_t groups(_groups); - borref_t expand(_expand); - borref_t new_current(_new_current); - intvec_t ida_groups; - pynodes_to_idanodes(&ida_groups, groups); - if ( ida_groups.empty() ) - Py_RETURN_NONE; - if ( viewer_set_groups_visibility(view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) ) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -//------------------------------------------------------------------------- -bool py_customidamemo_t::bind(PyObject *_self, TCustomControl *view) -{ - if ( this->self != NULL || this->view != NULL ) - return false; - PYGLOG("%p: py_customidamemo_t::bind(_self=%p, view=%p)\n", this, _self, view); - PYW_GIL_CHECK_LOCKED_SCOPE(); - - newref_t py_cobj(PyCObject_FromVoidPtr(this, NULL)); - PyObject_SetAttrString(_self, S_M_THIS, py_cobj.o); - - this->self = borref_t(_self); - this->view = view; - return true; -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::unbind() -{ - if ( self == NULL ) - return; - PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", this, self.o, view); - PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); - PyObject_SetAttrString(self.o, S_M_THIS, py_cobj.o); - self = newref_t(NULL); - view = NULL; -} - -//------------------------------------------------------------------------- -void idaapi py_customidamemo_t::s_on_view_mouse_moved( - TCustomControl *cv, - int shift, - view_mouse_event_t *e, - void *ud) -{ - PYW_GIL_GET; - py_customidamemo_t *_this = (py_customidamemo_t *) ud; - _this->on_view_mouse_moved(e); -} - -//------------------------------------------------------------------------- -int py_customidamemo_t::get_py_method_arg_count(char *method_name) -{ - newref_t method(PyObject_GetAttrString(self.o, method_name)); - if ( method != NULL && PyCallable_Check(method.o) ) - { - newref_t fc(PyObject_GetAttrString(method.o, "func_code")); - if ( fc != NULL ) - { - newref_t ac(PyObject_GetAttrString(fc.o, "co_argcount")); - if ( ac != NULL ) - return PyInt_AsLong(ac.o); - } - } - return -1; -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out) -{ - out->add(S_ON_VIEW_ACTIVATED, GRBASE_HAVE_VIEW_ACTIVATED); - out->add(S_ON_VIEW_DEACTIVATED, GRBASE_HAVE_VIEW_DEACTIVATED); - out->add(S_ON_VIEW_KEYDOWN, GRBASE_HAVE_KEYDOWN); - out->add(S_ON_POPUP, GRBASE_HAVE_POPUP); - out->add(S_ON_VIEW_CLICK, GRBASE_HAVE_VIEW_CLICK); - out->add(S_ON_VIEW_DBLCLICK, GRBASE_HAVE_VIEW_DBLCLICK); - out->add(S_ON_VIEW_CURPOS, GRBASE_HAVE_VIEW_CURPOS); - out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE); - out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED); - out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER); - out->add(S_ON_VIEW_MOUSE_MOVED, GRBASE_HAVE_VIEW_MOUSE_MOVED); -} - -//------------------------------------------------------------------------- -bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o) -{ - callbacks_ids_t cbids; - collect_class_callbacks_ids(&cbids); - cb_flags = 0; - for ( callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it ) - { - const callback_id_t &cbid = *it; - ref_t attr(PyW_TryGetAttrString(o, cbid.name.c_str())); - int have = cbid.have; - // Mandatory fields not present? - if ( (attr == NULL && have <= 0 ) - // Mandatory callback fields present but not callable? - || (attr != NULL && have >= 0 && PyCallable_Check(attr.o) == 0)) - { - return false; - } - if ( have > 0 && attr != NULL ) - cb_flags |= have; - } - - return true; -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::install_custom_viewer_handlers() -{ - if ( has_callback(GRBASE_HAVE_VIEW_MOUSE_MOVED) ) - { - // Set user-data - set_custom_viewer_handler(view, CVH_USERDATA, (void *)this); - - // - set_custom_viewer_handler(view, CVH_MOUSEMOVE, (void *)s_on_view_mouse_moved); - } -} - -//------------------------------------------------------------------------- -#define CHK_EVT(flag_needed) \ - if ( self == NULL || !has_callback(flag_needed) ) \ - return; \ - PYW_GIL_CHECK_LOCKED_SCOPE() - - -#ifdef PYGDBG_ENABLED -#define CHK_RES() \ - do \ - { \ - PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o); \ - if (PyErr_Occurred()) \ - PyErr_Print(); \ - } while ( false ) -#else -#define CHK_RES() \ - do \ - { \ - if (PyErr_Occurred()) \ - PyErr_Print(); \ - } while ( false ) -#endif - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_activated() -{ - CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_ACTIVATED, - NULL)); - CHK_RES(); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_deactivated() -{ - CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_DEACTIVATED, - NULL)); - CHK_RES(); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_keydown(int key, int state) -{ - CHK_EVT(GRBASE_HAVE_KEYDOWN); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_KEYDOWN, - "ii", - key, state)); - CHK_RES(); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_popup() -{ - CHK_EVT(GRBASE_HAVE_POPUP); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_POPUP, - NULL)); - CHK_RES(); -} - -//------------------------------------------------------------------------- -static PyObject *build_renderer_pos_swig_proxy(const view_mouse_event_t *event) -{ - return SWIG_NewPointerObj( - SWIG_as_voidptr(&event->renderer_pos), - SWIGTYPE_p_renderer_pos_info_t, - 0); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_click(const view_mouse_event_t *event) -{ - CHK_EVT(GRBASE_HAVE_VIEW_CLICK); - if ( ovc_num_args < 0 ) - ovc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_CLICK); - if ( ovc_num_args == 6 ) - { - PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_CLICK, - "iiiiO", - event->x, event->y, event->state, event->button, rpos)); - CHK_RES(); - } - else if ( ovc_num_args == 5 ) - { - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_CLICK, - "iiii", - event->x, event->y, event->state, event->button)); - CHK_RES(); - } - else - { - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_CLICK, - "iii", - event->x, event->y, event->state)); - CHK_RES(); - } -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) -{ - CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK); - if ( ovdc_num_args < 0 ) - ovdc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_DBLCLICK); - if ( ovdc_num_args == 5 ) - { - PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_DBLCLICK, - "iiiO", - event->x, event->y, event->state, rpos)); - CHK_RES(); - } - else - { - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_DBLCLICK, - "iii", - event->x, event->y, event->state)); - CHK_RES(); - } -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_curpos() -{ - CHK_EVT(GRBASE_HAVE_VIEW_CURPOS); - newref_t result( - PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_CURPOS, - NULL)); - CHK_RES(); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_close() -{ - CHK_EVT(GRBASE_HAVE_CLOSE); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); - CHK_RES(); -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt) -{ - CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED); - newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); - CHK_RES(); -} - -//------------------------------------------------------------------------- -static ref_t build_current_graph_item_tuple(int *out_icode, const view_mouse_event_t *event) -{ - const selection_item_t *item = event->location.item; - ref_t tuple; - if ( (event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY) - && item != NULL ) - { - if ( item->is_node ) - { - *out_icode = 1; - tuple = newref_t(Py_BuildValue("(i)", item->node)); - } - else - { - *out_icode = 2; - tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst)); - } - } - else - { - *out_icode = 0; - tuple = newref_t(Py_BuildValue("()")); - } - return tuple; -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event) -{ - CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER); - if ( ovmo_num_args < 0 ) - ovmo_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_OVER); - if ( event->rtype != TCCRT_GRAPH && event->rtype != TCCRT_PROXIMITY ) - return; - - int icode; - ref_t tuple = build_current_graph_item_tuple(&icode, event); - if ( ovmo_num_args == 7 ) - { - PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result(PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_MOUSE_OVER, - "iiiiOO", - event->x, event->y, event->state, icode, tuple.o, rpos)); - CHK_RES(); - } - else - { - newref_t result(PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_MOUSE_OVER, - "iiiiO", - event->x, event->y, event->state, icode, tuple.o)); - CHK_RES(); - } -} - -//------------------------------------------------------------------------- -void py_customidamemo_t::on_view_mouse_moved(const view_mouse_event_t *event) -{ - CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_MOVED); - if ( ovmm_num_args < 0 ) - ovmm_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_MOVED); - - int icode; - ref_t tuple = build_current_graph_item_tuple(&icode, event); - if ( ovmm_num_args == 7 ) - { - PyObject *rpos = build_renderer_pos_swig_proxy(event); - newref_t result(PyObject_CallMethod( - self.o, - (char *)S_ON_VIEW_MOUSE_MOVED, - "iiiiOO", - event->x, event->y, event->state, icode, tuple.o, rpos)); - CHK_RES(); - } -} - - -#undef CHK_RES -#undef CHK_EVT - -//------------------------------------------------------------------------- -//------------------------------------------------------------------------- - -#define GET_THIS() py_customidamemo_t *_this = view_extract_this(self) -#define CHK_THIS() \ - GET_THIS(); \ - if ( _this == NULL ) \ - return -#define CHK_THIS_OR_NULL() \ - GET_THIS(); \ - if ( _this == NULL ) \ - return NULL; -#define CHK_THIS_OR_NONE() \ - GET_THIS(); \ - if ( _this == NULL ) \ - Py_RETURN_NONE - -//------------------------------------------------------------------------- -void pygc_refresh(PyObject *self) -{ - CHK_THIS(); - _this->refresh(); -} - -//------------------------------------------------------------------------- -void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) -{ - CHK_THIS(); - _this->set_node_info(py_node_idx, py_node_info, py_flags); -} - -//------------------------------------------------------------------------- -void pygc_set_nodes_infos(PyObject *self, PyObject *values) -{ - CHK_THIS(); - _this->set_nodes_infos(values); -} - -//------------------------------------------------------------------------- -PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx) -{ - GET_THIS(); - if ( _this != NULL ) - return _this->get_node_info(py_node_idx); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes) -{ - CHK_THIS(); - _this->del_nodes_infos(py_nodes); -} - -//------------------------------------------------------------------------- -PyObject *pygc_get_current_renderer_type(PyObject *self) -{ - GET_THIS(); - if ( _this != NULL ) - return _this->get_current_renderer_type(); - else - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt) -{ - CHK_THIS(); - _this->set_current_renderer_type(py_rt); -} - -//------------------------------------------------------------------------- -PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos) -{ - CHK_THIS_OR_NONE(); - return _this->create_groups(groups_infos); -} - -//------------------------------------------------------------------------- -PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current) -{ - CHK_THIS_OR_NONE(); - return _this->delete_groups(groups, new_current); -} - -//------------------------------------------------------------------------- -PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current) -{ - CHK_THIS_OR_NONE(); - return _this->set_groups_visibility(groups, expand, new_current); -} - -//------------------------------------------------------------------------- -TForm *pycim_get_tform(PyObject *self) -{ - CHK_THIS_OR_NULL(); - TForm *form = NULL; - if ( !py_customidamemo_t::lookup_info.find_by_py_view(&form, NULL, _this) ) - return NULL; - return form; -} - -//------------------------------------------------------------------------- -TCustomControl *pycim_get_tcustom_control(PyObject *self) -{ - CHK_THIS_OR_NULL(); - TCustomControl *tcc = NULL; - if ( !py_customidamemo_t::lookup_info.find_by_py_view(NULL, &tcc, _this) ) - return NULL; - return tcc; -} - -#undef CHK_THIS_OR_NONE -#undef CHK_THIS_OR_NULL -#undef CHK_THIS -#undef GET_THIS - -//------------------------------------------------------------------------- -lookup_info_t py_customidamemo_t::lookup_info; -// - -%} - -%inline %{ -// -void pygc_refresh(PyObject *self); -void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); -void pygc_set_nodes_infos(PyObject *self, PyObject *values); -PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx); -void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes); -PyObject *pygc_get_current_renderer_type(PyObject *self); -void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt); -PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos); -PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current); -PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current); -TForm *pycim_get_tform(PyObject *self); -TCustomControl *pycim_get_tcustom_control(PyObject *self); -// -%} - -%pythoncode %{ -# -class CustomIDAMemo(object): - def Refresh(self): - """ - Refreshes the graph. This causes the OnRefresh() to be called - """ - _idaapi.pygc_refresh(self) - - def GetCurrentRendererType(self): - return _idaapi.pygc_get_current_renderer_type(self) - - def SetCurrentRendererType(self, rtype): - """ - Set the current view's renderer. - - @param rtype: The renderer type. Should be one of the idaapi.TCCRT_* values. - """ - _idaapi.pygc_set_current_renderer_type(self, rtype) - - def SetNodeInfo(self, node_index, node_info, flags): - """ - Set the properties for the given node. - - Example usage (set second nodes's bg color to red): - inst = ... - p = idaapi.node_info_t() - p.bg_color = 0x00ff0000 - inst.SetNodeInfo(1, p, idaapi.NIF_BG_COLOR) - - @param node_index: The node index. - @param node_info: An idaapi.node_info_t instance. - @param flags: An OR'ed value of NIF_* values. - """ - _idaapi.pygc_set_node_info(self, node_index, node_info, flags) - - def SetNodesInfos(self, values): - """ - Set the properties for the given nodes. - - Example usage (set first three nodes's bg color to purple): - inst = ... - p = idaapi.node_info_t() - p.bg_color = 0x00ff00ff - inst.SetNodesInfos({0 : p, 1 : p, 2 : p}) - - @param values: A dictionary of 'int -> node_info_t' objects. - """ - _idaapi.pygc_set_nodes_infos(self, values) - - def GetNodeInfo(self, node): - """ - Get the properties for the given node. - - @param node: The index of the node. - @return: A tuple (bg_color, frame_color, ea, text), or None. - """ - return _idaapi.pygc_get_node_info(self, node) - - def DelNodesInfos(self, *nodes): - """ - Delete the properties for the given node(s). - - @param nodes: A list of node IDs - """ - return _idaapi.pygc_del_nodes_infos(self, nodes) - - def CreateGroups(self, groups_infos): - """ - Send a request to modify the graph by creating a - (set of) group(s), and perform an animation. - - Each object in the 'groups_infos' list must be of the format: - { - "nodes" : [, , , ...] # The list of nodes to group - "text" : # The synthetic text for that group - } - - @param groups_infos: A list of objects that describe those groups. - @return: A [, , ...] list of group nodes, or None (failure). - """ - return _idaapi.pygc_create_groups(self, groups_infos) - - def DeleteGroups(self, groups, new_current = -1): - """ - Send a request to delete the specified groups in the graph, - and perform an animation. - - @param groups: A list of group node numbers. - @param new_current: A node to focus on after the groups have been deleted - @return: True on success, False otherwise. - """ - return _idaapi.pygc_delete_groups(self, groups, new_current) - - def SetGroupsVisibility(self, groups, expand, new_current = -1): - """ - Send a request to expand/collapse the specified groups in the graph, - and perform an animation. - - @param groups: A list of group node numbers. - @param expand: True to expand the group, False otherwise. - @param new_current: A node to focus on after the groups have been expanded/collapsed. - @return: True on success, False otherwise. - """ - return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current) - - def GetTForm(self): - """ - Return the TForm hosting this view. - - @return: The TForm that hosts this view, or None. - """ - return _idaapi.pycim_get_tform(self) - - def GetTCustomControl(self): - """ - Return the TCustomControl underlying this view. - - @return: The TCustomControl underlying this view, or None. - """ - return _idaapi.pycim_get_tcustom_control(self) - - -# -%} - -%{ -// -class py_idaview_t : public py_customidamemo_t -{ - typedef py_customidamemo_t inherited; - -public: - static bool Bind(PyObject *self); - static bool Unbind(PyObject *self); -}; - -//------------------------------------------------------------------------- -bool py_idaview_t::Bind(PyObject *self) -{ - // Already a py_idaview_t associated to this object? - py_idaview_t *_this = view_extract_this(self); - if ( _this != NULL ) - return false; - - qstring title; - if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) - return false; - - // Get the IDAView associated to this TForm - TForm *tform = find_tform(title.c_str()); - if ( tform == NULL ) - return false; - TCustomControl *v = get_tform_idaview(tform); - if ( v == NULL ) - return false; - - // Get unique py_idaview_t associated to that tform - py_idaview_t *py_view; - TCustomControl *found_view; - if ( lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_view, tform) ) - { - // If we have a py_idaview_t for that form, ensure it has - // the expected view. - QASSERT(30451, found_view == v); - } - else - { - py_view = new py_idaview_t(); - lookup_info_t::entry_t &e = lookup_info.new_entry(py_view); - lookup_info.commit(e, tform, v); - } - - // Finally, bind: - // py_idaview_t <=> IDAViewWrapper - // py_idaview_t => TCustomControl - bool ok = py_view->bind(self, v); - if ( ok ) - { - ok = py_view->collect_pyobject_callbacks(self); - if ( ok ) - py_view->install_custom_viewer_handlers(); - else - delete py_view; - } - return ok; -} - -//------------------------------------------------------------------------- -bool py_idaview_t::Unbind(PyObject *self) -{ - py_idaview_t *_this = view_extract_this(self); - if ( _this == NULL ) - return false; - _this->unbind(); - return true; -} - -//------------------------------------------------------------------------- -bool pyidag_bind(PyObject *self) -{ - return py_idaview_t::Bind(self); -} - -//------------------------------------------------------------------------- -bool pyidag_unbind(PyObject *self) -{ - return py_idaview_t::Unbind(self); -} - -// -%} - -%inline %{ -// -bool pyidag_bind(PyObject *self); -bool pyidag_unbind(PyObject *self); -// -%} - -%pythoncode %{ -# -class IDAViewWrapper(CustomIDAMemo): - """This class wraps access to native IDA views. See kernwin.hpp file""" - def __init__(self, title): - """ - Constructs the IDAViewWrapper object around the view - whose title is 'title'. - - @param title: The title of the existing IDA view. E.g., 'IDA View-A' - """ - self._title = title - - def Bind(self): - return _idaapi.pyidag_bind(self) - - def Unbind(self): - return _idaapi.pyidag_unbind(self) - -# -%} + +%{ +// + +//------------------------------------------------------------------------- +class py_customidamemo_t; +class lookup_info_t +{ +public: + struct entry_t + { + entry_t() : form(NULL), view(NULL), py_view(NULL) {} + private: + TForm *form; + TCustomControl *view; + py_customidamemo_t *py_view; + friend class lookup_info_t; + }; + + entry_t &new_entry(py_customidamemo_t *py_view) + { + QASSERT(30454, py_view != NULL && !find_by_py_view(NULL, NULL, py_view)); + entry_t &e = entries.push_back(); + e.py_view = py_view; + return e; + } + + void commit(entry_t &e, TForm *form, TCustomControl *view) + { + QASSERT(30455, &e >= entries.begin() && &e < entries.end()); + QASSERT(30456, form != NULL && view != NULL && e.py_view != NULL + && !find_by_form(NULL, NULL, form) + && !find_by_view(NULL, NULL, view) + && find_by_py_view(NULL, NULL, e.py_view)); + e.form = form; + e.view = view; + } + +#define FIND_BY__BODY(crit, res1, res2) \ + { \ + for ( entries_t::const_iterator it = entries.begin(); it != entries.end(); ++it ) \ + { \ + const entry_t &e = *it; \ + if ( e.crit == crit ) \ + { \ + if ( out_##res1 != NULL ) \ + *out_##res1 = e.res1; \ + if ( out_##res2 != NULL ) \ + *out_##res2 = e.res2; \ + return true; \ + } \ + } \ + return false; \ + } + bool find_by_form(TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) const FIND_BY__BODY(form, view, py_view); + bool find_by_view(TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const FIND_BY__BODY(view, form, py_view); + bool find_by_py_view(TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) const FIND_BY__BODY(py_view, view, form); +#undef FIND_BY__BODY + + bool del_by_py_view(const py_customidamemo_t *py_view) + { + for ( entries_t::iterator it = entries.begin(); it != entries.end(); ++it ) + { + if ( it->py_view == py_view ) + { + entries.erase(it); + return true; + } + } + return false; + } + +private: + typedef qvector entries_t; + entries_t entries; +}; + +//------------------------------------------------------------------------- +template +T *view_extract_this(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_this(PyW_TryGetAttrString(self, S_M_THIS)); + if ( py_this == NULL || !PyCObject_Check(py_this.o) ) + return NULL; + return (T*) PyCObject_AsVoidPtr(py_this.o); +} + +//------------------------------------------------------------------------- +class py_customidamemo_t +{ + void convert_node_info( + node_info_t *out, + uint32 *out_flags, + ref_t py_nodeinfo) + { + if ( out_flags != NULL ) + *out_flags = 0; +#define COPY_PROP(checker, converter, pname, flag) \ + do \ + { \ + newref_t pname(PyObject_GetAttrString(py_nodeinfo.o, #pname)); \ + if ( pname != NULL && checker(pname.o) ) \ + { \ + out->pname = converter(pname.o); \ + if ( out_flags != NULL ) \ + *out_flags |= flag; \ + } \ + } while ( false ) +#define COPY_ULONG_PROP(pname, flag) COPY_PROP(PyNumber_Check, PyLong_AsUnsignedLong, pname, flag) +#define COPY_STRING_PROP(pname, flag) COPY_PROP(PyString_Check, PyString_AsString, pname, flag) + COPY_ULONG_PROP(bg_color, NIF_BG_COLOR); + COPY_ULONG_PROP(frame_color, NIF_FRAME_COLOR); + COPY_ULONG_PROP(ea, NIF_EA); + COPY_STRING_PROP(text, NIF_TEXT); +#undef COPY_STRING_PROP +#undef COPY_ULONG_PROP +#undef COPY_PROP + } + + enum + { + GRBASE_HAVE_VIEW_ACTIVATED = 0x001, + GRBASE_HAVE_VIEW_DEACTIVATED = 0x002, + GRBASE_HAVE_KEYDOWN = 0x004, + GRBASE_HAVE_POPUP = 0x008, + GRBASE_HAVE_VIEW_CLICK = 0x010, + GRBASE_HAVE_VIEW_DBLCLICK = 0x020, + GRBASE_HAVE_VIEW_CURPOS = 0x040, + GRBASE_HAVE_CLOSE = 0x080, + GRBASE_HAVE_VIEW_SWITCHED = 0x100, + GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200, + GRBASE_HAVE_VIEW_MOUSE_MOVED = 0x400, + }; + + static void ensure_view_callbacks_installed(); + int cb_flags; + // number of arguments for: + int ovc_num_args; // OnViewClick implementation + int ovdc_num_args; // OnViewDblclick implementation + int ovmo_num_args; // OnViewMouseOver implementation + int ovmm_num_args; // OnViewMouseMoved implementation + +protected: + ref_t self; + TCustomControl *view; + // This is called after having modified the + // node properties in the IDB. In case an + // implementation is performing some caching, + // this is a chance to update that cache. + // If 'ni' is NULL, then the node info was deleted. + virtual void node_info_modified( + int /*n*/, + const node_info_t * /*ni*/, + uint32 /*flags*/) {} + + struct callback_id_t + { + qstring name; + int have; + }; + struct callbacks_ids_t : public qvector + { + void add(const char *_n, int _h) + { + callback_id_t &o = push_back(); + o.name = _n; + o.have = _h; + } + }; + callbacks_ids_t cbids; + + bool collect_pyobject_callbacks(PyObject *self); + virtual void collect_class_callbacks_ids(callbacks_ids_t *out); + + void install_custom_viewer_handlers(); + + // Bi-directionally bind/unbind the Python object and this controller. + bool bind(PyObject *_self, TCustomControl *view); + void unbind(bool clear_view); + + static lookup_info_t lookup_info; + friend TForm *pycim_get_tform(PyObject *self); + friend TCustomControl *pycim_get_tcustom_control(PyObject *self); + +public: + py_customidamemo_t(); + virtual ~py_customidamemo_t(); + virtual void refresh() + { + refresh_viewer(view); + } + void set_node_info(PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); + void set_nodes_infos(PyObject *dict); + PyObject *get_node_info(PyObject *py_node_idx); + void del_nodes_infos(PyObject *py_nodes); + PyObject *get_current_renderer_type(); + void set_current_renderer_type(PyObject *py_rto); + PyObject *create_groups(PyObject *groups_infos); + PyObject *delete_groups(PyObject *groups, PyObject *new_current); + PyObject *set_groups_visibility(PyObject *groups, PyObject *expand, PyObject *new_current); + + // View events + void on_view_activated(); + void on_view_deactivated(); + void on_view_keydown(int key, int state); + void on_view_popup(); + void on_view_click(const view_mouse_event_t *event); + void on_view_dblclick(const view_mouse_event_t *event); + void on_view_curpos(); + void on_view_close(); + void on_view_switched(tcc_renderer_type_t rt); + void on_view_mouse_over(const view_mouse_event_t *event); + void on_view_mouse_moved(const view_mouse_event_t *event); + inline bool has_callback(int flag) { return (cb_flags & flag) != 0; } + int get_py_method_arg_count(char *method_name); + + // View events that are bound with 'set_custom_viewer_handler()'. + static void idaapi s_on_view_mouse_moved( + TCustomControl *cv, + int shift, + view_mouse_event_t *e, + void *ud); +}; + +//------------------------------------------------------------------------- +py_customidamemo_t::py_customidamemo_t() + : self(newref_t(NULL)), + view(NULL) +{ + PYGLOG("%p: py_customidamemo_t()\n", this); + ensure_view_callbacks_installed(); + ovc_num_args = -1; + ovdc_num_args = -1; + ovmo_num_args = -1; + ovmm_num_args = -1; +} + +//------------------------------------------------------------------------- +py_customidamemo_t::~py_customidamemo_t() +{ + PYGLOG("%p: ~py_customidamemo_t()\n", this); + unbind(true); + lookup_info.del_by_py_view(this); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::ensure_view_callbacks_installed() +{ + static bool installed = false; + if ( !installed ) + { + struct ida_local lambda_t + { + static int idaapi callback(void * /*ud*/, int code, va_list va) + { + py_customidamemo_t *py_view; + if ( lookup_info.find_by_view(NULL, &py_view, va_arg(va, TCustomControl *)) ) + { + PYW_GIL_GET; + switch ( code ) + { + case view_activated: + py_view->on_view_activated(); + break; + case view_deactivated: + py_view->on_view_deactivated(); + break; + case view_keydown: + { + int key = va_arg(va, int); + int state = va_arg(va, int); + py_view->on_view_keydown(key, state); + } + break; + case obsolete_view_popup: + py_view->on_view_popup(); + break; + case view_click: + case view_dblclick: + { + const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); + if ( code == view_click ) + py_view->on_view_click(event); + else + py_view->on_view_dblclick(event); + } + break; + case view_curpos: + py_view->on_view_curpos(); + break; + case view_close: + py_view->on_view_close(); + delete py_view; + break; + case view_switched: + { + tcc_renderer_type_t rt = (tcc_renderer_type_t) va_arg(va, int); + py_view->on_view_switched(rt); + } + break; + case view_mouse_over: + { + const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); + py_view->on_view_mouse_over(event); + } + break; + } + } + return 0; + } + }; + hook_to_notification_point(HT_VIEW, lambda_t::callback, NULL); + installed = true; + } +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_node_info( + PyObject *py_node_idx, + PyObject *py_node_info, + PyObject *py_flags) +{ + if ( !PyNumber_Check(py_node_idx) || !PyNumber_Check(py_flags) ) + return; + borref_t py_idx(py_node_idx); + borref_t py_ni(py_node_info); + borref_t py_fl(py_flags); + node_info_t ni; + convert_node_info(&ni, NULL, py_ni); + int idx = PyInt_AsLong(py_idx.o); + uint32 flgs = PyLong_AsLong(py_fl.o); + viewer_set_node_info(view, idx, ni, flgs); + node_info_modified(idx, &ni, flgs); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_nodes_infos(PyObject *dict) +{ + if ( !PyDict_Check(dict) ) + return; + Py_ssize_t pos = 0; + PyObject *o_key, *o_value; + while ( PyDict_Next(dict, &pos, &o_key, &o_value) ) + { + borref_t key(o_key); + borref_t value(o_value); + if ( !PyNumber_Check(key.o) ) + continue; + uint32 flags; + node_info_t ni; + convert_node_info(&ni, &flags, value); + int idx = PyInt_AsLong(key.o); + viewer_set_node_info(view, idx, ni, flags); + node_info_modified(idx, &ni, flags); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::get_node_info(PyObject *py_node_idx) +{ + if ( !PyNumber_Check(py_node_idx) ) + Py_RETURN_NONE; + node_info_t ni; + if ( !viewer_get_node_info(view, &ni, PyInt_AsLong(py_node_idx)) ) + Py_RETURN_NONE; + return Py_BuildValue("(kkks)", ni.bg_color, ni.frame_color, ni.ea, ni.text.c_str()); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::del_nodes_infos(PyObject *py_nodes) +{ + if ( !PySequence_Check(py_nodes) ) + return; + Py_ssize_t sz = PySequence_Size(py_nodes); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(py_nodes, i)); + if ( !PyNumber_Check(item.o) ) + continue; + int idx = PyInt_AsLong(item.o); + viewer_del_node_info(view, idx); + node_info_modified(idx, NULL, 0); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::get_current_renderer_type() +{ + tcc_renderer_type_t rt = get_view_renderer_type(view); + return PyLong_FromLong(long(rt)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_current_renderer_type(PyObject *py_rto) +{ + tcc_renderer_type_t rt = TCCRT_INVALID; + borref_t py_rt(py_rto); + if ( PyNumber_Check(py_rt.o) ) + { + rt = tcc_renderer_type_t(PyLong_AsLong(py_rt.o)); + set_view_renderer_type(view, rt); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::create_groups(PyObject *_groups_infos) +{ + if ( !PySequence_Check(_groups_infos) ) + Py_RETURN_NONE; + borref_t groups_infos(_groups_infos); + groups_crinfos_t gis; + Py_ssize_t sz = PySequence_Size(groups_infos.o); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(groups_infos.o, i)); + if ( !PyDict_Check(item.o) ) + continue; + borref_t nodes(PyDict_GetItemString(item.o, "nodes")); + if ( nodes.o == NULL || !PySequence_Check(nodes.o) ) + continue; + borref_t text(PyDict_GetItemString(item.o, "text")); + if ( text.o == NULL || !PyString_Check(text.o) ) + continue; + group_crinfo_t gi; + Py_ssize_t nodes_cnt = PySequence_Size(nodes.o); + for ( Py_ssize_t k = 0; k < nodes_cnt; ++k ) + { + newref_t node(PySequence_GetItem(nodes.o, k)); + if ( PyInt_Check(node.o) ) + gi.nodes.add_unique(PyInt_AsLong(node.o)); + } + if ( !gi.nodes.empty() ) + { + gi.text = PyString_AsString(text.o); + gis.push_back(gi); + } + } + intvec_t groups; + if ( gis.empty() || !viewer_create_groups(view, &groups, gis) || groups.empty() ) + Py_RETURN_NONE; + + PyObject *py_groups = PyList_New(0); + for ( intvec_t::const_iterator it = groups.begin(); it != groups.end(); ++it ) + PyList_Append(py_groups, PyInt_FromLong(long(*it))); + return py_groups; +} + +//------------------------------------------------------------------------- +static void pynodes_to_idanodes(intvec_t *idanodes, ref_t pynodes) +{ + Py_ssize_t sz = PySequence_Size(pynodes.o); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(pynodes.o, i)); + if ( !PyInt_Check(item.o) ) + continue; + idanodes->add_unique(PyInt_AsLong(item.o)); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::delete_groups(PyObject *_groups, PyObject *_new_current) +{ + if ( !PySequence_Check(_groups) || !PyNumber_Check(_new_current) ) + Py_RETURN_NONE; + borref_t groups(_groups); + borref_t new_current(_new_current); + intvec_t ida_groups; + pynodes_to_idanodes(&ida_groups, groups); + if ( ida_groups.empty() ) + Py_RETURN_NONE; + if ( viewer_delete_groups(view, ida_groups, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::set_groups_visibility(PyObject *_groups, PyObject *_expand, PyObject *_new_current) +{ + if ( !PySequence_Check(_groups) + || !PyBool_Check(_expand) + || !PyNumber_Check(_new_current) ) + Py_RETURN_NONE; + borref_t groups(_groups); + borref_t expand(_expand); + borref_t new_current(_new_current); + intvec_t ida_groups; + pynodes_to_idanodes(&ida_groups, groups); + if ( ida_groups.empty() ) + Py_RETURN_NONE; + if ( viewer_set_groups_visibility(view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +bool py_customidamemo_t::bind(PyObject *_self, TCustomControl *view) +{ + if ( this->self != NULL || this->view != NULL ) + return false; + PYGLOG("%p: py_customidamemo_t::bind(_self=%p, view=%p)\n", this, _self, view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + + newref_t py_cobj(PyCObject_FromVoidPtr(this, NULL)); + PyObject_SetAttrString(_self, S_M_THIS, py_cobj.o); + + this->self = borref_t(_self); + this->view = view; + return true; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::unbind(bool clear_view) +{ + if ( self == NULL ) + return; + PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", this, self.o, view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); + PyObject_SetAttrString(self.o, S_M_THIS, py_cobj.o); + self = newref_t(NULL); + if ( clear_view ) + view = NULL; +} + +//------------------------------------------------------------------------- +void idaapi py_customidamemo_t::s_on_view_mouse_moved( + TCustomControl *cv, + int shift, + view_mouse_event_t *e, + void *ud) +{ + PYW_GIL_GET; + py_customidamemo_t *_this = (py_customidamemo_t *) ud; + _this->on_view_mouse_moved(e); +} + +//------------------------------------------------------------------------- +int py_customidamemo_t::get_py_method_arg_count(char *method_name) +{ + newref_t method(PyObject_GetAttrString(self.o, method_name)); + if ( method != NULL && PyCallable_Check(method.o) ) + { + newref_t fc(PyObject_GetAttrString(method.o, "func_code")); + if ( fc != NULL ) + { + newref_t ac(PyObject_GetAttrString(fc.o, "co_argcount")); + if ( ac != NULL ) + return PyInt_AsLong(ac.o); + } + } + return -1; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + out->add(S_ON_VIEW_ACTIVATED, GRBASE_HAVE_VIEW_ACTIVATED); + out->add(S_ON_VIEW_DEACTIVATED, GRBASE_HAVE_VIEW_DEACTIVATED); + out->add(S_ON_VIEW_KEYDOWN, GRBASE_HAVE_KEYDOWN); + out->add(S_ON_POPUP, GRBASE_HAVE_POPUP); + out->add(S_ON_VIEW_CLICK, GRBASE_HAVE_VIEW_CLICK); + out->add(S_ON_VIEW_DBLCLICK, GRBASE_HAVE_VIEW_DBLCLICK); + out->add(S_ON_VIEW_CURPOS, GRBASE_HAVE_VIEW_CURPOS); + out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE); + out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED); + out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER); + out->add(S_ON_VIEW_MOUSE_MOVED, GRBASE_HAVE_VIEW_MOUSE_MOVED); +} + +//------------------------------------------------------------------------- +bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o) +{ + callbacks_ids_t cbids; + collect_class_callbacks_ids(&cbids); + cb_flags = 0; + for ( callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it ) + { + const callback_id_t &cbid = *it; + ref_t attr(PyW_TryGetAttrString(o, cbid.name.c_str())); + int have = cbid.have; + // Mandatory fields not present? + if ( (attr == NULL && have <= 0 ) + // Mandatory callback fields present but not callable? + || (attr != NULL && have >= 0 && PyCallable_Check(attr.o) == 0)) + { + return false; + } + if ( have > 0 && attr != NULL ) + cb_flags |= have; + } + + return true; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::install_custom_viewer_handlers() +{ + if ( has_callback(GRBASE_HAVE_VIEW_MOUSE_MOVED) ) + { + // Set user-data + set_custom_viewer_handler(view, CVH_USERDATA, (void *)this); + + // + set_custom_viewer_handler(view, CVH_MOUSEMOVE, (void *)s_on_view_mouse_moved); + } +} + +//------------------------------------------------------------------------- +#define CHK_EVT(flag_needed) \ + if ( self == NULL || !has_callback(flag_needed) ) \ + return; \ + PYW_GIL_CHECK_LOCKED_SCOPE() + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_activated() +{ + CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_ACTIVATED, + NULL)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_deactivated() +{ + CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DEACTIVATED, + NULL)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_keydown(int key, int state) +{ + CHK_EVT(GRBASE_HAVE_KEYDOWN); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_KEYDOWN, + "ii", + key, state)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_popup() +{ + CHK_EVT(GRBASE_HAVE_POPUP); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_POPUP, + NULL)); +} + +//------------------------------------------------------------------------- +static PyObject *build_renderer_pos_swig_proxy(const view_mouse_event_t *event) +{ + return SWIG_NewPointerObj( + SWIG_as_voidptr(&event->renderer_pos), + SWIGTYPE_p_renderer_pos_info_t, + 0); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_click(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_CLICK); + if ( ovc_num_args < 0 ) + ovc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_CLICK); + if ( ovc_num_args == 6 ) + { + PyObject *rpos = build_renderer_pos_swig_proxy(event); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CLICK, + "iiiiO", + event->x, event->y, event->state, event->button, rpos)); + } + else if ( ovc_num_args == 5 ) + { + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CLICK, + "iiii", + event->x, event->y, event->state, event->button)); + } + else + { + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CLICK, + "iii", + event->x, event->y, event->state)); + } +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK); + if ( ovdc_num_args < 0 ) + ovdc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_DBLCLICK); + if ( ovdc_num_args == 5 ) + { + PyObject *rpos = build_renderer_pos_swig_proxy(event); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DBLCLICK, + "iiiO", + event->x, event->y, event->state, rpos)); + } + else + { + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DBLCLICK, + "iii", + event->x, event->y, event->state)); + } +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_curpos() +{ + CHK_EVT(GRBASE_HAVE_VIEW_CURPOS); + pycall_res_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CURPOS, + NULL)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_close() +{ + CHK_EVT(GRBASE_HAVE_CLOSE); + pycall_res_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt) +{ + CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED); + pycall_res_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); +} + +//------------------------------------------------------------------------- +static ref_t build_current_graph_item_tuple(int *out_icode, const view_mouse_event_t *event) +{ + const selection_item_t *item = event->location.item; + ref_t tuple; + if ( (event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY) + && item != NULL ) + { + if ( item->is_node ) + { + *out_icode = 1; + tuple = newref_t(Py_BuildValue("(i)", item->node)); + } + else + { + *out_icode = 2; + tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst)); + } + } + else + { + *out_icode = 0; + tuple = newref_t(Py_BuildValue("()")); + } + return tuple; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER); + if ( ovmo_num_args < 0 ) + ovmo_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_OVER); + if ( event->rtype != TCCRT_GRAPH && event->rtype != TCCRT_PROXIMITY ) + return; + + int icode; + ref_t tuple = build_current_graph_item_tuple(&icode, event); + if ( ovmo_num_args == 7 ) + { + PyObject *rpos = build_renderer_pos_swig_proxy(event); + pycall_res_t result(PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_MOUSE_OVER, + "iiiiOO", + event->x, event->y, event->state, icode, tuple.o, rpos)); + } + else + { + pycall_res_t result(PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_MOUSE_OVER, + "iiiiO", + event->x, event->y, event->state, icode, tuple.o)); + } +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_mouse_moved(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_MOVED); + if ( ovmm_num_args < 0 ) + ovmm_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_MOVED); + + int icode; + ref_t tuple = build_current_graph_item_tuple(&icode, event); + if ( ovmm_num_args == 7 ) + { + PyObject *rpos = build_renderer_pos_swig_proxy(event); + pycall_res_t result(PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_MOUSE_MOVED, + "iiiiOO", + event->x, event->y, event->state, icode, tuple.o, rpos)); + } +} + + +#undef CHK_EVT + +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + +#define GET_THIS() py_customidamemo_t *_this = view_extract_this(self) +#define CHK_THIS() \ + GET_THIS(); \ + if ( _this == NULL ) \ + return +#define CHK_THIS_OR_NULL() \ + GET_THIS(); \ + if ( _this == NULL ) \ + return NULL; +#define CHK_THIS_OR_NONE() \ + GET_THIS(); \ + if ( _this == NULL ) \ + Py_RETURN_NONE + +//------------------------------------------------------------------------- +void pygc_refresh(PyObject *self) +{ + CHK_THIS(); + _this->refresh(); +} + +//------------------------------------------------------------------------- +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) +{ + CHK_THIS(); + _this->set_node_info(py_node_idx, py_node_info, py_flags); +} + +//------------------------------------------------------------------------- +void pygc_set_nodes_infos(PyObject *self, PyObject *values) +{ + CHK_THIS(); + _this->set_nodes_infos(values); +} + +//------------------------------------------------------------------------- +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_node_info(py_node_idx); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes) +{ + CHK_THIS(); + _this->del_nodes_infos(py_nodes); +} + +//------------------------------------------------------------------------- +PyObject *pygc_get_current_renderer_type(PyObject *self) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_current_renderer_type(); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt) +{ + CHK_THIS(); + _this->set_current_renderer_type(py_rt); +} + +//------------------------------------------------------------------------- +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos) +{ + CHK_THIS_OR_NONE(); + return _this->create_groups(groups_infos); +} + +//------------------------------------------------------------------------- +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current) +{ + CHK_THIS_OR_NONE(); + return _this->delete_groups(groups, new_current); +} + +//------------------------------------------------------------------------- +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current) +{ + CHK_THIS_OR_NONE(); + return _this->set_groups_visibility(groups, expand, new_current); +} + +//------------------------------------------------------------------------- +TForm *pycim_get_tform(PyObject *self) +{ + CHK_THIS_OR_NULL(); + TForm *form = NULL; + if ( !py_customidamemo_t::lookup_info.find_by_py_view(&form, NULL, _this) ) + return NULL; + return form; +} + +//------------------------------------------------------------------------- +TCustomControl *pycim_get_tcustom_control(PyObject *self) +{ + CHK_THIS_OR_NULL(); + TCustomControl *tcc = NULL; + if ( !py_customidamemo_t::lookup_info.find_by_py_view(NULL, &tcc, _this) ) + return NULL; + return tcc; +} + +#undef CHK_THIS_OR_NONE +#undef CHK_THIS_OR_NULL +#undef CHK_THIS +#undef GET_THIS + +//------------------------------------------------------------------------- +lookup_info_t py_customidamemo_t::lookup_info; +// + +%} + +%inline %{ +// +void pygc_refresh(PyObject *self); +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); +void pygc_set_nodes_infos(PyObject *self, PyObject *values); +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx); +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes); +PyObject *pygc_get_current_renderer_type(PyObject *self); +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt); +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos); +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current); +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current); +TForm *pycim_get_tform(PyObject *self); +TCustomControl *pycim_get_tcustom_control(PyObject *self); +// +%} + +%pythoncode %{ +# +class CustomIDAMemo(object): + def Refresh(self): + """ + Refreshes the graph. This causes the OnRefresh() to be called + """ + _idaapi.pygc_refresh(self) + + def GetCurrentRendererType(self): + return _idaapi.pygc_get_current_renderer_type(self) + + def SetCurrentRendererType(self, rtype): + """ + Set the current view's renderer. + + @param rtype: The renderer type. Should be one of the idaapi.TCCRT_* values. + """ + _idaapi.pygc_set_current_renderer_type(self, rtype) + + def SetNodeInfo(self, node_index, node_info, flags): + """ + Set the properties for the given node. + + Example usage (set second nodes's bg color to red): + inst = ... + p = idaapi.node_info_t() + p.bg_color = 0x00ff0000 + inst.SetNodeInfo(1, p, idaapi.NIF_BG_COLOR) + + @param node_index: The node index. + @param node_info: An idaapi.node_info_t instance. + @param flags: An OR'ed value of NIF_* values. + """ + _idaapi.pygc_set_node_info(self, node_index, node_info, flags) + + def SetNodesInfos(self, values): + """ + Set the properties for the given nodes. + + Example usage (set first three nodes's bg color to purple): + inst = ... + p = idaapi.node_info_t() + p.bg_color = 0x00ff00ff + inst.SetNodesInfos({0 : p, 1 : p, 2 : p}) + + @param values: A dictionary of 'int -> node_info_t' objects. + """ + _idaapi.pygc_set_nodes_infos(self, values) + + def GetNodeInfo(self, node): + """ + Get the properties for the given node. + + @param node: The index of the node. + @return: A tuple (bg_color, frame_color, ea, text), or None. + """ + return _idaapi.pygc_get_node_info(self, node) + + def DelNodesInfos(self, *nodes): + """ + Delete the properties for the given node(s). + + @param nodes: A list of node IDs + """ + return _idaapi.pygc_del_nodes_infos(self, nodes) + + def CreateGroups(self, groups_infos): + """ + Send a request to modify the graph by creating a + (set of) group(s), and perform an animation. + + Each object in the 'groups_infos' list must be of the format: + { + "nodes" : [, , , ...] # The list of nodes to group + "text" : # The synthetic text for that group + } + + @param groups_infos: A list of objects that describe those groups. + @return: A [, , ...] list of group nodes, or None (failure). + """ + return _idaapi.pygc_create_groups(self, groups_infos) + + def DeleteGroups(self, groups, new_current = -1): + """ + Send a request to delete the specified groups in the graph, + and perform an animation. + + @param groups: A list of group node numbers. + @param new_current: A node to focus on after the groups have been deleted + @return: True on success, False otherwise. + """ + return _idaapi.pygc_delete_groups(self, groups, new_current) + + def SetGroupsVisibility(self, groups, expand, new_current = -1): + """ + Send a request to expand/collapse the specified groups in the graph, + and perform an animation. + + @param groups: A list of group node numbers. + @param expand: True to expand the group, False otherwise. + @param new_current: A node to focus on after the groups have been expanded/collapsed. + @return: True on success, False otherwise. + """ + return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current) + + def GetTForm(self): + """ + Return the TForm hosting this view. + + @return: The TForm that hosts this view, or None. + """ + return _idaapi.pycim_get_tform(self) + + def GetTCustomControl(self): + """ + Return the TCustomControl underlying this view. + + @return: The TCustomControl underlying this view, or None. + """ + return _idaapi.pycim_get_tcustom_control(self) + + +# +%} + +%{ +// +class py_idaview_t : public py_customidamemo_t +{ + typedef py_customidamemo_t inherited; + +public: + static bool Bind(PyObject *self); + static bool Unbind(PyObject *self); +}; + +//------------------------------------------------------------------------- +bool py_idaview_t::Bind(PyObject *self) +{ + // Already a py_idaview_t associated to this object? + py_idaview_t *_this = view_extract_this(self); + if ( _this != NULL ) + return false; + + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return false; + + // Get the IDAView associated to this TForm + TForm *tform = find_tform(title.c_str()); + if ( tform == NULL ) + return false; + TCustomControl *v = get_tform_idaview(tform); + if ( v == NULL ) + return false; + + // Get unique py_idaview_t associated to that tform + py_idaview_t *py_view; + TCustomControl *found_view; + if ( lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_view, tform) ) + { + // If we have a py_idaview_t for that form, ensure it has + // the expected view. + QASSERT(30451, found_view == v); + } + else + { + py_view = new py_idaview_t(); + lookup_info_t::entry_t &e = lookup_info.new_entry(py_view); + lookup_info.commit(e, tform, v); + } + + // Finally, bind: + // py_idaview_t <=> IDAViewWrapper + // py_idaview_t => TCustomControl + bool ok = py_view->bind(self, v); + if ( ok ) + { + ok = py_view->collect_pyobject_callbacks(self); + if ( ok ) + py_view->install_custom_viewer_handlers(); + else + delete py_view; + } + return ok; +} + +//------------------------------------------------------------------------- +bool py_idaview_t::Unbind(PyObject *self) +{ + py_idaview_t *_this = view_extract_this(self); + if ( _this == NULL ) + return false; + _this->unbind(true); + return true; +} + +//------------------------------------------------------------------------- +bool pyidag_bind(PyObject *self) +{ + return py_idaview_t::Bind(self); +} + +//------------------------------------------------------------------------- +bool pyidag_unbind(PyObject *self) +{ + return py_idaview_t::Unbind(self); +} + +// +%} + +%inline %{ +// +bool pyidag_bind(PyObject *self); +bool pyidag_unbind(PyObject *self); +// +%} + +%pythoncode %{ +# +class IDAViewWrapper(CustomIDAMemo): + """This class wraps access to native IDA views. See kernwin.hpp file""" + def __init__(self, title): + """ + Constructs the IDAViewWrapper object around the view + whose title is 'title'. + + @param title: The title of the existing IDA view. E.g., 'IDA View-A' + """ + self._title = title + + def Bind(self): + return _idaapi.pyidag_bind(self) + + def Unbind(self): + return _idaapi.pyidag_unbind(self) + +# +%} diff --git a/tools/swigdocs.py b/tools/swigdocs.py index 2d58ee77..944d64e4 100644 --- a/tools/swigdocs.py +++ b/tools/swigdocs.py @@ -1,98 +1,98 @@ -# ----------------------------------------------------------------------- -# This script is used to extract embedded documentation strings -# from SWIG interface files. -# (c) Hex-Rays -# -import glob -import sys -import os - -# --------------------------------------------------------------------------- -def extract_docs(lines, out): - S_SWIG_CLOSE = '%}' - S_PYDOC_START = '#' - S_PYDOC_END = '#' - S_COMMENT = '#' - S_INLINE = '%inline %{' - S_PYCODE_START = '%pythoncode %{' - - in_inline = False - in_pythoncode = False - in_pydoc = False - - for line in lines: - line = line.rstrip() - # skip empty lines - if not line: - continue - - # Inside pythoncode tag? - if in_pythoncode: - if line == S_PYDOC_START: - in_pydoc = True - continue - elif line == S_PYDOC_END: - in_pydoc = False - continue - elif line == S_SWIG_CLOSE: - in_pythoncode = False - continue - # Skip unneeded tags - elif line[:8] == '#