From 62ddab8db6929edbea8d2016e4659cf7cc62aa88 Mon Sep 17 00:00:00 2001 From: Arnaud Diederen Date: Tue, 9 Aug 2016 10:01:18 +0200 Subject: [PATCH] IDAPython for IDA 6.95: Initial commit --- Scripts/3rd/BboeVt.py | 0 Scripts/AsmViewer.py | 0 Scripts/CallStackWalk.py | 0 Scripts/DbgCmd.py | 0 Scripts/DrvsDispatch.py | 0 Scripts/ExchainDump.py | 0 Scripts/FindInstructions.py | 0 Scripts/ImpRef.py | 0 Scripts/ImportExportViewer.py | 0 Scripts/SEHGraph.py | 0 Scripts/VaDump.py | 0 Scripts/VirusTotal.py | 0 Scripts/callstack_test.py | 0 Scripts/msdnapihelp.py | 0 api_contents.txt | 172 +- basetsd.h | 4 - build.py | 7 +- chkapi.py | 312 -- examples/chooser.py | 0 examples/colours.py | 0 examples/debughook.py | 0 examples/ex1_idaapi.py | 0 examples/ex1_idautils.py | 0 examples/ex_actions.py | 0 examples/ex_add_menu_item.py | 18 - examples/ex_askusingform.py | 2 + examples/ex_choose2.py | 0 examples/ex_cli.py | 0 examples/ex_custdata.py | 0 examples/ex_custview.py | 0 examples/ex_dbg.py | 0 examples/ex_debug_names.py | 0 examples/ex_func_chooser.py | 0 examples/ex_gdl_qflow_chart.py | 0 examples/ex_graph.py | 0 examples/ex_imports.py | 0 examples/ex_prefix_plugin.py | 0 examples/ex_pyqt.py | 9 +- examples/ex_pyside.py | 34 - examples/ex_strings.py | 0 examples/ex_uihook.py | 0 examples/hotkey.py | 0 examples/idapythonrc.py | 0 examples/structure.py | 0 examples/vds6.py | 93 + idaapi.cpp | 4 - idapython.vcxproj | 10 +- makefile | 481 ++-- out_of_tree/parsed_notifications.zip | Bin 0 -> 574022 bytes patch_directors_cc.py | 80 - pydoc_injections.txt | 214 ++ python.cfg | 4 + python.cpp | 520 ++-- python/idaapi.py | 42 + python/idautils.py | 197 +- python/idc.py | 1922 +++++++------ python/init.py | 40 +- pywraps.cpp | 2515 +++++++++++++++++ pywraps.hpp | 562 +++- pywraps/deploy_all.py | 212 -- pywraps/driver.cpp | 172 ++ pywraps/driver_bytes.cpp | 19 + pywraps/driver_chooser.cpp | 109 + pywraps/driver_cli.cpp | 29 + pywraps/driver_custview.cpp | 384 +++ pywraps/driver_dbg.cpp | 79 + pywraps/driver_diskio.cpp | 47 + pywraps/driver_expr.cpp | 56 + pywraps/driver_graph.cpp | 44 + pywraps/driver_kernwin.cpp | 78 + pywraps/driver_nalt.cpp | 18 + pywraps/driver_notifywhen.cpp | 36 + pywraps/link_gen.py | 336 --- pywraps/py_area.hpp | 11 + ...{py_custdata.hpp => py_bytes_custdata.hpp} | 16 +- .../{py_custdata.py => py_bytes_custdata.py} | 40 +- pywraps/py_dbg.hpp | 186 -- pywraps/py_dbg.py | 8 + pywraps/py_expr.hpp | 5 +- pywraps/py_expr.py | 35 +- pywraps/py_frame.hpp | 59 + pywraps/py_gdl.py | 6 +- pywraps/py_graph.hpp | 26 +- pywraps/py_graph.py | 15 +- pywraps/py_hexrays.hpp | 439 +++ pywraps/py_hexrays.py | 419 +++ pywraps/py_idaapi.hpp | 1789 +----------- pywraps/py_idaapi.py | 204 +- ..._linput.hpp => py_idaapi_loader_input.hpp} | 10 +- pywraps/py_idbhooks.hpp | 640 ----- pywraps/py_idd.hpp | 38 +- pywraps/py_idd.py | 55 +- pywraps/py_idp.hpp | 1067 +------ pywraps/py_idp.py | 232 ++ pywraps/py_idp_idbhooks.hpp | 51 + pywraps/py_kernwin.hpp | 284 +- pywraps/py_kernwin.py | 58 +- ...ngform.hpp => py_kernwin_askusingform.hpp} | 37 +- ...singform.py => py_kernwin_askusingform.py} | 150 +- .../{py_choose.hpp => py_kernwin_choose.hpp} | 10 +- .../{py_choose.py => py_kernwin_choose.py} | 14 +- ...{py_choose2.hpp => py_kernwin_choose2.hpp} | 52 +- .../{py_choose2.py => py_kernwin_choose2.py} | 62 +- pywraps/{py_cli.hpp => py_kernwin_cli.hpp} | 8 +- pywraps/{py_cli.py => py_kernwin_cli.py} | 32 +- ...y_custview.hpp => py_kernwin_custview.hpp} | 10 +- ...{py_custview.py => py_kernwin_custview.py} | 97 +- pywraps/py_kernwin_idaview.hpp | 92 + pywraps/py_kernwin_idaview.py | 25 + ...{py_plgform.hpp => py_kernwin_plgform.hpp} | 14 +- .../{py_plgform.py => py_kernwin_plgform.py} | 13 +- pywraps/py_lines.hpp | 34 +- pywraps/py_lines.py | 5 +- pywraps/py_nalt.hpp | 122 +- pywraps/py_nalt.py | 66 +- pywraps/py_name.hpp | 2 +- pywraps/py_name.py | 2 +- pywraps/py_pro.hpp | 106 + pywraps/py_pro.py | 87 + pywraps/py_typeinf.hpp | 144 +- pywraps/py_typeinf.py | 4 + pywraps/py_ua.hpp | 116 - pywraps/py_ua.py | 363 +-- pywraps/py_view.hpp | 1052 ------- pywraps/py_view.py | 144 - pywraps/py_xref.hpp | 118 + pywraps/py_xref.py | 8 + pywraps/pywraps.vcxproj | 663 ----- pywraps/pywraps.vcxproj.filters | 518 ---- pywraps/sidaapi.py | 228 -- pywraps/swig_stub.cpp | 6 + pywraps/swig_stub.h | 25 + swig/allins.i | 4 + swig/area.i | 22 +- swig/askusingform.i | 17 - swig/auto.i | 14 +- swig/bytes.i | 62 +- swig/choose.i | 12 - swig/choose2.i | 17 - swig/cli.i | 17 - swig/custdata.i | 15 - swig/custview.i | 17 - swig/dbg.i | 54 +- swig/diskio.i | 11 +- swig/entry.i | 7 +- swig/enum.i | 7 +- swig/expr.i | 18 +- swig/fixup.i | 6 +- swig/frame.i | 26 +- swig/funcs.i | 20 +- swig/gdl.i | 6 + swig/graph.i | 3 + swig/hexrays.i | 805 +----- swig/ida.i | 12 +- swig/idaapi.i | 323 +-- swig/idbhooks.i | 6 + swig/idd.i | 13 + swig/idp.i | 64 +- swig/ints.i | 4 - swig/kernwin.i | 172 +- swig/lines.i | 20 - swig/loader.i | 44 +- swig/moves.i | 11 +- swig/nalt.i | 16 +- swig/name.i | 17 +- swig/netnode.i | 9 +- swig/offset.i | 5 +- swig/plgform.i | 17 - swig/pro.i | 200 +- swig/queue.i | 10 +- swig/registry.i | 4 +- swig/search.i | 3 + swig/segment.i | 10 +- swig/srarea.i | 39 +- swig/strlist.i | 8 +- swig/struct.i | 7 +- swig/typeconv.i | 54 +- swig/typeinf.i | 36 +- swig/ua.i | 21 +- swig/view.i | 16 - swig/xref.i | 21 +- tools/chkapi.py | 450 +++ tools/deploy.py | 90 +- tools/deploy/header.i.in | 830 ++++++ hrdoc.cfg => tools/docs/hrdoc.cfg.in | 5 +- hrdoc.css => tools/docs/hrdoc.css | 0 hrdoc.py => tools/docs/hrdoc.py | 27 +- tools/funlines.py | 85 + tools/gendocs.py | 25 - tools/genhooks/genhooks.py | 4 +- tools/genhooks/recipe_idphooks.py | 4 + tools/genidaapi.py | 24 + tools/genswigheader.py | 42 + tools/idapython_implib.def.in | 66 + inject_pydoc.py => tools/inject_pydoc.py | 66 +- tools/patch_constants.py | 192 ++ tools/patch_directors_cc.py | 82 + 197 files changed, 11206 insertions(+), 12091 deletions(-) mode change 100644 => 100755 Scripts/3rd/BboeVt.py mode change 100644 => 100755 Scripts/AsmViewer.py mode change 100644 => 100755 Scripts/CallStackWalk.py mode change 100644 => 100755 Scripts/DbgCmd.py mode change 100644 => 100755 Scripts/DrvsDispatch.py mode change 100644 => 100755 Scripts/ExchainDump.py mode change 100644 => 100755 Scripts/FindInstructions.py mode change 100644 => 100755 Scripts/ImpRef.py mode change 100644 => 100755 Scripts/ImportExportViewer.py mode change 100644 => 100755 Scripts/SEHGraph.py mode change 100644 => 100755 Scripts/VaDump.py mode change 100644 => 100755 Scripts/VirusTotal.py mode change 100644 => 100755 Scripts/callstack_test.py mode change 100644 => 100755 Scripts/msdnapihelp.py delete mode 100644 basetsd.h delete mode 100644 chkapi.py mode change 100644 => 100755 examples/chooser.py mode change 100644 => 100755 examples/colours.py mode change 100644 => 100755 examples/debughook.py mode change 100644 => 100755 examples/ex1_idaapi.py mode change 100644 => 100755 examples/ex1_idautils.py mode change 100644 => 100755 examples/ex_actions.py delete mode 100644 examples/ex_add_menu_item.py mode change 100644 => 100755 examples/ex_askusingform.py mode change 100644 => 100755 examples/ex_choose2.py mode change 100644 => 100755 examples/ex_cli.py mode change 100644 => 100755 examples/ex_custdata.py mode change 100644 => 100755 examples/ex_custview.py mode change 100644 => 100755 examples/ex_dbg.py mode change 100644 => 100755 examples/ex_debug_names.py mode change 100644 => 100755 examples/ex_func_chooser.py mode change 100644 => 100755 examples/ex_gdl_qflow_chart.py mode change 100644 => 100755 examples/ex_graph.py mode change 100644 => 100755 examples/ex_imports.py mode change 100644 => 100755 examples/ex_prefix_plugin.py mode change 100644 => 100755 examples/ex_pyqt.py delete mode 100644 examples/ex_pyside.py mode change 100644 => 100755 examples/ex_strings.py mode change 100644 => 100755 examples/ex_uihook.py mode change 100644 => 100755 examples/hotkey.py mode change 100644 => 100755 examples/idapythonrc.py mode change 100644 => 100755 examples/structure.py create mode 100644 examples/vds6.py delete mode 100644 idaapi.cpp create mode 100644 out_of_tree/parsed_notifications.zip delete mode 100644 patch_directors_cc.py create mode 100644 pydoc_injections.txt create mode 100644 python/idaapi.py mode change 100644 => 100755 python/idautils.py mode change 100644 => 100755 python/idc.py mode change 100644 => 100755 python/init.py create mode 100644 pywraps.cpp delete mode 100644 pywraps/deploy_all.py create mode 100644 pywraps/driver.cpp create mode 100644 pywraps/driver_bytes.cpp create mode 100644 pywraps/driver_chooser.cpp create mode 100644 pywraps/driver_cli.cpp create mode 100644 pywraps/driver_custview.cpp create mode 100644 pywraps/driver_dbg.cpp create mode 100644 pywraps/driver_diskio.cpp create mode 100644 pywraps/driver_expr.cpp create mode 100644 pywraps/driver_graph.cpp create mode 100644 pywraps/driver_kernwin.cpp create mode 100644 pywraps/driver_nalt.cpp create mode 100644 pywraps/driver_notifywhen.cpp delete mode 100755 pywraps/link_gen.py create mode 100644 pywraps/py_area.hpp rename pywraps/{py_custdata.hpp => py_bytes_custdata.hpp} (98%) rename pywraps/{py_custdata.py => py_bytes_custdata.py} (87%) create mode 100644 pywraps/py_dbg.py create mode 100644 pywraps/py_frame.hpp create mode 100644 pywraps/py_hexrays.hpp create mode 100644 pywraps/py_hexrays.py rename pywraps/{py_linput.hpp => py_idaapi_loader_input.hpp} (98%) delete mode 100644 pywraps/py_idbhooks.hpp create mode 100644 pywraps/py_idp.py create mode 100644 pywraps/py_idp_idbhooks.hpp rename pywraps/{py_askusingform.hpp => py_kernwin_askusingform.hpp} (90%) rename pywraps/{py_askusingform.py => py_kernwin_askusingform.py} (91%) rename pywraps/{py_choose.hpp => py_kernwin_choose.hpp} (92%) rename pywraps/{py_choose.py => py_kernwin_choose.py} (86%) rename pywraps/{py_choose2.hpp => py_kernwin_choose2.hpp} (96%) rename pywraps/{py_choose2.py => py_kernwin_choose2.py} (78%) rename pywraps/{py_cli.hpp => py_kernwin_cli.hpp} (98%) rename pywraps/{py_cli.py => py_kernwin_cli.py} (80%) rename pywraps/{py_custview.hpp => py_kernwin_custview.hpp} (99%) rename pywraps/{py_custview.py => py_kernwin_custview.py} (67%) create mode 100644 pywraps/py_kernwin_idaview.hpp create mode 100644 pywraps/py_kernwin_idaview.py rename pywraps/{py_plgform.hpp => py_kernwin_plgform.hpp} (93%) rename pywraps/{py_plgform.py => py_kernwin_plgform.py} (93%) create mode 100644 pywraps/py_pro.hpp create mode 100644 pywraps/py_pro.py delete mode 100644 pywraps/py_view.hpp delete mode 100644 pywraps/py_view.py create mode 100644 pywraps/py_xref.hpp create mode 100644 pywraps/py_xref.py delete mode 100644 pywraps/pywraps.vcxproj delete mode 100644 pywraps/pywraps.vcxproj.filters delete mode 100755 pywraps/sidaapi.py create mode 100644 pywraps/swig_stub.cpp create mode 100644 pywraps/swig_stub.h delete mode 100644 swig/askusingform.i delete mode 100644 swig/choose.i delete mode 100644 swig/choose2.i delete mode 100644 swig/cli.i delete mode 100644 swig/custdata.i delete mode 100644 swig/custview.i delete mode 100644 swig/plgform.i delete mode 100644 swig/view.i create mode 100644 tools/chkapi.py create mode 100644 tools/deploy/header.i.in rename hrdoc.cfg => tools/docs/hrdoc.cfg.in (98%) rename hrdoc.css => tools/docs/hrdoc.css (100%) rename hrdoc.py => tools/docs/hrdoc.py (84%) create mode 100644 tools/funlines.py delete mode 100644 tools/gendocs.py create mode 100644 tools/genidaapi.py create mode 100644 tools/genswigheader.py create mode 100644 tools/idapython_implib.def.in rename inject_pydoc.py => tools/inject_pydoc.py (88%) create mode 100644 tools/patch_constants.py create mode 100644 tools/patch_directors_cc.py diff --git a/Scripts/3rd/BboeVt.py b/Scripts/3rd/BboeVt.py old mode 100644 new mode 100755 diff --git a/Scripts/AsmViewer.py b/Scripts/AsmViewer.py old mode 100644 new mode 100755 diff --git a/Scripts/CallStackWalk.py b/Scripts/CallStackWalk.py old mode 100644 new mode 100755 diff --git a/Scripts/DbgCmd.py b/Scripts/DbgCmd.py old mode 100644 new mode 100755 diff --git a/Scripts/DrvsDispatch.py b/Scripts/DrvsDispatch.py old mode 100644 new mode 100755 diff --git a/Scripts/ExchainDump.py b/Scripts/ExchainDump.py old mode 100644 new mode 100755 diff --git a/Scripts/FindInstructions.py b/Scripts/FindInstructions.py old mode 100644 new mode 100755 diff --git a/Scripts/ImpRef.py b/Scripts/ImpRef.py old mode 100644 new mode 100755 diff --git a/Scripts/ImportExportViewer.py b/Scripts/ImportExportViewer.py old mode 100644 new mode 100755 diff --git a/Scripts/SEHGraph.py b/Scripts/SEHGraph.py old mode 100644 new mode 100755 diff --git a/Scripts/VaDump.py b/Scripts/VaDump.py old mode 100644 new mode 100755 diff --git a/Scripts/VirusTotal.py b/Scripts/VirusTotal.py old mode 100644 new mode 100755 diff --git a/Scripts/callstack_test.py b/Scripts/callstack_test.py old mode 100644 new mode 100755 diff --git a/Scripts/msdnapihelp.py b/Scripts/msdnapihelp.py old mode 100644 new mode 100755 diff --git a/api_contents.txt b/api_contents.txt index 6593fb9..99cb140 100644 --- a/api_contents.txt +++ b/api_contents.txt @@ -95,6 +95,7 @@ 'IDP_Hooks_add_dref', 'IDP_Hooks_add_func', 'IDP_Hooks_adjust_libfunc_ea', + 'IDP_Hooks_adjust_refinfo', 'IDP_Hooks_assemble', 'IDP_Hooks_auto_empty', 'IDP_Hooks_auto_empty_finally', @@ -140,7 +141,6 @@ 'IDP_Hooks_get_func_cvtarg_map', 'IDP_Hooks_get_jump_target', 'IDP_Hooks_get_macro_insn_head', - 'IDP_Hooks_get_operand_info', 'IDP_Hooks_get_operand_string', 'IDP_Hooks_get_reg_info2', 'IDP_Hooks_get_reg_name', @@ -164,7 +164,6 @@ 'IDP_Hooks_kernel_config_loaded', 'IDP_Hooks_load_idasgn', 'IDP_Hooks_loader', - 'IDP_Hooks_loader_elf_machine', 'IDP_Hooks_loader_finished', 'IDP_Hooks_make_code', 'IDP_Hooks_make_data', @@ -179,7 +178,6 @@ 'IDP_Hooks_newfile', 'IDP_Hooks_newprc', 'IDP_Hooks_newseg', - 'IDP_Hooks_obsolete_get_operand_info', 'IDP_Hooks_obsolete_makemicro', 'IDP_Hooks_oldfile', 'IDP_Hooks_out_3byte', @@ -283,6 +281,7 @@ '_askseg', '_askseg__varargs__', '_decompile', + '_kludge_use_TPopupMenu', 'a2funcoff', 'accepts_udts', 'action_ctx_base_t_action_get', @@ -341,6 +340,7 @@ 'add_const', 'add_cref', 'add_custom_viewer_popup_item', + 'add_custom_viewer_popup_item__SWIG_1', 'add_dref', 'add_encoding', 'add_entry', @@ -416,6 +416,7 @@ 'append_func_tail', 'append_name', 'append_struct_fields2', + 'append_tinfo_covered', 'append_varloc', 'apply_callee_tinfo', 'apply_cdecl', @@ -557,6 +558,7 @@ 'argloc_t_is_custom', 'argloc_t_is_ea', 'argloc_t_is_fragmented', + 'argloc_t_is_mixed_scattered', 'argloc_t_is_reg', 'argloc_t_is_reg1', 'argloc_t_is_reg2', @@ -992,10 +994,9 @@ 'calc_reference_target', 'calc_stkvar_struc_offset', 'calc_switch_cases', - 'calc_switch_cases__SWIG_0', - 'calc_switch_cases__SWIG_1', 'calc_target', 'calc_thunk_func_target', + 'calc_tinfo_gaps', 'calc_type_size', 'calc_varloc_info', 'calcexpr', @@ -1456,6 +1457,7 @@ 'cinsn_t_creturn_get', 'cinsn_t_cswitch_get', 'cinsn_t_cwhile_get', + 'cinsn_t_is_epilog', 'cinsn_t_is_ordinary_flow', 'cinsn_t_new_insn', 'cinsn_t_print1', @@ -1540,6 +1542,7 @@ 'citem_t_print1', 'citem_t_swap', 'cleanup_appcall', + 'cleanup_name', 'cleanup_varloc', 'clearBreak', 'clear_cached_cfuncs', @@ -1599,7 +1602,8 @@ 'codeflag', 'compact_til', 'compare', - 'compare__SWIG_0', + 'compare', + 'compare', 'compare__SWIG_1', 'compare__SWIG_12', 'compare__SWIG_13', @@ -1621,6 +1625,7 @@ 'compare__SWIG_28', 'compare__SWIG_29', 'compare__SWIG_3', + 'compare__SWIG_3', 'compare__SWIG_30', 'compare__SWIG_4', 'compare__SWIG_5', @@ -1657,6 +1662,7 @@ 'convert_argloc_to_varloc', 'convert_encoding', 'convert_pt_flags_to_hti', + 'convert_to_user_call', 'convert_varloc_to_argloc', 'copy_srareas', 'copy_tinfo_t', @@ -1672,6 +1678,7 @@ 'create_helper__varargs__', 'create_insn', 'create_memory_linput', + 'create_menu', 'create_numbered_type_name', 'create_numbered_type_reference', 'create_source_viewer', @@ -1679,6 +1686,7 @@ 'create_switch_xrefs', 'create_tform', 'create_tinfo', + 'create_toolbar', 'create_typedef', 'create_typedef__SWIG_0', 'create_typedef__SWIG_1', @@ -2113,6 +2121,10 @@ 'delete_lvar_uservec_t', 'delete_lvars_t', 'delete_member_t', + 'delete_memreg_info_t', + 'delete_memreg_infos_t', + 'delete_menu', + 'delete_microcode_filter_t', 'delete_module_info_t', 'delete_netnode', 'delete_node_info_t', @@ -2140,6 +2152,7 @@ 'delete_refinfo_t', 'delete_reg_info_t', 'delete_regarg_t', + 'delete_reginfovec_t', 'delete_regobj_t', 'delete_regobjs_t', 'delete_regval_t', @@ -2178,6 +2191,7 @@ 'delete_til_t', 'delete_tinfo_t', 'delete_tinfo_visitor_t', + 'delete_toolbar', 'delete_treeloc_t', 'delete_twinpos_t', 'delete_type_attr_t', @@ -2188,6 +2202,9 @@ 'delete_type_visitor_t', 'delete_typedef_type_data_t', 'delete_uchar_array', + 'delete_udc_filter_t', + 'delete_udcall_map_iterator_t', + 'delete_udcall_t', 'delete_udt_member_t', 'delete_udt_type_data_t', 'delete_udtmembervec_t', @@ -2299,9 +2316,11 @@ 'disown_ctree_visitor_t', 'disown_enum_member_visitor_t', 'disown_highlighter_cbs_t', + 'disown_microcode_filter_t', 'disown_struct_field_visitor_t', 'disown_text_sink_t', 'disown_tinfo_visitor_t', + 'disown_udc_filter_t', 'disown_user_lvar_modifier_t', 'display_complex_call_chart', 'display_copyright_warning', @@ -2906,6 +2925,7 @@ 'get_c_header_path', 'get_c_macros', 'get_call_tev_callee', + 'get_chooser_data', 'get_chooser_obj', 'get_cmt', 'get_colored_demangled_name', @@ -3077,6 +3097,7 @@ 'get_hidden_area_num', 'get_hidden_area_qty', 'get_highlighted_identifier', + 'get_ida_subdirs', 'get_idainfo_by_type', 'get_idainfo_by_type2', 'get_idainfo_by_type3', @@ -3230,6 +3251,7 @@ 'get_reg_name', 'get_reg_val', 'get_reg_vals', + 'get_registered_actions', 'get_ret_tev_return', 'get_root_filename', 'get_running_notification', @@ -3318,7 +3340,6 @@ 'get_tev_info', 'get_tev_memory_info', 'get_tev_qty', - 'get_tev_reg_mem', 'get_tev_reg_mem_ea', 'get_tev_reg_mem_qty', 'get_tev_reg_val', @@ -3561,6 +3582,7 @@ 'idainfo_is_64bit', 'idainfo_is_dll', 'idainfo_is_flat_off32', + 'idainfo_is_hard_float', 'idainfo_is_mem_aligned4', 'idainfo_is_snapshot', 'idainfo_lenxref_get', @@ -3799,6 +3821,7 @@ 'install_command_interpreter', 'install_extlang', 'install_hexrays_callback', + 'install_microcode_filter', 'instant_dbgopts_t__pass_get', 'instant_dbgopts_t__pass_set', 'instant_dbgopts_t_attach_get', @@ -4193,6 +4216,7 @@ 'load_loader_module', 'load_plugin', 'load_til', + 'load_til2', 'load_til__SWIG_0', 'load_til__SWIG_1', 'load_til_header', @@ -4491,7 +4515,50 @@ 'member_t_soff_get', 'member_t_soff_set', 'member_t_unimem', + 'memreg_info_t_ea_get', + 'memreg_info_t_ea_set', + 'memreg_info_t_get_bytes', + 'memreg_infos_t___getitem__', + 'memreg_infos_t___len__', + 'memreg_infos_t___setitem__', + 'memreg_infos_t_at', + 'memreg_infos_t_back', + 'memreg_infos_t_back__SWIG_0', + 'memreg_infos_t_back__SWIG_1', + 'memreg_infos_t_begin', + 'memreg_infos_t_begin__SWIG_0', + 'memreg_infos_t_begin__SWIG_1', + 'memreg_infos_t_capacity', + 'memreg_infos_t_clear', + 'memreg_infos_t_empty', + 'memreg_infos_t_end', + 'memreg_infos_t_end__SWIG_0', + 'memreg_infos_t_end__SWIG_1', + 'memreg_infos_t_erase', + 'memreg_infos_t_erase__SWIG_0', + 'memreg_infos_t_erase__SWIG_1', + 'memreg_infos_t_extract', + 'memreg_infos_t_front', + 'memreg_infos_t_front__SWIG_0', + 'memreg_infos_t_front__SWIG_1', + 'memreg_infos_t_grow', + 'memreg_infos_t_inject', + 'memreg_infos_t_insert', + 'memreg_infos_t_pop_back', + 'memreg_infos_t_push_back', + 'memreg_infos_t_push_back__SWIG_0', + 'memreg_infos_t_push_back__SWIG_1', + 'memreg_infos_t_qclear', + 'memreg_infos_t_reserve', + 'memreg_infos_t_resize', + 'memreg_infos_t_resize__SWIG_0', + 'memreg_infos_t_resize__SWIG_1', + 'memreg_infos_t_size', + 'memreg_infos_t_swap', + 'memreg_infos_t_truncate', 'micro_err_format', + 'microcode_filter_t_apply', + 'microcode_filter_t_match', 'modify_user_lvars', 'module_info_t_base_get', 'module_info_t_base_set', @@ -4634,6 +4701,8 @@ 'new_argloc_t__SWIG_0', 'new_argloc_t__SWIG_1', 'new_argpart_t', + 'new_argpart_t__SWIG_0', + 'new_argpart_t__SWIG_1', 'new_array_parameters_t', 'new_array_type_data_t', 'new_asm_t', @@ -4841,6 +4910,11 @@ 'new_lvar_uservec_t', 'new_lvars_t', 'new_member_t', + 'new_memreg_info_t', + 'new_memreg_infos_t', + 'new_memreg_infos_t__SWIG_0', + 'new_memreg_infos_t__SWIG_1', + 'new_microcode_filter_t', 'new_module_info_t', 'new_netnode', 'new_netnode__SWIG_0', @@ -4888,6 +4962,9 @@ 'new_refinfo_t', 'new_reg_info_t', 'new_regarg_t', + 'new_reginfovec_t', + 'new_reginfovec_t__SWIG_0', + 'new_reginfovec_t__SWIG_1', 'new_regobj_t', 'new_regobjs_t', 'new_regval_t', @@ -4954,6 +5031,9 @@ 'new_typedef_type_data_t__SWIG_0', 'new_typedef_type_data_t__SWIG_1', 'new_uchar_array', + 'new_udc_filter_t', + 'new_udcall_map_iterator_t', + 'new_udcall_t', 'new_udt_member_t', 'new_udt_type_data_t', 'new_udtmembervec_t', @@ -5198,6 +5278,7 @@ 'parse_decls', 'parse_reg_name', 'parse_types2', + 'parse_user_call', 'partial_type_num', 'patch_byte', 'patch_long', @@ -5798,6 +5879,52 @@ 'regarg_t_reg_set', 'regarg_t_type_get', 'regarg_t_type_set', + 'reginfovec_t___eq__', + 'reginfovec_t___getitem__', + 'reginfovec_t___len__', + 'reginfovec_t___ne__', + 'reginfovec_t___setitem__', + 'reginfovec_t__del', + 'reginfovec_t_add_unique', + 'reginfovec_t_at', + 'reginfovec_t_back', + 'reginfovec_t_back__SWIG_0', + 'reginfovec_t_back__SWIG_1', + 'reginfovec_t_begin', + 'reginfovec_t_begin__SWIG_0', + 'reginfovec_t_begin__SWIG_1', + 'reginfovec_t_capacity', + 'reginfovec_t_clear', + 'reginfovec_t_empty', + 'reginfovec_t_end', + 'reginfovec_t_end__SWIG_0', + 'reginfovec_t_end__SWIG_1', + 'reginfovec_t_erase', + 'reginfovec_t_erase__SWIG_0', + 'reginfovec_t_erase__SWIG_1', + 'reginfovec_t_extract', + 'reginfovec_t_find', + 'reginfovec_t_find__SWIG_0', + 'reginfovec_t_find__SWIG_1', + 'reginfovec_t_front', + 'reginfovec_t_front__SWIG_0', + 'reginfovec_t_front__SWIG_1', + 'reginfovec_t_grow', + 'reginfovec_t_has', + 'reginfovec_t_inject', + 'reginfovec_t_insert', + 'reginfovec_t_pop_back', + 'reginfovec_t_push_back', + 'reginfovec_t_push_back__SWIG_0', + 'reginfovec_t_push_back__SWIG_1', + 'reginfovec_t_qclear', + 'reginfovec_t_reserve', + 'reginfovec_t_resize', + 'reginfovec_t_resize__SWIG_0', + 'reginfovec_t_resize__SWIG_1', + 'reginfovec_t_size', + 'reginfovec_t_swap', + 'reginfovec_t_truncate', 'register_action', 'register_addon', 'register_and_attach_to_menu', @@ -5924,6 +6051,7 @@ 'resolve_typedef2', 'restore_database_snapshot', 'restore_user_cmts', + 'restore_user_defined_calls', 'restore_user_iflags', 'restore_user_labels', 'restore_user_lvar_settings', @@ -5952,6 +6080,7 @@ 'save_tinfo', 'save_trace_file', 'save_user_cmts', + 'save_user_defined_calls', 'save_user_iflags', 'save_user_labels', 'save_user_lvar_settings', @@ -6127,6 +6256,7 @@ 'set_const_cmt', 'set_const_name', 'set_custom_data_type_ids', + 'set_custom_fixup_ex', 'set_custom_viewer_qt_aware', 'set_dbg_default_options', 'set_dbg_options', @@ -6541,6 +6671,8 @@ 'tinfo_t___ne__', 'tinfo_t___str__', 'tinfo_t__print', + 'tinfo_t_append_covered', + 'tinfo_t_calc_gaps', 'tinfo_t_calc_purged_bytes', 'tinfo_t_calc_score', 'tinfo_t_calc_udt_aligns', @@ -6717,6 +6849,7 @@ 'tinfo_t_set_const', 'tinfo_t_set_declalign', 'tinfo_t_set_modifiers', + 'tinfo_t_set_named_type', 'tinfo_t_set_numbered_type', 'tinfo_t_set_volatile', 'tinfo_t_swap', @@ -6797,6 +6930,30 @@ 'uchar_array___setitem__', 'uchar_array_cast', 'uchar_array_frompointer', + 'udc_filter_t_apply', + 'udc_filter_t_init', + 'udc_filter_t_match', + 'udcall_map_begin', + 'udcall_map_clear', + 'udcall_map_end', + 'udcall_map_erase', + 'udcall_map_find', + 'udcall_map_first', + 'udcall_map_free', + 'udcall_map_insert', + 'udcall_map_iterator_t___eq__', + 'udcall_map_iterator_t___ne__', + 'udcall_map_iterator_t_x_get', + 'udcall_map_iterator_t_x_set', + 'udcall_map_new', + 'udcall_map_next', + 'udcall_map_prev', + 'udcall_map_second', + 'udcall_map_size', + 'udcall_t_name_get', + 'udcall_t_name_set', + 'udcall_t_tif_get', + 'udcall_t_tif_set', 'udt_member_t___eq__', 'udt_member_t___lt__', 'udt_member_t___ne__', @@ -6833,6 +6990,7 @@ 'udt_type_data_t_effalign_get', 'udt_type_data_t_effalign_set', 'udt_type_data_t_is_cppobj', + 'udt_type_data_t_is_last_baseclass', 'udt_type_data_t_is_msstruct', 'udt_type_data_t_is_unaligned', 'udt_type_data_t_is_union_get', diff --git a/basetsd.h b/basetsd.h deleted file mode 100644 index 5fdca7c..0000000 --- a/basetsd.h +++ /dev/null @@ -1,4 +0,0 @@ -#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 2a5c9a0..a5d4648 100755 --- a/build.py +++ b/build.py @@ -40,6 +40,7 @@ parser.add_argument("--swig-inc", type=str, help="Path(s) to the SWIG includes d parser.add_argument("--with-hexrays", help="Build Hex-Rays decompiler bindings (requires the 'hexrays.hpp' header to be present in the SDK's include/ directory)", default=False, action="store_true") parser.add_argument("--debug", help="Build debug version of the plugin", default=False, action="store_true") parser.add_argument("--python-home", help="Python home, where the 'include' directory can be found", default=None) +parser.add_argument("-j", "--parallel", action="store_true", help="Build in parallel", default=False) parser.add_argument("-v", "--verbose", help="Verbose mode", default=False, action="store_true") args = parser.parse_args() @@ -58,6 +59,8 @@ def run(proc_argv, env=None): def main(): argv = ["make"] + if args.parallel: + argv.append("-j") env = os.environ.copy() env["OUT_OF_TREE_BUILD"] = "1" if args.swig_bin: @@ -79,9 +82,7 @@ def main(): if "__EA64__" in env: del env["__EA64__"] print "\n### Building EAsize=%d(bit) version of the plugin" % (64 if ea64 else 32) - run(argv + ["dist"], env=env) # 'dist' depends on build phases - print "\n### Packaging" - run(argv + ["package"], env=env) + run(argv, env=env) # ----------------------------------------------------------------------- if __name__ == "__main__": diff --git a/chkapi.py b/chkapi.py deleted file mode 100644 index 1b884f5..0000000 --- a/chkapi.py +++ /dev/null @@ -1,312 +0,0 @@ -from __future__ import with_statement - -import sys, optparse, re, pprint - -ignorable_api_contents = [ - "qwait" - ] - -class TextStream: - def __init__(self, text): - self.text = text - self.point = 0 - self.maxpoint = len(self.text) - self.line_nr = 0 - self.char_nr = 0 - self.curline = None - - def line(self): - pt = self.point - self.advance_to_newline() - return self.text[pt : self.point] - - def char(self): - c, self.point = self.text[self.point], self.point + 1 - if c == '\n': - self.line_nr += 1 - self.char_nr = 0 - return c - - def advance_to_newline(self): - p = self.point - while self.text[p] != '\n': - p += 1 - p += 1 - self.line_nr += 1 - self.char_nr = 0 - self.point = p - - def empty(self): - return self.point >= self.maxpoint - - -if __name__ == "__main__": - - p = optparse.OptionParser(description='Check the generated idaapi_include.cpp file') - p.add_option('-v', "--verbose", dest="verbose", action="store_true") - p.add_option('-f', "--file", dest="path", type="string") - p.add_option('-x', "--with-hexrays", dest="with_hexrays", action="store_true") - p.add_option('-r', "--report-contents", dest="report_contents", type="string") - opts, _ = p.parse_args(sys.argv[1:]) - - api_contents = { - "functions" : [] - } - - if not opts.path: - p.print_help() - sys.exit(1) - - functions_coherence_base = { - # - # qstrvec_t: Specialized, internal, clink'ed object. - # - "_wrap_qstrvec_t_assign" : { - "mustcall" : "qstrvec_t_assign" - }, - "_wrap_qstrvec_t_addressof" : { - "mustcall" : "qstrvec_t_addressof" - }, - "_wrap_qstrvec_t_set" : { - "mustcall" : "qstrvec_t_set" - }, - "_wrap_qstrvec_t_from_list" : { - "mustcall" : "qstrvec_t_from_list" - }, - "_wrap_qstrvec_t_size" : { - "mustcall" : "qstrvec_t_size" - }, - "_wrap_qstrvec_t_get" : { - "mustcall" : "qstrvec_t_get" - }, - "_wrap_qstrvec_t_add" : { - "mustcall" : "qstrvec_t_add" - }, - "_wrap_qstrvec_t_clear" : { - "mustcall" : "qstrvec_t_clear" - }, - "_wrap_qstrvec_t_insert" : { - "mustcall" : "qstrvec_t_insert" - }, - "_wrap_qstrvec_t_remove" : { - "mustcall" : "qstrvec_t_remove" - }, - - # - # Misc. - # - "_wrap_areacb_t_get_type" : { - "mustcall" : "get_type", - }, - "_wrap_tinfo_t_deserialize__SWIG_2" : { - "mustcall" : "tinfo_t_deserialize__SWIG_2", - }, - "_wrap_get_bpt_group" : { - "mustcall" : "PyString_FromStringAndSize", - }, - "_wrap_get_jumptable_info" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_get_array_parameters" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_read_dbg_memory" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_write_dbg_memory" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_get_grp_bpts" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_generic_linput_t_read" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_generic_linput64_t_read64" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_linput_buffer_t_read" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_get_ptr_object_size" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_tag_strlen" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_get_next_member_idx" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - "_wrap_get_prev_member_idx" : { - "string" : "resultobj = PyLong_FromLongLong(result)", - }, - # char[ANY] out typemap - "_wrap_idainfo_tag_get" : { - "nostring" : " --size;", - }, - # char[ANY] varout typemap - "Swig_var_database_idb_get" : { - "nostring" : " --size;", - }, - } - - functions_coherence_hexrays = { - "_wrap_cfuncptr_t___str__" : { - "mustcall" : ["cfunc_t___str__", "PyString_FromStringAndSize"], - }, - "_wrap_cfunc_t___str__" : { - "mustcall" : ["cfunc_t___str__", "PyString_FromStringAndSize"], - }, - "_wrap_hexrays_failure_t_desc" : { - "mustcall" : "PyString_FromStringAndSize", - }, - "_wrap_vd_failure_t_desc" : { - "mustcall" : "PyString_FromStringAndSize", - }, - "_wrap_create_field_name" : { - "mustcall" : "PyString_FromStringAndSize", - }, - "delete_qrefcnt_t_Sl_cfunc_t_Sg_" : { - "mustcall" : "hexrays_deregister_python_cfuncptr_t_instance", - }, - "_wrap__decompile" : { - "mustcall" : "hexrays_register_python_cfuncptr_t_instance", - }, - "_wrap_vdui_t_cfunc_get" : { - "mustcall" : "hexrays_register_python_cfuncptr_t_instance", - }, - # - # qvector - # - "_wrap_strvec_t___len__" : { - "mustcall" : "qvector_Sl_simpleline_t_Sg____len__", - }, - "_wrap_strvec_t___setitem__" : { - "mustcall" : "qvector_Sl_simpleline_t_Sg____setitem__", - }, - "_wrap_strvec_t___getitem__" : { - "mustcall" : "qvector_Sl_simpleline_t_Sg____getitem__", - }, - # - # vdui_t::cfunc - # - "_wrap_vdui_t_cfunc_get" : { - "string" : "SWIGTYPE_p_qrefcnt_tT_cfunc_t_t", # proper typemap must be used - }, - } - - functions_coherence = functions_coherence_base.copy() - if opts.with_hexrays: - functions_coherence.update(functions_coherence_hexrays) - - # Mark all functions as not-spotted. - for fname in functions_coherence.keys(): - chk = functions_coherence[fname] - chk["spotted"] = False - - # Read and go through lines - with open(opts.path, "r") as f: - ts = TextStream(f.read()) - - def dbg(msg): - if opts.verbose: - print("DEBUG: %s" % msg) - - def is_fundecl(line): - if len(line) <= 2: - return False - if line[0:1].isspace(): - return False - if line[len(line)-1:] != "{": - return False - if line.find("(") == -1 or line.find(")") == -1: - return False - return True - - def collect_funbody_lines(ts): - pt = ts.point - braces_cnt = 1 - while True: - c = ts.char() - if c == "{": - braces_cnt = braces_cnt + 1 - elif c == "}": - braces_cnt = braces_cnt - 1 - if braces_cnt == 0: - break; - # TODO: Skip strings! - return ts.text[pt : ts.point].split("\n") - - api_fname_regex = re.compile(".*PyObject \\*_wrap_([^\\(]*)\\(.*\\).*") - - # Process lines - STATE_UNKNOWN = 0 - STATE_IN_FUN = 1 - state = STATE_UNKNOWN - while not ts.empty(): - line = ts.line().rstrip() - # dbg("Line: '%s'" % line) - if is_fundecl(line): - # dbg("Entering function (from line %d: '%s')" % (ts.line_nr, line)) - state = STATE_IN_FUN - funstart = line - match = api_fname_regex.match(funstart) - if match: - fname = match.group(1) - if fname not in ignorable_api_contents: - api_contents["functions"].append(fname) - funlines = collect_funbody_lines(ts) - # Do we care about this function? - check_for = None - for fname in functions_coherence.keys(): - if funstart.find(fname + "(") > -1: - check_for = fname - break - if check_for: - dbg("Checking function at line %d: '%s'" % (ts.line_nr, check_for)) - chk = functions_coherence[check_for] - chk["spotted"] = True - def look_for_stuff(stuff, isFuncall, lookForPresence=True): - if isinstance(stuff, str): - stuff = [stuff] - for bit in stuff: - found = False - bit_pat = ("%s(" % bit) if isFuncall else bit - dbg("Thing to look for: '%s'" % bit_pat) - for funline in funlines: - # dbg("Testing line: '%s'" % funline) - if funline.find(bit_pat) > -1: - found = True - break - if lookForPresence: - if not found: - raise Exception("Couldn't find '%s', from function '%s' (lines: %s)" % - (bit_pat, check_for, "\n".join([""] + funlines))) - else: - if found: - raise Exception("Did find unwanted '%s', from function '%s' (lines: %s)" % - (bit_pat, check_for, "\n".join([""] + funlines))) - - if "mustcall" in chk: - look_for_stuff(chk["mustcall"], True) - if "string" in chk: - look_for_stuff(chk["string"], False) - if "nostring" in chk: - look_for_stuff(chk["nostring"], False, lookForPresence=False) - - # Ensure all functions were spotted. - for fname in functions_coherence.keys(): - chk = functions_coherence[fname] - if not chk["spotted"]: - raise Exception("Couldn't spot function '%s'" % fname) - - # Report contents - if opts.report_contents: - # NB: we use "wb" here so that the endlines - # are written as-is, in Unix format - # and so 'diff' does not report bogus changes - # against the file from repository - with open(opts.report_contents, "wb") as f: - api_contents["functions"].sort() - f.write(pprint.pformat(api_contents)) - diff --git a/examples/chooser.py b/examples/chooser.py old mode 100644 new mode 100755 diff --git a/examples/colours.py b/examples/colours.py old mode 100644 new mode 100755 diff --git a/examples/debughook.py b/examples/debughook.py old mode 100644 new mode 100755 diff --git a/examples/ex1_idaapi.py b/examples/ex1_idaapi.py old mode 100644 new mode 100755 diff --git a/examples/ex1_idautils.py b/examples/ex1_idautils.py old mode 100644 new mode 100755 diff --git a/examples/ex_actions.py b/examples/ex_actions.py old mode 100644 new mode 100755 diff --git a/examples/ex_add_menu_item.py b/examples/ex_add_menu_item.py deleted file mode 100644 index c01e777..0000000 --- a/examples/ex_add_menu_item.py +++ /dev/null @@ -1,18 +0,0 @@ -import idaapi - -def cb(*args): - print("Callback called!") - return 1 - -try: - ex_addmenu_item_ctx - idaapi.del_menu_item(ex_addmenu_item_ctx) - print("Menu removed") - del ex_addmenu_item_ctx -except: - ex_addmenu_item_ctx = idaapi.add_menu_item("Search/", "X", "", 0, cb, tuple("hello world")) - if ex_addmenu_item_ctx is None: - print("Failed to add menu!") - del ex_addmenu_item_ctx - else: - print("Menu added successfully. Run the script again to delete the menu") \ No newline at end of file diff --git a/examples/ex_askusingform.py b/examples/ex_askusingform.py old mode 100644 new mode 100755 index 977e364..aef440d --- a/examples/ex_askusingform.py +++ b/examples/ex_askusingform.py @@ -50,6 +50,7 @@ Form Test {FormChangeCb} This is a string: +{cStr1}+ This is an address: +{cAddr1}+ +This is some HTML: +{cHtml1}+ Escape\{control} This is a string: '{cStr2}' @@ -85,6 +86,7 @@ The end! """, { 'cStr1': Form.StringLabel("Hello"), 'cStr2': Form.StringLabel("StringTest"), + 'cHtml1': Form.StringLabel("Is this red?", tp=Form.FT_HTML_LABEL), 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR), 'cVal1' : Form.NumericLabel(99, Form.FT_HEX), 'iStr1': Form.StringInput(), diff --git a/examples/ex_choose2.py b/examples/ex_choose2.py old mode 100644 new mode 100755 diff --git a/examples/ex_cli.py b/examples/ex_cli.py old mode 100644 new mode 100755 diff --git a/examples/ex_custdata.py b/examples/ex_custdata.py old mode 100644 new mode 100755 diff --git a/examples/ex_custview.py b/examples/ex_custview.py old mode 100644 new mode 100755 diff --git a/examples/ex_dbg.py b/examples/ex_dbg.py old mode 100644 new mode 100755 diff --git a/examples/ex_debug_names.py b/examples/ex_debug_names.py old mode 100644 new mode 100755 diff --git a/examples/ex_func_chooser.py b/examples/ex_func_chooser.py old mode 100644 new mode 100755 diff --git a/examples/ex_gdl_qflow_chart.py b/examples/ex_gdl_qflow_chart.py old mode 100644 new mode 100755 diff --git a/examples/ex_graph.py b/examples/ex_graph.py old mode 100644 new mode 100755 diff --git a/examples/ex_imports.py b/examples/ex_imports.py old mode 100644 new mode 100755 diff --git a/examples/ex_prefix_plugin.py b/examples/ex_prefix_plugin.py old mode 100644 new mode 100755 diff --git a/examples/ex_pyqt.py b/examples/ex_pyqt.py old mode 100644 new mode 100755 index f137122..6a0914d --- a/examples/ex_pyqt.py +++ b/examples/ex_pyqt.py @@ -1,5 +1,6 @@ + from idaapi import PluginForm -from PyQt4 import QtCore, QtGui +from PyQt5 import QtCore, QtGui, QtWidgets import sip class MyPluginFormClass(PluginForm): @@ -15,12 +16,12 @@ class MyPluginFormClass(PluginForm): def PopulateForm(self): # Create layout - layout = QtGui.QVBoxLayout() + layout = QtWidgets.QVBoxLayout() layout.addWidget( - QtGui.QLabel("Hello from PyQt")) + QtWidgets.QLabel("Hello from PyQt")) layout.addWidget( - QtGui.QLabel("Hello from IDAPython")) + QtWidgets.QLabel("Hello from IDAPython")) self.parent.setLayout(layout) diff --git a/examples/ex_pyside.py b/examples/ex_pyside.py deleted file mode 100644 index dba9911..0000000 --- a/examples/ex_pyside.py +++ /dev/null @@ -1,34 +0,0 @@ -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/ex_strings.py b/examples/ex_strings.py old mode 100644 new mode 100755 diff --git a/examples/ex_uihook.py b/examples/ex_uihook.py old mode 100644 new mode 100755 diff --git a/examples/hotkey.py b/examples/hotkey.py old mode 100644 new mode 100755 diff --git a/examples/idapythonrc.py b/examples/idapythonrc.py old mode 100644 new mode 100755 diff --git a/examples/structure.py b/examples/structure.py old mode 100644 new mode 100755 diff --git a/examples/vds6.py b/examples/vds6.py new file mode 100644 index 0000000..eac7657 --- /dev/null +++ b/examples/vds6.py @@ -0,0 +1,93 @@ + +""" +This is a crude (and not very pythonic) reimplementation of the example +hexrays plugin 'hexrays_sample6.cpp', shipped with the Hex-Rays decompiler. + +It modifies the decompilation output: removes some space characters. +""" + +import idautils +import idc +import ida_hexrays +import ida_lines + +def dbg(msg): + #print msg + pass + +def is_cident_char(c): + return c.isalnum() or c == '_' + +def my_tag_skipcodes(l, storage): + n = ida_lines.tag_skipcodes(l) + dbg("Skipping %d chars ('%s')" % (n, l[0:n])) + storage.append(l[0:n]) + return l[n:] + +def remove_spaces(sl): + dbg("*" * 80) + l = sl.line + out = [] + + def push(c): + dbg("Appending '%s'" % c) + out.append(c) + + # skip initial spaces, do not compress them + while True: + l = my_tag_skipcodes(l, out) + if not l: + break + c = l[0] + if not c.isspace(): + break + push(c) + l = l[1:] + + # remove all spaces except in string and char constants + delim = None # if not None, then we are skipping until 'delim' + last = None # last seen character + while True: + # go until comments + dbg("-" * 60) + nchars = ida_lines.tag_advance(l, 1) + push(l[0:nchars]) + l = l[nchars:] + l = my_tag_skipcodes(l, out) + if not l: + break + if l.startswith("//"): + push(l) + break + c = l[0] + dbg("c: '%s', last: '%s', l: '%s'" % (c, last, l)) + if delim: + # we're inside a literal. + if c == delim: + delim = None # literal ended + elif c == '"' or c == "'": + delim = c # string/char literal started + elif c.isspace(): + end = l.lstrip() + nptr = my_tag_skipcodes(end, out) + dbg("end: '%s', nptr: '%s'" % (end, nptr)) + # do not concatenate idents + if not is_cident_char(last) or not is_cident_char(nptr[0]): + l = end + c = l[0] if l else '' + dbg("new l: '%s'" % l) + last = l[0] if l else '' + + sl.line = "".join(out) + +def cb(event, *args): + if event == ida_hexrays.hxe_func_printed: + cf = args[0] + for sl in cf.get_pseudocode(): + remove_spaces(sl); + return 0 + +if ida_hexrays.init_hexrays_plugin(): + ida_hexrays.install_hexrays_callback(cb) +else: + print 'remove spaces: hexrays is not available.' diff --git a/idaapi.cpp b/idaapi.cpp deleted file mode 100644 index 6ca3913..0000000 --- a/idaapi.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// This file just includes the correct idaapi_include.cpp file -// We need it because we can not compile a file from a subdirectory - -#include diff --git a/idapython.vcxproj b/idapython.vcxproj index bfb6177..17090a7 100644 --- a/idapython.vcxproj +++ b/idapython.vcxproj @@ -1,5 +1,5 @@  - + Debug64 @@ -27,25 +27,25 @@ DynamicLibrary false MultiByte - v120 + v140 DynamicLibrary false MultiByte - v120 + v140 DynamicLibrary false MultiByte - v120 + v140 DynamicLibrary false MultiByte - v120 + v140 diff --git a/makefile b/makefile index 8443621..4552454 100644 --- a/makefile +++ b/makefile @@ -27,25 +27,17 @@ all: else PROC=python -STRIPOBJS=idaapi.cpp API_CONTENTS=api_contents.txt +PYDOC_INJECTIONS=pydoc_injections.txt IDA_INCLUDE=../../include -STAGING=./staging -ST_SWIG=$(STAGING)/swig -ST_SDK=$(STAGING)/idasdk -ST_PYW=$(STAGING)/pywraps -ST_PARSED_HEADERS=$(STAGING)/parsed_notifications/xml - -# ATM, 'dist' in in staging/, but that will change since -# staging directories will be $(SYSDIR)-dependent -DIST=$(STAGING)/dist +DIST=$(F)dist ifdef __NT__ SYSNAME=win + MSRUNTIME=/MD else -.NOTPARALLEL: endif ifdef __LINUX__ SYSNAME=linux @@ -62,11 +54,103 @@ ifdef __MAC__ DEFS=-D__MAC__ endif -O1=idaapi -ADDITIONAL_GOALS=pyfiles config $(TEST_IDC) +# O1=idaapi + +# __USE_RTTI__=1 + + +DONT_ERASE_LIB=1 -__USE_RTTI__=1 include ../plugin.mak +PLUGIN_SCRIPT= +ifdef __LINUX__ +OUTDLLOPTS=-Wl,-soname,$(notdir $(BINARY)) +else + ifdef __MAC__ + OUTDLLOPTS=-Wl,-install_name,@executable_path/plugins/$(notdir $(BINARY)) + endif +endif + + +ST_SWIG=$(F)swig +ifeq ($(OUT_OF_TREE_BUILD),) + ST_SDK=$(F)idasdk +else + ST_SDK=$(IDA_INCLUDE) +endif +ST_PYW=$(F)pywraps +ST_WRAP=$(F)wrappers +ST_PARSED_HEADERS_NOXML=$(F)parsed_notifications +ST_PARSED_HEADERS=$(ST_PARSED_HEADERS_NOXML)/xml +ST_API_CONTENTS=$(F)api_contents.txt.new +ST_PYDOC_INJECTIONS=$(F)pydoc_injections.txt + +# output directory for python scripts +DEPLOY_PYDIR=$(R)python +DEPLOY_INIT_PY=$(DEPLOY_PYDIR)/init.py +DEPLOY_IDC_PY=$(DEPLOY_PYDIR)/idc.py +DEPLOY_IDAUTILS_PY=$(DEPLOY_PYDIR)/idautils.py +ifeq ($(OUT_OF_TREE_BUILD),) + TEST_IDC=test_idc + DBLZIP_SCRIPT:=$(abspath ../../ida/build/dblzip.py) + PKGBIN_SCRIPT:=$(abspath ../../ida/build/pkgbin.py) +endif + +# +SDK_SOURCES=$(wildcard $(IDA_INCLUDE)/*.h) $(wildcard $(IDA_INCLUDE)/*.hpp) +ifeq ($(OUT_OF_TREE_BUILD),) + ST_SDK_TARGETS=$(SDK_SOURCES:$(IDA_INCLUDE)/%=$(ST_SDK)/%) +else + ST_SDK_TARGETS=$(SDK_SOURCES) +endif + +PYTHON_DYNLOAD=$(BIN_PATH)../python/lib/python2.7/lib-dynload +DEPLOY_LIBDIR=$(PYTHON_DYNLOAD)/ida_$(ADRSIZE) + +$(DEPLOY_LIBDIR): + -@if [ ! -d "$(DEPLOY_LIBDIR)" ] ; then mkdir -p 2>/dev/null $(DEPLOY_LIBDIR) ; fi + +$(DEPLOY_PYDIR): + -@if [ ! -d "$(DEPLOY_PYDIR)" ] ; then mkdir -p 2>/dev/null $(DEPLOY_PYDIR) ; fi + +ifdef __NT__ +MODULE_SFX=.pyd +else +MODULE_SFX=.so +endif + +# We are building 'MODULES_NAMES' from subvars because it appears some versions +# of make do not deal too well with '\'s, and introduce spaces, which later is +# problematic when substituting ' ' for ',' & passing modules list to scripts +MNAMES_0=allins area auto bytes dbg diskio entry enum expr fixup +MNAMES_1=fpro frame funcs gdl graph hexrays ida idaapi idd idp ints +MNAMES_2=kernwin lines loader moves nalt name netnode offset pro queue +MNAMES_3=registry search segment srarea strlist struct typeinf ua xref +MODULES_NAMES=$(MNAMES_0) $(MNAMES_1) $(MNAMES_2) $(MNAMES_3) + +MODULES=$(MODULES_NAMES:%=$(F)_ida_%$(MODULE_SFX)) +DEPLOYED_MODULES=$(MODULES_NAMES:%=$(DEPLOY_LIBDIR)/_ida_%$(MODULE_SFX)) +MODULES_OBJECTS=$(MODULES_NAMES:%=$(F)%$(O)) + +ALL_ST_SWIG=$(foreach mod,$(MODULES_NAMES),$(ST_SWIG)/$(mod).i) +ALL_ST_WRAP_CPP=$(foreach mod,$(MODULES_NAMES),$(ST_WRAP)/$(mod).cpp) +ALL_ST_WRAP_PY=$(foreach mod,$(MODULES_NAMES),$(ST_WRAP)/ida_$(mod).py) + +PYTHON_MODULES=$(MODULES_NAMES:%=$(DEPLOY_PYDIR)/ida_%.py) + +ifdef __NT__ +MODULE_LINKIDA= +CREATE_IMPLIB=$(RS)lib32.bat +IDAPYTHON_IMPLIB_DEF=$(F)idapython_implib.def +IDAPYTHON_IMPLIB_DEF_IN=tools/idapython_implib.def.in +IDAPYTHON_IMPLIB_PATH=$(F)python.lib +BINARY_LINKOPTS=/def:$(IDAPYTHON_IMPLIB_DEF) /IMPLIB:$(IDAPYTHON_IMPLIB_PATH) +RESFILES=$(IDAPYTHON_IMPLIB_DEF) +else +MODULE_LINKIDA=-L$(R) $(LINKIDA) $(BINARY) +endif + +all: objdir pyfiles config $(DEPLOYED_MODULES) $(PYTHON_MODULES) $(ST_API_CONTENTS) $(IDAPYTHON_IMPLIB) $(ST_PYDOC_INJECTIONS) #$(TEST_IDC) # used python version PYTHON_VERSION_MAJOR?=2 @@ -75,8 +159,8 @@ PYTHON_VERSION_MINOR?=7 # IDAPython version IDAPYTHON_VERSION_MAJOR=6 IDAPYTHON_VERSION_MINOR=9 -IDAPYTHON_VERSION_PATCH=0 -PACKAGE_NAME=idapython-$(IDAPYTHON_VERSION_MAJOR).$(IDAPYTHON_VERSION_MINOR).$(IDAPYTHON_VERSION_PATCH)-python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)-$(SYSNAME).zip +IDAPYTHON_VERSION_PATCH=5 +PACKAGE_NAME=idapython-$(IDAPYTHON_VERSION_MAJOR).$(IDAPYTHON_VERSION_MINOR).$(IDAPYTHON_VERSION_PATCH)-python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)-$(SYSNAME) # HIJACK the $(I) variable to point to our staging SDK @@ -87,19 +171,6 @@ ifdef __CODE_CHECKER__ OBJS:=$(filter-out $(OBJ1),$(OBJS)) endif -# output directory for python scripts - -ifeq ($(OUT_OF_TREE_BUILD),) - SCRIPTDIR=$(R)python - DEPLOY_INIT_PY=$(SCRIPTDIR)/init.py - DEPLOY_IDC_PY=$(SCRIPTDIR)/idc.py - DEPLOY_IDAUTILS_PY=$(SCRIPTDIR)/idautils.py - DEPLOY_PYTHON_CFG=$(C)python.cfg - TEST_IDC=test_idc -else - SCRIPTDIR=python -endif - ifdef __NT__ # os and compiler specific flags ifneq ($(UCRT_INCLUDE),) I_UCRT_INCLUDE=/I$(UCRT_INCLUDE) @@ -114,16 +185,18 @@ endif endif _SWIGFLAGS=-D__NT__ -DWIN32 -D_USRDLL -I$(PYTHON_DIR)/include SWIGINCLUDES?= # nothing - OSFLAGS=$(_SWIGFLAGS) -UNO_OBSOLETE_FUNCS + # FIXME: Cannot enable the .cfg file ATM, because there's just too many errors if I do. + PLATFORM_CFLAGS=$(_SWIGFLAGS) -UNO_OBSOLETE_FUNCS else # unix/mac ifdef __LINUX__ # use the precompiled 2.7 ifeq ($(OUT_OF_TREE_BUILD),) - PYDIR=$(shell pwd)/precompiled + # assign right away, so it won't be eval'd 10301829 times + PYDIR:=$(shell pwd)/precompiled PYLIBDIR=$(PYDIR) # copy these files to IDA's directory - PYLIBFILES=$(shell find precompiled/lib -type f) - PRECOMPILED_COPY=$(R)$(PYTHONLIBNAME) $(patsubst precompiled/%,$(SCRIPTDIR)/%,$(PYLIBFILES)) + PYLIBFILES:=$(shell find precompiled/lib -type f) + PRECOMPILED_COPY=$(R)$(PYTHONLIBNAME) $(patsubst precompiled/%,$(DEPLOY_PYDIR)/%,$(PYLIBFILES)) else PYDIR=$(IDAPYTHON_PYTHONHOME) PYLIBDIR=$(PYDIR)/lib @@ -138,40 +211,50 @@ else # unix/mac MACDEFINES=-DMACSDKVER=$(MACSDKVER) endif IDAPYTHON_CFLAGS=-w -g - OSFLAGS=$(SYS) -g $(PYTHON32_INCLUDE) $(ARCH_CFLAGS) $(PIC) -UNO_OBSOLETE_FUNCS # gcc flags + PLATFORM_CFLAGS=$(SYS) -g $(PYTHON32_INCLUDE) $(ARCH_CFLAGS) $(PIC) -UNO_OBSOLETE_FUNCS # gcc flags _SWIGFLAGS=$(DEFS) SWIGINCLUDES?=-I$(SWIGDIR)share/swig/$(SWIG_VERSION)/python -I$(SWIGDIR)share/swig/$(SWIG_VERSION) endif -SWIGFLAGS=$(_SWIGFLAGS) $(SWIGINCLUDES) +# Apparently that's not needed, but I don't understand why ATM, since doc says: +# ...Then, only modules compiled with SWIG_TYPE_TABLE set to myprojectname +# will share type information. So if your project has three modules, all three +# should be compiled with -DSWIG_TYPE_TABLE=myprojectname, and then these +# three modules will share type information. But any other project's +# types will not interfere or clash with the types in your module. +DEF_TYPE_TABLE=-DSWIG_TYPE_TABLE=idaapi +SWIGFLAGS=$(_SWIGFLAGS) $(SWIGINCLUDES) $(DEF_TYPE_TABLE) ADDITIONAL_LIBS=$(PYTHONLIB) +PUBTREE_DIR=$(F)/public_tree -.PHONY: pyfiles docs $(TEST_IDC) staging_dirs clean check_python dist dist_dirs dist_cfg package +.PHONY: pyfiles docs $(TEST_IDC) staging_dirs clean check_python package public_tree config: $(C)python.cfg clean: - rm -rf $(STAGING)/ obj/ + rm -rf obj/ -pyfiles: $(SCRIPTDIR)/idautils.py \ - $(SCRIPTDIR)/idc.py \ - $(SCRIPTDIR)/init.py \ - $(SCRIPTDIR)/idaapi.py +pyfiles: $(DEPLOY_PYDIR)/idautils.py \ + $(DEPLOY_PYDIR)/idc.py \ + $(DEPLOY_PYDIR)/init.py \ + $(DEPLOY_PYDIR)/idaapi.py GENHOOKS=tools/genhooks/ +_SPACE := $(null) # +_COMMA := , -$(DEPLOY_INIT_PY): python/init.py +$(DEPLOY_INIT_PY): python/init.py | $(DEPLOY_PYDIR) $(CP) $? $@ -$(DEPLOY_IDC_PY): python/idc.py +$(DEPLOY_IDC_PY): python/idc.py | $(DEPLOY_PYDIR) $(CP) $? $@ -$(DEPLOY_IDAUTILS_PY): python/idautils.py +$(DEPLOY_IDAUTILS_PY): python/idautils.py | $(DEPLOY_PYDIR) $(CP) $? $@ -$(SCRIPTDIR)/idaapi.py: $(F)idaapi.py - $(PYTHON) inject_pydoc.py swig/ $? $@ +$(DEPLOY_PYDIR)/idaapi.py: python/idaapi.py tools/genidaapi.py | $(DEPLOY_PYDIR) + $(PYTHON) tools/genidaapi.py -i $< -o $@ -m $(subst $(_SPACE),$(_COMMA),$(MODULES_NAMES)) -$(SCRIPTDIR)/lib/%: precompiled/lib/% +$(DEPLOY_PYDIR)/lib/%: precompiled/lib/% mkdir -p $(@D) cp $< $@ @chmod +w $@ @@ -184,41 +267,37 @@ $(R)$(PYTHONLIBNAME): $(PYDIR)/$(PYTHONLIBNAME) # ------------------------------------------------------------------------- # Hooks generation -$(ST_PARSED_HEADERS)/structprocessor__t.xml: $(I)idp.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) - @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg - -$(ST_PARSED_HEADERS)/dbg_8hpp.xml: $(I)dbg.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) - @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg - -$(ST_PARSED_HEADERS)/kernwin_8hpp.xml: $(I)kernwin.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) - @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg - -$(ST_PARSED_HEADERS)/namespaceidb__event.xml: $(I)kernwin.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) - @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg +# http://stackoverflow.com/questions/11032280/specify-doxygen-parameters-through-command-line +GENERATED_HEADERS=$(ST_PARSED_HEADERS)/headers_generated.marker +$(GENERATED_HEADERS): $(I)idp.hpp $(I)dbg.hpp $(I)kernwin.hpp $(GENHOOKS)doxy_gen_notifs.cfg $(ST_SDK_TARGETS) +ifeq ($(OUT_OF_TREE_BUILD),) + @( cat $(GENHOOKS)doxy_gen_notifs.cfg; echo "OUTPUT_DIRECTORY=$(ST_PARSED_HEADERS_NOXML)" ) | $(DOXYGEN_BIN) - +else + (cd $(F) && unzip ../../out_of_tree/parsed_notifications.zip) +endif + @touch $@ # staging_dirs: -@if [ ! -d "$(ST_SDK)" ] ; then mkdir -p 2>/dev/null $(ST_SDK) ; fi -@if [ ! -d "$(ST_SWIG)" ] ; then mkdir -p 2>/dev/null $(ST_SWIG) ; fi -@if [ ! -d "$(ST_PYW)" ] ; then mkdir -p 2>/dev/null $(ST_PYW) ; fi + -@if [ ! -d "$(ST_WRAP)" ] ; then mkdir -p 2>/dev/null $(ST_WRAP) ; fi -@if [ ! -d "$(ST_PARSED_HEADERS)" ] ; then mkdir -p 2>/dev/null $(ST_PARSED_HEADERS) ; fi # ------------------------------------------------------------------------- -# Rules for preparing 'staging/idasdk/*.h[pp]' +# obj/.../idasdk/*.h[pp] # -SDK_SOURCES=$(wildcard $(IDA_INCLUDE)/*.h) $(wildcard $(IDA_INCLUDE)/*.hpp) -ST_SDK_TARGETS=$(SDK_SOURCES:$(IDA_INCLUDE)/%=$(ST_SDK)/%) -$(ST_SDK)/%.h: $(IDA_INCLUDE)/%.h | staging_dirs - @$(CP) $^ $@ && chmod +rw $@ -$(ST_SDK)/%.hpp: $(IDA_INCLUDE)/%.hpp | staging_dirs - @$(CP) $^ $@ && chmod +rw $@ - +ifeq ($(OUT_OF_TREE_BUILD),) +$(ST_SDK)/%.h: $(IDA_INCLUDE)/%.h | staging_dirs $(PRECOMPILED_COPY) + $(PYTHON) ../../bin/update_sdk.py -filter-file -input $^ -output $@ +$(ST_SDK)/%.hpp: $(IDA_INCLUDE)/%.hpp | staging_dirs $(PRECOMPILED_COPY) + $(PYTHON) ../../bin/update_sdk.py -filter-file -input $^ -output $@ +endif # ------------------------------------------------------------------------- -# Rules for preparing 'staging/pywraps/*' +# obj/.../pywraps/* # -PYW_SOURCES=$(wildcard pywraps/*.hpp) $(wildcard pywraps/*.py) -ST_PYW_TARGETS=$(PYW_SOURCES:pywraps/%=$(ST_PYW)/%) $(ST_PYW)/%.hpp: pywraps/%.hpp | staging_dirs @$(CP) $^ $@ && chmod +rw $@ $(ST_PYW)/%.py: pywraps/%.py | staging_dirs @@ -226,121 +305,190 @@ $(ST_PYW)/%.py: pywraps/%.py | staging_dirs ifneq ($(OUT_OF_TREE_BUILD),) - # envvar HAS_HEXRAYS must have been set by build.py if needed + # envvar HAS_HEXRAYS must have been set by build.py if needed else + HAS_HEXRAYS=1 # force hexrays bindings +endif - # force hexrays bindings - HAS_HEXRAYS=1 - # These require special care, as they will have to be injected w/ hooks -- this - # only happens if we are sitting in the hexrays source tree; when published to - # the outside world, the pywraps must already contain the injected code. - $(ST_PYW)/py_idp.hpp: pywraps/py_idp.hpp \ +# These require special care, as they will have to be injected w/ hooks -- this +# only happens if we are sitting in the hexrays source tree; when published to +# the outside world, the pywraps must already contain the injected code. +$(ST_PYW)/py_idp.hpp: pywraps/py_idp.hpp \ $(I)idp.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_idphooks.py \ - $(ST_PARSED_HEADERS)/structprocessor__t.xml | staging_dirs $(SDK_SOURCES) + $(GENERATED_HEADERS) | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/structprocessor__t.xml -e idp_notify \ -r int -n 0 -m hookgenIDP -q "processor_t::" \ -R $(GENHOOKS)recipe_idphooks.py - $(ST_PYW)/py_idbhooks.hpp: pywraps/py_idbhooks.hpp \ +$(ST_PYW)/py_idp_idbhooks.hpp: pywraps/py_idp_idbhooks.hpp \ $(I)idp.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_idbhooks.py \ - $(ST_PARSED_HEADERS)/namespaceidb__event.xml | staging_dirs $(SDK_SOURCES) + $(GENERATED_HEADERS) | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/namespaceidb__event.xml -e event_code_t \ -r int -n 0 -m hookgenIDB -q "idb_event::" \ -R $(GENHOOKS)recipe_idbhooks.py - $(ST_PYW)/py_dbg.hpp: pywraps/py_dbg.hpp \ +$(ST_PYW)/py_dbg.hpp: pywraps/py_dbg.hpp \ $(I)dbg.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_dbghooks.py \ - $(ST_PARSED_HEADERS)/dbg_8hpp.xml | staging_dirs $(SDK_SOURCES) + $(GENERATED_HEADERS) | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/dbg_8hpp.xml -e dbg_notification_t \ -r void -n 0 -m hookgenDBG \ -R $(GENHOOKS)recipe_dbghooks.py - $(ST_PYW)/py_kernwin.hpp: pywraps/py_kernwin.hpp \ +$(ST_PYW)/py_kernwin.hpp: pywraps/py_kernwin.hpp \ $(I)kernwin.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_uihooks.py \ - $(ST_PARSED_HEADERS)/kernwin_8hpp.xml | staging_dirs $(SDK_SOURCES) + $(GENERATED_HEADERS) | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/kernwin_8hpp.xml -e ui_notification_t \ -r void -n 0 -m hookgenUI \ -R $(GENHOOKS)recipe_uihooks.py \ -d "ui_dbg_,ui_obsolete" -D "ui:" -s "ui_" -endif # OUT_OF_TREE_BUILD ifneq ($(HAS_HEXRAYS),) WITH_HEXRAYS=-DWITH_HEXRAYS WITH_HEXRAYS_CHKAPI=--with-hexrays endif +CFLAGS= $(CCOPT) $(PLATFORM_CFLAGS) $(MSRUNTIME) -D__EXPR_SRC -I. -I$(ST_SWIG) -I$(ST_SDK) -I$(F) \ + -DVER_MAJOR="1" -DVER_MINOR="7" -DVER_PATCH="0" -D__IDP__ \ + -DUSE_STANDARD_FILE_FUNCTIONS $(IDAPYTHON_CFLAGS) \ + $(SWITCH64) $(ARCH_CFLAGS) $(WITH_HEXRAYS) $(DEF_TYPE_TABLE) -# ------------------------------------------------------------------------- -# Rules for preparing 'staging/swig/*.i' -# -# Note: At the time we define those rules, we cannot go and look in -# staging/pywraps/ just yet, as those will/might not have been deployed at -# that point (i.e., when building from scratch.) -# Thus, we must find the dependencies in pywraps/, and then re-route them to -# staging/pywraps/ -SWIG_SOURCES=$(wildcard swig/*.i) -ST_SWIG_TARGETS=$(SWIG_SOURCES:swig/%.i=$(ST_SWIG)/%.i) -find-pywraps-deps = $(wildcard pywraps/py_$(subst .i,,$(notdir $1)).*) -define make-deploy-swig-file-rule -$1: $(subst staging/,,$1) $(addprefix staging/,$(call find-pywraps-deps,$1)) | $(ST_PYW_TARGETS) - @$(PYTHON) tools/deploy.py --pywraps $(ST_PYW) --template $$(subst staging/,,$$@) --output $$@ --module $$(subst .i,,$$(notdir $$@)) +ST_SWIG_HEADER=$(ST_SWIG)/header.i +$(ST_SWIG)/header.i: tools/deploy/header.i.in tools/genswigheader.py $(ST_SDK_TARGETS) | staging_dirs + $(PYTHON) tools/genswigheader.py -i $< -o $@ -m $(subst $(_SPACE),$(_COMMA),$(MODULES_NAMES)) -s $(ST_SDK) + + +ifdef __NT__ +$(IDAPYTHON_IMPLIB_DEF): $(IDAPYTHON_IMPLIB_DEF_IN) + sed s/%LIBNAME%/$(notdir $(BINARY))/ < $? > $@ +endif + +find-pywraps-deps = $(wildcard pywraps/py_$(subst .i,,$(notdir $1))*.*) + +# Some .i files depend on some other .i files in order to be parseable by SWiG +# (e.g., srarea.i imports area.i). Declare the list of such dependencies here +# so they will be picked by the auto-generated rules. +SWIG_IFACE_bytes=area +SWIG_IFACE_dbg=idd +SWIG_IFACE_frame=area +SWIG_IFACE_funcs=area +SWIG_IFACE_gdl=area +SWIG_IFACE_hexrays=typeinf +SWIG_IFACE_segment=area +SWIG_IFACE_srarea=area +SWIG_IFACE_typeinf=idp + +define make-module-rules + + # Note: apparently make cannot work well when a given recipe generates multiple files + # http://stackoverflow.com/questions/19822435/multiple-targets-from-one-recipe-and-parallel-execution + # Consequently, rules such as this: + # + # $(ST_WRAP)/ida_$1.py: $(ST_WRAP)/$1.cpp + # + # i.e., that do nothing but rely on the generation of another file, + # will not work in // execution. Thus, we will rely exclusively on + # the presence of the generated .cpp file, and not other generated + # files. + + # ../../bin/x86_linux_gcc/python/ida_$1.py (note: dep. on .cpp. See note above.) + $(DEPLOY_PYDIR)/ida_$1.py: $(ST_WRAP)/$1.cpp | $(DEPLOY_PYDIR) tools/inject_pydoc.py + $(PYTHON) tools/inject_pydoc.py \ + -i $(ST_WRAP)/ida_$1.py \ + -w $(ST_SWIG)/$1.i \ + -o $$@ \ + -v > $(ST_WRAP)/ida_$1.pydoc_injection 2>&1 + + # obj/x86_linux_gcc/swig/X.i + $(ST_SWIG)/$1.i: $(addprefix $(F),$(call find-pywraps-deps,$1)) swig/$1.i $(ST_SWIG_HEADER) $(SWIG_IFACE_$1:%=$(ST_SWIG)/%.i) $(ST_SWIG_HEADER) tools/deploy.py + $(PYTHON) tools/deploy.py --pywraps $(ST_PYW) --template $$(subst $(F),,$$@) --output $$@ --module $$(subst .i,,$$(notdir $$@)) --interface-dependencies=$(subst $(_SPACE),$(_COMMA),$(SWIG_IFACE_$1)) + + # obj/x86_linux_gcc/wrappers/X.cpp + $(ST_WRAP)/$1.cpp: $(ST_SWIG)/$1.i + $(SWIG) -modern $(WITH_HEXRAYS) -python -threads -c++ -shadow \ + $(MACDEFINES) -D__GNUC__ $(SWIGFLAGS) $(SWITCH64) -I$(ST_SWIG) \ + -outdir $(ST_WRAP) -o $$@ -I$(ST_SDK) $$< + @$(PYTHON) tools/patch_constants.py --file $(ST_WRAP)/$1.cpp + ifdef __NT__ + @$(PYTHON) tools/patch_directors_cc.py --file $(ST_WRAP)/$1.h + endif + # The copying of the .py will preserve attributes (including timestamps). + # And, since we have patched $1.cpp, it'll be more recent than ida_$1.py, + # and make would keep copying the .py file at each invocation. + # To prevent that, let's make the source .py file more recent than .cpp. + @touch $(ST_WRAP)/ida_$1.py + + # obj/x86_linux_gcc/X.o32 + $(F)$1$(O): $(ST_WRAP)/$1.cpp + ifdef __CODE_CHECKER__ + touch $$@ + else + $(CXX) $(CFLAGS) $(MSRUNTIME) $(NORTTI) -DPLUGIN_SUBMODULE -DSWIG_DIRECTOR_NORTTI -c $(OBJSW)$$@ $(ST_WRAP)/$1.cpp + ifndef __NT__ + ifeq ($(OUT_OF_TREE_BUILD),) + @$(STRIPSYM_TOOL) $$@ $(STRIPSYMS) > /dev/null || ($(RM) $$@; false) + endif + endif + endif + + # obj/x86_linux_gcc/_ida_X.so + $(F)_ida_$1$(MODULE_SFX): $(F)$1$(O) $(BINARY) $(IDAPYTHON_IMPLIB_DEF) + ifdef __NT__ + $(LINKER) $(LINKOPTS) /OPT:ICF /OPT:REF /INCREMENTAL:NO /STUB:../../plugins/stub /OUT:$$@ $$< $(IDALIB) user32.lib $(ADDITIONAL_LIBS) $(IDAPYTHON_IMPLIB_PATH) + else + $(CCL) $(OUTDLL) $(OUTSW)$$@ $$< $(MODULE_LINKIDA) $(PLUGIN_SCRIPT) $(ADDITIONAL_LIBS) $(STDLIBS) + endif + + # ../../bin/x86_linux_gcc/python/lib/lib-dynload/ida_32/_ida_X.so + $(DEPLOY_LIBDIR)/_ida_$1$(MODULE_SFX): $(F)_ida_$1$(MODULE_SFX) | $(DEPLOY_LIBDIR) + @$(CP) $$< $$@ endef -$(foreach tgt,$(ST_SWIG_TARGETS),$(eval $(call make-deploy-swig-file-rule,$(tgt)))) +$(foreach mod,$(MODULES_NAMES),$(eval $(call make-module-rules,$(mod)))) + +$(ST_API_CONTENTS): $(ALL_ST_WRAP_CPP) + $(PYTHON) tools/chkapi.py $(WITH_HEXRAYS_CHKAPI) -i $(subst $(_SPACE),$(_COMMA),$(ALL_ST_WRAP_CPP)) -p $(subst $(_SPACE),$(_COMMA),$(ALL_ST_WRAP_PY)) -r $(ST_API_CONTENTS) + @(diff -w $(API_CONTENTS) $(ST_API_CONTENTS)) > /dev/null || \ + (echo "API CONTENTS CHANGED! update api_contents.txt or fix the API" && \ + echo "(New API: $(ST_API_CONTENTS)) ***" && \ + (diff -U 1 -w $(API_CONTENTS) $(ST_API_CONTENTS) && false)) + +# Check that doc injection is stable +PYDOC_INJECTIONS_RESULTS=$(MODULES_NAMES:%=$(ST_WRAP)/ida_%.pydoc_injection) +$(ST_PYDOC_INJECTIONS): $(PYTHON_MODULES) + cat $(PYDOC_INJECTIONS_RESULTS) > $@ + @(diff -w $(PYDOC_INJECTIONS) $(ST_PYDOC_INJECTIONS)) > /dev/null || \ + (echo "PYDOC INJECTION CHANGED! update $(PYDOC_INJECTIONS) or fix .. what needs fixing" && \ + echo "(New API: $(ST_PYDOC_INJECTIONS)) ***" && \ + (diff -U 1 -w $(PYDOC_INJECTIONS) $(ST_PYDOC_INJECTIONS) && false)) # Require a strict SWiG version (other versions might generate different code.) SWIG_VERSION_ACTUAL=$(shell $(SWIG) -version | awk "/SWIG Version [0-9.]+/ { if (match(\$$0, /([0-9.]+)/)) { print substr(\$$0, RSTART, RLENGTH); } }") -# idaapi.py is created together with idaapi_include.cpp -$(F)idaapi.py: | $(F)idaapi_include.cpp -# idaapi_include.h is created together with idaapi_include.cpp -patched_idaapi_include_h: patch_directors_cc.py | $(F)idaapi_include.h -ifdef __NT__ - @$(PYTHON) patch_directors_cc.py -f $(F)idaapi_include.h -endif +# ST_WRAP_FILES=$(MODULES_NAMES:%=$(ST_WRAP)/%.cpp) $(MODULES_NAMES:%=$(ST_WRAP)/%.h) $(MODULES_NAMES:%=$(ST_WRAP)/ida_%.py) +# .PRECIOUS: $(ST_WRAP_FILES) $(MODULES_OBJECTS) +.PRECIOUS: $(ST_API_CONTENTS) $(ST_PYDOC_INJECTIONS) -$(F)idaapi_include.h: | $(F)idaapi_include.cpp -$(F)idaapi_include.cpp: $(PRECOMPILED_COPY) \ - $(ST_SDK_TARGETS) \ - $(ST_PYW_TARGETS) \ - $(ST_SWIG_TARGETS) \ - pywraps.hpp | objdir - @ ! (grep __EA64__ swig/* | grep -v typeconv.i) || \ - (echo "ERROR: __EA64__ macro is foribidden in swig subdirectory (to ensure the same api for both ida versions)" && false) -ifneq ($(SWIG_VERSION_ACTUAL),$(SWIG_VERSION)) - $(error Expected SWIG version "$(SWIG_VERSION)", but got "$(SWIG_VERSION_ACTUAL)" (from $(SWIG))) -endif - $(SWIG) -modern $(WITH_HEXRAYS) -python -threads -c++ -shadow \ - $(MACDEFINES) -D__GNUC__ $(SWIGFLAGS) $(SWITCH64) -I$(ST_SWIG) \ - -outdir $(F) -o $@ -I$(ST_SDK) $(ST_SWIG)/idaapi.i || \ - { $(RM) $(F)idaapi_include.*; exit 1 ;} - $(PYTHON) chkapi.py $(WITH_HEXRAYS_CHKAPI) -f $@ -r $(API_CONTENTS).new - @(diff $(API_CONTENTS) $(API_CONTENTS).new) > /dev/null || \ - (rm -f $@ $(F)idaapi_include.h && \ - echo "API CONTENTS CHANGED! update api_contents.txt or fix the API" && \ - echo "(New API: $(API_CONTENTS).new) ***" && \ - (diff -U 1 -w $(API_CONTENTS) $(API_CONTENTS).new && false)) - - -# Python version -CFLAGS= $(CCOPT) $(OSFLAGS) -D__EXPR_SRC -I. -I$(ST_SWIG) -I$(ST_SDK) -I$(F) \ - -DVER_MAJOR="1" -DVER_MINOR="7" -DVER_PATCH="0" -D__IDP__ \ - -DUSE_STANDARD_FILE_FUNCTIONS $(IDAPYTHON_CFLAGS) \ - $(SWITCH64) $(ARCH_CFLAGS) $(WITH_HEXRAYS) +DOCS_MODULES=$(MODULES_NAMES:%=ida_%) +tools/docs/hrdoc.cfg: tools/docs/hrdoc.cfg.in + sed s/%IDA_MODULES%/"$(DOCS_MODULES)"/ < $? > $@ # the html files are produced in docs\hr-html directory -docs: hrdoc.py hrdoc.cfg hrdoc.css - idaq -Shrdoc.py -t +docs: tools/docs/hrdoc.py tools/docs/hrdoc.cfg tools/docs/hrdoc.css +ifndef __NT__ + TVHEADLESS=1 $(R)idal -Stools/docs/hrdoc.py -t > /dev/null +else + $(R)idaq -Stools/docs/hrdoc.py -t +endif # Test that all functions that are present in ftable.cpp # are present in idc.py (and therefore made available by @@ -367,52 +515,37 @@ ifneq ($(wildcard ../../tests),) (echo "ERROR: The IDAPython IDC interface is incomplete. IDA log:" && cat $(F)idctest.log && false) endif -dist_dirs: - -@if [ ! -d "$(DIST)/plugins" ] ; then mkdir -p 2>/dev/null $(DIST)/plugins ; fi - -DIST_OTHER_SOURCES=python.cfg README.md AUTHORS.txt CHANGES.txt COPYING.txt STATUS.txt -DIST_OTHER_TARGETS=$(DIST_OTHER_SOURCES:%=$(DIST)/%) -$(DIST)/%: % | dist_dirs - @$(CP) $^ $@ && chmod +rw $@ - -DIST_BINARY=$(DIST)/plugins/$(PROC)$(PLUGIN) -$(DIST_BINARY): $(BINARY) | dist_dirs - $(CP) $? $@ -ifdef __LINUX__ - strip $@ -else - ifdef __MAC__ - strip -x $@ - endif +package: +ifeq ($(OUT_OF_TREE_BUILD),) + -@if [ ! -d "$(DIST)" ] ; then mkdir -p 2>/dev/null $(DIST) ; fi + $(PYTHON) $(PKGBIN_SCRIPT) \ + --input-binary-tree $(R) \ + --output-dir $(DIST) \ + --confirmed \ + --component plugins/idapython + (cd $(DIST) && $(PYTHON) $(DBLZIP_SCRIPT) --once --output ../../../obj/$(PACKAGE_NAME)) endif -dist: $(BINARY) pyfiles $(DIST_OTHER_TARGETS) $(DIST_BINARY) dist_cfg | dist_dirs - # rsync -a docs $(DIST)/ - rsync -a examples $(DIST)/ - rsync -a python $(DIST)/ - -package: - (cd $(DIST) && zip -r ../../$(PACKAGE_NAME) *) +public_tree: all +ifeq ($(OUT_OF_TREE_BUILD),) + -@if [ ! -d "$(PUBTREE_DIR)/out_of_tree" ] ; then mkdir -p 2>/dev/null $(PUBTREE_DIR)/out_of_tree ; fi + rsync -a --exclude=obj/ \ + --exclude=precompiled/ \ + --exclude=repl.py \ + --exclude=test_idc.py \ + --exclude=RELEASE.md \ + . $(PUBTREE_DIR) + (cd $(F) && zip -r ../../$(PUBTREE_DIR)/out_of_tree/parsed_notifications.zip parsed_notifications) +endif +echo_modules: + @echo $(MODULES_NAMES) # MAKEDEP dependency list ------------------ -$(F)idaapi$(O) : $(F)idaapi_include.cpp $(I)allins.hpp $(I)area.hpp \ - $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp $(I)dbg.hpp \ - $(I)diskio.hpp $(I)entry.hpp $(I)enum.hpp $(I)err.h \ - $(I)expr.hpp $(I)fixup.hpp $(I)fpro.h $(I)frame.hpp \ - $(I)funcs.hpp $(I)gdl.hpp $(I)graph.hpp $(I)ida.hpp \ - $(I)idd.hpp $(I)idp.hpp $(I)ieee.h $(I)ints.hpp \ - $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \ - $(I)loader.hpp $(I)moves.hpp $(I)nalt.hpp $(I)name.hpp \ - $(I)netnode.hpp $(I)offset.hpp $(I)pro.h $(I)queue.hpp \ - $(I)search.hpp $(I)segment.hpp $(I)sistack.hpp \ - $(I)srarea.hpp $(I)strlist.hpp $(I)struct.hpp \ - $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \ - idaapi.cpp pywraps.hpp | patched_idaapi_include_h $(ST_SDK_TARGETS) $(F)python$(O) : $(I)area.hpp $(I)bitrange.hpp $(I)bytes.hpp \ $(I)diskio.hpp $(I)expr.hpp $(I)fpro.h $(I)funcs.hpp \ $(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \ $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \ $(I)netnode.hpp $(I)pro.h $(I)segment.hpp $(I)ua.hpp \ - $(I)xref.hpp python.cpp pywraps.hpp | $(ST_SDK_TARGETS) + $(I)xref.hpp python.cpp pywraps.hpp pywraps.cpp | $(ST_SDK_TARGETS) endif diff --git a/out_of_tree/parsed_notifications.zip b/out_of_tree/parsed_notifications.zip new file mode 100644 index 0000000000000000000000000000000000000000..05a4b6fd6c2587d1849060b17ab7cc394efe1df9 GIT binary patch literal 574022 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

CQH>GI1J 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≶()#3kfxLuyx=m``jBmAK?`_|vto{2tCaiV9ZA!o4$6p`n*H2u^mM)H3S_M< zWf~k-QgnuWCVI^veBOXw%}j4>bDa$y)pY>n;zW20xHr*G`GsVbY197p{(*HN*L!nK z&PYs@y%@ZT3KCfYA_vCeB#fzwOe=SXcje>LVfNM5*_Z`U)HRPhqjJ{WA_1w85J{qT zYa$JgKW`|8aiAs!AhA+{Ia?3ACJdW%4+#TU>6(A@bJ?Q8p;Ez$>e|G{LJU9`DS>po zwC-D;ijEu~fmTl2Zh-=6=B~wr3?GMU^L{zzJq=2|JDDngoKNNMaZ}gLSTs4I=`LGf zj0&d?Vlnj~HUVRrS|$qLhH^vqEX)rL?3oJkY(C3-!#a;#|^p+8XUe z4v=1eQJV~}X+@R%bZLL?okRFOPGv(^kUVS(U|_=mY=X_kWuYhJl)yH>)2Cn911)p^ zY|~1YG}zq+pd~A3bZO0kD>SWH+CS!+J9NJ1PDfkXe`Xi0RUN3si+J)W6H;y7QTsO5 zX$JqmR(FpN{ranCsl)BSiv$ZCsD-E@0627~M1w3%mC29n#&+0vao-s%cSoUH0h3i# z$7K_mqHMkOGW9%O?Vvo!)|(y8#Q=U0ERdneh0+IlAl?+mMd9WT&qhMWn~kCc%}04&OqMAR3{ygK16q&d+ObfY^Hwob!WiqQwM@} z>J?O%(bFU>VbwI&|7Tc(Yg^z zSR_ZDRj`<%2Jh55t2kdxn?yA$+(~K2eNm4&%{-=tn^Pj*52L2S(DnZQ`qFAVRLKCL z3NX0WkxVGfbkSP1jG3N><;jy?q-4yx|E2K%FjF%Ad^6wzA>)@{#_g|^LNXEyVo`LV z8^hL_;y^Ka3iI^-rsg1D*=$+2?poBq6P_}+xew!NZDz1j#9Y4dP1yM>qvEU{V90Fw zCEl{J!!XT)IlF*=r(y6ve)`K_|NOU~4uAROzyABrfBVm0fBE^>>)#{0zI1rf!~Nqo z-{|va`O%AC?Q7r92?uOwK6dsibRjo(xyv^0}#S`n6?x!#u1;PIO)rq${I zsYMxU8>oeH)dGI?DHvETwVm%zfBMsR7zCi!aHTsdN^@%3NYOdK0gHgt<2MKKZ?6sF zvbBH1QB2nQ&5Ij*D^In2j8i#=fCM8-M_}gAD*$uQ${>D^ZuB%}-`@ZBhB^F)wFE5A zSP4_D`>c8ZXa)I;K7kfY=_g6p73SFBW(11WsOFRtYB~>!XLsGxig@&I_ou59EPUkQ z)NhY>{q-00`1!BD`HsGP(?Va_7q-d3(4#_r1BG+8t3vI&UZ-*ISjO!evg_|`f@7#i zNoMX#WuK!g<%Z4I&_jyTP1y&2eDvYecbh9exp@0vtP@AvL;eWv=n?x7xb$>E zUF+WMwmd#Q9^XIQJ}t1r*Ur6s;0yBq#|OXd+S9M*;iN_CZ#~4WBOCe!!vqIDU`_3D zen5E9{^^?GVV%vU`3y2{Ods3^qK3U8GF7e2kjM1NuO5lDcHlp@+QD4-)_}ova+%6R zxyY0VegBb&Z|q2vU{wwFv<6IfLAM4@*7_I8)vrhLBmndLO&^b^Gi$p)f9SQ{qKka- z{=7TqdVZt)-o&1y?MY%J*X zrB%dhFLn7A{`H%u_KW=?-ql+3(A5l3YGn}61KYA%1v)gf&%@~RW!?EFmZfm#F;ei} z=wL&%I=c*3>077J>x$*~_wwPg{p5*l8`Wrt9bV}Xn$CcsMrle{NA7S;_a9kZb7Aw( z+50XY^V=L=ZDP|GM^RP;ldP)k)~;FZpB~;GK3-ZzzQoM>&bDv>V^~aM#8ePAoh$QQ z^hefcZ<*|BLrEen2y%48QjQf|*hO2jvSWH0wz$~ex7k*~sM>|k4YJ5)bFo&?eK35< zX?}RJXfEAsvkfT7Ucz-myx%Iw9C+X00GI}#g)%=vfbG-mHrsUs!!ir7Rd74_81;u5 zH~=_icp+Z@>YSgq*=|9H5dle50{`KzxNjBf98f$uH{aZUab9$3@1@zynj*>-fP$)Z z?s#-c1e2OH!-hJw7rQosjX5P2-sF<#Oy$lXuTF(fY6*VuOginCQ>-3ZQlhXh#6qY#x!;x};NJaov#Y zL5^f?zBlHS!X3D5HMhjOGv3Bffx~NN^IJ7{b6CE;Z`;C5jxB-W0}LmH_nyV!iB&4P zTaCG2%cPq_TwHLl<;i%DRY#-}%La3?(f#HnEt{CYi)mNU;{}89Ra0fy0U|)jzCIlO ziBrl)^VCE;UwD&Z_Vi#t71Qh+Zx>MC8wfHE1o~#g>M0zjc{<$TyfL;lP!I>;KfI+7 zutAKsm72EIwCL4zsa;C}Uj!K6k^({;+9ymsp4)Z&Spm#i8S~3R2 zSPr}nh`fqOuBe%Ttl~fnLtPE*{LPkmM@rX#QpO8ApJk(~)5)gn5$)?7$t`|&>89q) z=`zp@=9mi5S|O2Jo}RR4n4OA|!-zS|3GnBK-#h>iB;CB}Nafb3XiRs-gunQI4 zud^1+l{If*jNVUu?zvRFy*u6thQM!JPJK5qsbqmFYfiBK|GZt3Ghy3k<`Oq=K)8rrD&q)~JRCaL@GJrf%ckx0ibD6>__5 zn?!>fm^6*MT|apH+~dKwEw9$ByUZpPf{t0O){WLCfvI5~UG7YrSJc)0w|M)?7Qhu3 zN{8fFGNT7JjY`&;Sb^A0t6;Q1^nml-zIXr?3(b)?nsu0xmrzr8!%M3p()8oxVGv^b z+Bt=$K^P0{ViZJVAX-g>52As`X?8+CySmW+|HqI4(9OW72PjhO>@_7n88Y59hzA-u z{eUyOcTbjNU_$_@oU|mv_1GG?tE1=&^uFy3Mry;GPZt1`2UyqbLKHyT=sk5?W30n@ zk!AsII&7U-+7+aeb3LO20tS-<2^=i0UohG>35b@G&izQSqzsHV^*36efq(e={2M_A zc0E;i76!31zKx`Vz~b=TI^LP*d_Z~nE)l#BWLzjYS4&QNXTbkxr(^xBn&+H$a|BPI zmfOW>J#6yF?vR2XM8tPv5&vXSaF^`(=oL;STr(*qaB4rp7hK zH${36MbIHmhlX+{8nNX4RHwa@!Z5~BwiWl#<9Y8 zdXAm6p*sH3l8$c6^N8{|(@|)5ALyPf2w81xqj@D@;ITxw>;xhXD}nnl<|lG=L86C? zhzP4_|FRX);wpO{{Y4a&PdZ6TP`j>{`*>O4~)D|uw4o+Wu3cJ86sF~ zGiM5EKRp|DIq%~lXUCqgBAS64kD_wP@<7wrBO*z zz2kXV?bdC7!YTi~ZtKgBk80@kV|%OVgV!p^)=r!9dnt<~FL-!-@vCp7daEtgDH!&0VKL`;b8TP17=4 znL_97x9rfc;`(1_=bOyg?a{>BpFbK!{N_!1k6_zdD@@>O0&+TZl&^jVZv@shZ{|_< zj~d-y(A5Uu51Zg*M6a{xgVw-DfwOCM-bUk_x|>6nOxhw4>>IEks9V#%h9k0ohP9H$ z)DCZoVfG*Dy=Cb(G{zpHaXO7%*@Ow=Ywg;e2s$)d`jA=TTb7N`jykm0RWDVDG#68w za3)6}5%b<|OGIv&P4vWtVF%(U*#na`m?xsvEU_nWPS@B?i?P4EzK1SsG~{MuEhhRU zORCi3ixv1k4ZBRgu?XuyFfq>KuL2O zd!Sa{t_NhPVY=kC^&od0v23agV{P;Po9}c_sC{ z{$cg+76k>P7R5C7fbL#Xm9jz}i%Uy!9>jh9=EtAkzFGb8^Sk%Izxwah>-WFDd;Ydg zt=!$d?qTU3yim=Zwhyzh!-iK*K?0OASpg`C*xoROR@;_6XpgUMQe!kcJD`@7HJTB# z=`kknHNoD@la2rHF#+~sD_UE!T7CMgopx68RnFH_#3UVYWOAz~2}Sc+kOAG+cq9Fdi;D+1jVT=m2IqTiWGZ zI(|>6k!aU^I3c5b-NY40Gp3ODfZER4(m;T5xZpUI*a@f`Tg^FBXkh9fa#QeoK+fZC z2|_ou5&W$Hm4ZDvPlU}dF^M#Qx0wVQ(LKVk7fC?jNwCpD`>OTUB5~EC_*UVirg-?v zdVP#vKbI8QnwW^~QPC$0W49bSJF{AE71Vrk@i zQP%Cx{W1!kC|@f9gk1|}*^3*-HTbOoOiiha#Q6||_UY!+<|)OK6t%yNl9t9%KX>L( z@JCO}Q26=wtiyxNijxR*ACxoz11G2+Qe%w)B%=T zQvIeXWS5kuSDA|c_Z*!L>4>gpPgF1aO|o@nIM4$V z{n8D`=Tzo{JU0T$-xmcy{ge{2&K%Oj907S7v4rdss2hl*Z5+&8Rjx1Li&&j8v=^6v z+p%^_^1xs&3ni{0xl-q|nKnXhWOxCEY1NZ8zzmYS@mh>!Lm}n_sO^Di_mlGl^d{hk z0rs$apom(d&Jfd7_gWge%KOQAdiAlU+PY~0SRy>(sG++C3z=OBWFAwWn{z)sJs7x{ zAmh;q7lLopxtTpCcDd%B=Z13q=a=)pu7|Ll{C1|nRKN#v?!629K^De58yY%&!p>3n zBHy~4eO#4`3puet8m;T(kJP+%-7r-@w9Y92>*m^a+|4^FFXhwcucrkVArVnu8t{lU z;0Tps>Vb5I8_|4v;UeGXf1SU`wTuiZ4OnKus*|OhW$0u%UPhihh(~u$pvj>(5t|{&!>bh z(w*Lv<4{Et@FN9qn}J8I*JfC3m#<|zT%*c&wJw)0amLYm%YDFj_h@|uWD}6=97_kp z2=a)OOU3NTWn_x38OUXrrFR_>F*LRtLutdwdR^OQkQ9l|)c-~yWH zz_*E;W#`){Jq0tT3%By^H=5(AWTCVyoP12fX!)0F%y4PI_GnwTxgoq2Z;9ufP(p2y zHK+zoIgijZkym4lI;|jtb~n*vw}g9saC@pd(dm3zLFu?ShiBmW3TT3U>@I1CJ6UYC z$|!r5=0Eio_E<$g_1Oh%Utqy`C!G>d4FlUgzKPc%vs2<5r&*u2*b1_hb+Ag0v1u5^ zQ8jsP3l~oC0-IPfnI!-)f`O2wyU~)bu0$OlM)TOo{Y^Zry>U9-s{t|OoUx&bZDG3` zQ(p$%&F$g6tGJ0roivKlqAVj^#KHl>qX6T;U(0A*49~UtR?LNNopK|6!!@+QR3Z2k zZ%wgE)Pl{OmGG>>gYBx-ssodtwe-q+)2@wO}44@)NU=FTU z22&Lmp({n3J@y@+*ltwjHso_k>zElx%#7TdjA{la&_6J&D5HZ}&yV{4WxVG8rvy7a z!-U)5Oe`@sh-f>Ig{wyAhxWPF+`p5j7gRL0D{Gq^j@ZW`P6!tjmQzOAg=-(2`{{SI zP;D-Pp91@2Vmq8S`Uwf}QGNK19R>PUL2p9lw?f{~uam+In96Lj!bdGd z=Qcuzc0+Lph+_}lu-$8M1_wXgYheU@HHWa%CZ^gj~iJ_iAZbM0S$Cty~EqsxrnWU&3 z+%OHYHUmvI7S>6(ujS?8MI^# zB=R+Oud^RZ)BfX{s6014<>N;PdNmE_?w!5*39p`Kul}Mx-8*}=*AJ`w=f%Zs-u~G) zEx783|G)ZWEtwHXK&piL;b`$hzx(%rmM z$aQ-oyk?&>+#x`BprAy{G^Sjm_6Z3Y_BZ$J1J`MfsiO%HfSIJHP)J+YW&!QM(w0&g zC7F5u*nW0(Bnh-#?#K@7NoX*dfN{fVdQ|}S@OSf(PA9=&keV1>#n#)P4e70ZW8fTu z<5;J?YiNXS*-7T0L)Hy_*#ALA@kAz%=woSgtZ}_T4EEyMU56xfa7yw;MJVw zz36TPIpXcmIbB(x3PxJ$08w6;Ml?xd$aQ)mZ+1ai=!7S;pHg-j9lC!{d%-tpk_98;tcv?D|aro`Z+!WK5s z@7Xo>2Tu{PKjoFTvi_>iW4kja{JHcB@D!BTdu{qD^#zy9&8 z)ycsJQg20jbu2ftdIN4hU{m0tdDrSc{O7B;KOMGuUtMAKvPiJ3?_nvT_Mp~oJ#}P5 z3cz#WUV9mLqa5$A|N6K0uin2|{r3BB|9sZ!v3n~H-cGw&;P9KbL8}T_b9kfQz53f< z-@p3pzn-;zA+24e3V?45VK1N(aqxh8Hc*bq?q9F}?)SHcEnZh2R%fg8o1-7yQEAP} zfMsYshQw|R18?FQG|){J|Hn^1zx`wN3vS-K|9RH>OXdN57Ve-~J_+QeF|Wixh&{TA z(%r59!;e2bYkg~*ZMb`7RMOE!(nqtgnl_?K_RctOUH{#ipMHDR>i^H%l{Uq7EX%*t z`|@#s!JZHT0+4k5X`)wK@odR~mUrL3f0@HJ*z|!jgA|S+Y-2g=^iq}C-BnfLT@brH zRE%gXHP)IdLga>J7or$V{SEr!kP)NM zlG1JH@74zwv-bKURFG(0fZM>4e1(1%sT&JA&{OjMA&vOos8<(@!`;6YpVSazWsRNX zC_HbFKwp&)(qWDDG48wbBW$sqft`0O}(!WS5Y3 zi`;qhWZ3LddF6|D*kknbEDqOw9|yGc$=Qd)(>;Kz@yrtsQrm1ZCS2E6*s?~~7}~&{ z+|TjZENA|BuoZ%MHwr#e$r=Q-TgU=iT)?keHSx(CZ5<^xu{R37JJ_t) zLRQzLSt9f~@Z)!U*)0p`f(br@}M7mnsI4CnnqqFBGdF z7TtsMp@F@QcG?>{@x>9W*csV+d@HD879#j4bu`U%rCf9o-aU<`oX97u%d35$lPs!s z7FvT`5KhNX14ueZ3)o70$k%)VdWRUY1?)-+tq6zp4L26D%#mq2Hy1|`E)&3u!>iT% z-H|0|i89<`AZbEscCBU0yW$&mnx8^GUtC7pBym+%bU(O9;+uv=W zy%amF*`5kQr!ZB*&%m>QIpwJZe!lo+2clAdexpWuXs)KFn@zP==?hkxgAzgDmQfEV zDR$K1n;jY9Jv3r9#5em`WJEFGmeyQv*xt3-;>MAC1HA{Z58y=A7YCA(9fOy*wPS15 z=Cd_&3i!8OV#hr)AB<6THtyk6KnJCbo~1@XVd6tixu=k^tLGtqU-w(Zr%M!th4GvU z9*h(+n~mIB@QDvz2B`0k*NcOhUwe-UhlydW>_Q=!Ld+L^@GB6$9_lBILKQGCmNWgw zJ!W(*mI}ODT}sZvSAq?RO?YZ!S^~_=-)3^|drVeu?yg}twa+RAC_9gjhO87qSu-)V zmlrb$*gd9guuhI*66bTCfZf8(&J|;AXNB+%37G^UV{M8j z;_zD|Kf&+hsmYG0sX|gj!=A>qG+vG)CTXv$iLrSzIv{+1e7Rnn&I|?jpzJziq-B6t zR>u~lL=ff)3BrL-Q=qHU^NX`xr`0;dyWlbl5nC@6wu@=lMZ7*!MUC7NaT$D8r?bvw zwt%aKbg(81EMyUr0*!^FYCZ8^Xf^Um1U_0_&en}z?uiCsYtI=X&393FaZtML;2IFn z#mGU#y}VkVy%X9k8jmX?(@H-=sodU_7~WOXoGLES@l zk+6bojM!2gqa0JvA$Vbx7uv?b`U3RgWW#Wvn17jh{(FyItroWG0MUv?C7p1Y=kF1- z-<>l9q5-01pPkPN2=}-#DyGEeW_)1daxdUPBs^qxtOYeV5drSe>iBqhu{@ni zEANq2vI)`7rxLxkCEF^XCQL3uY_{2WqR+2ZXQ!95727>>2&|RK9`T8t6%tl@^tR*M zBcIdhXOWMV$BTC-bE5v!_sk(8EMO}`EbElCG>8)ki_S=BS36>tD;~{YgDu`J*Q+__ z{iEw##ip^5rTRfaBHtu9p_(?`ORv=+Y8~HLak1QE7;bL9Iyk(uDPq;7P=rxk^k@ZE zpl_*-rsLoSe|36!HEZSnFP!VA``Z>cx&}5MI#zxatheEyVTCTdZ;At1^Wpe(IX8?w zf;Gx#L6X~eYaOxpglG9gt+Y{FDN|3t&b{J!gbS(KoldqY2_9H6#VN?mYigcEra?Tp z!CjqTukVc9LbVEG?F5U4OCHhigaCeC81}Q7Kt4R%1GfYtT(Ox27o9CkMWmP%Az^Wc zFj(6g+|#oUi`DMPHew4E5ejA-1FbX!g)=5oRHacqP9?s7RRfYtTq2gMo?vayKx zDh%5Sn+j@*#0l{fdu!y-kKo!Evfr-XUe3M9*#d7GbXzF_vII#Xw5xmv5222*3i&=* zU^n1bi;JuE%oCwU;1D*pM$h}>!~$|WoCz|942~>B99^Oi{OVT*@cq8M!eKz?s4iV% z!m!0pFvOA|JDAV@Jp4W%50)l);v3m^To+rs0*8{B7kSC_scGji_y(JNZ7=L3yh*0Cz^3#X;x-g3yH}Wa7J?bw+}&aCGvu}uQzV>; z#LPq8V&@@92%Nkmmg*%=1mPH9Qg1HIoV(=XJJ|FI& zEwR9(R~2jA%Gj%m?7P}K{CXN3A#AX*&QEr_D}S`y*yh4iht=-0F_u2Z7q+ajpifg% zez80{qo+BX`AlJZqm?MIx-6R*Z0-!D#V9ZjvS9-J)T|?bUi`M#$U}9vE)g^%vsgo_ zO6wSN@*YgO)v2Af{LkUh$;<}=TgXhLO~UBXYVkE!5GMH566;1A_raOq4e0uG32nCf zQa3thhrMk1Ood6Bg;GqzUP}Qo)d}CGBFf1b;>i(r`tHN7ok~(0 z-l2BO+ZKGdt+ir0;BE#NYPSo0zhGut3SeqBRQ8yzEFW5QfF4#M<)I?D38l-o=j&fT z%tL?le*Cs=E|1nS!Y7Mi0TheggVGt^C1RIXtonhYxw)|CDdN$=oc%U1qO;)-e7$c< zX90DV9d?5QtqQ#qvDSTqaMZnRDCb5WF{MMAU@esmNB=p7zSY~h+L z$(`6i55o-^+`N_nj!zcZqaHXM;?BR|+x}ePA^TglEQp*n*h?vywVU9ATpIBS!rspC z4ksdiugm(6+pji`hquKV{3Y4%GG~Mq+l3X?n9-@o`Qfj#@0%Xs3YOo1V-Y{NqOHZYW$nKoU)`_{<}ECUut8AlV1Zq-=894Rn>vSyx?Wp`rLSL8`Qz1RGg_Uo ztl-EI$1!{|M6WmLy&|eKE?2s3T5UhVG;7(Nh;Q#4L|aIb3KElwKj@&ICMr7tva8p>pwrI7bV)yF|6jOtVFb`o|*p^CQ^S}uocK9Kdy?iZP>R@Kf*K@Ti3N~ z2$)cz4?!Y%fv6GX5P#aV&mi}_{_Dk^$>!JvuMg^KU`no1MPU(YJ#YIep9xM>GABs; zZ)t!32&-a&xX{>?k;RQWl%)^6-EB=Vmv&r}hag|RePJ>1JFoHDwh5t`^UacznYodT z$z|F&h@mLrM70-9%kcdWn}NCe5)3iqQAu za$-2@Op`G@3AN0N6w*(R(0xMJ_4UizGS71LDKN`Ucw<3!RC1;Y-grt_I3DZEQTfHHRXTPEtCK|HD9Hft>lRDKz;l4byJ7`)pud@ zMH`!23fPG``j%@0Yt~>AN$Y3)JG{c2+B0+P(xzj<>*`&Bo@H5XQ|U2Mg|D}9Toz{& z64T4-yH3^cutkBr2RO-&O;-daF+yGkU4lH_|yM$n2shnHMag=FEgd z;h)+dU-3uxiV}C|&%-CDgJ=!H$O;!Qi2-_!$>|~UEzN}JrIDXLf7+uU!Ang9*N~yg z#2S$rE>2}%BIdaXW#IKDn*RWSwNZY;C{`C-U{7e(u`VqB zeVCkLeY9}93-=GS5nA41yBI7&&ywznwIrb>x@$FCH)d6vQB42c*Cj{WSjos`C5rAT zvxQC;y!2-E2O*wv$zRd{SU{)0_l8~Y`+8&0msjx2&sjmmRdQ$|!mGeH8)i7GfNd*E zYs5VH5)O!N@gSjrdPKi)>q zsPFjd-$GgPKk$4wXr;*t)Nm{|Ha5<@dEB<~kB`6qaq}oPXzi7<9aaS!oK{_yS}m)P zr8K-Ao7=E{Y{K(4FqcUs*6jsTLconob%i@Fs%jQ28oQ69<>CCoEW6p*@rzobiIo** zV`Ld*CB4h3=osR(jmwLRvpMesb~6zg18g)aQ>2zPw7^2cT^W;9(_1+QPnvkOdb>P( z_X_VyYLzyMW?Xi@$c|YIN@yW+hz9YYKk4VL%ccnUn!nn4!95u9ycMZ2*;j|m_SQM( z*lTCvt&;^oqWbYs4%F-FYM-q+%S>KDHq!(|wwAf@0^e5Q66`IaO?1n+G4t)&(YuqM zVkoN#vHrG#Pqq+4*vmahUV>!X{lu}#jiEEsnszpmeNtF{F}4KyWF4=YZR>`;|9qaP zsefbU<<;B$x5x|3G!o+67A=J+#wcI25O6iE`RJi9$9K;+W-kA8zN@>LWv0@;CB-(w z%1a?lv^Lf(YjZV9(Z=`Cccb~{{onjkj3>ON>yDBprnjXatq&{7U#F?VC=#qyV* zWGGl^bGlg%9+nEM#D?z{3!sT2%S5B@TSG5Sev+YV03dQ}iN)8GSDpowo$VI1TZ%N+ zo_AA!^^*)O>^)O-R+~=44vG}K%v$OA9xeLk*67LE;qHdsomcwo;dlb(F)ObF#wx2` zgv?DE<;;Cdwef#MCdn@}MS3>0owrOA$N_=uHh$@{D7sRW^Vzx-hd20p&^J@T4I$y6{ByvBjpG3_A_^&dvy z|JA6LSffGMiVzK?Ll&r&)Qds@l1gFQ`+*4%p_?WDDmGy0W7jIj6kMkul_dC3 zVzMYSRDLMP_RqO!e^HCR(p+TL|Cg-n63phJ0n%Ks9e4-`a&clHUQ-b#3Fd5NicI-W zN_dQia0PNru?D(%W@g)7KP1fl!;bZ~A^35fzP{|m!7~rUYiIQb z@8IGQ4+H#t32yIoSyyee#`)q~~1oH;piTTH-lr4bm zM9mD2B>Pr(>3>x+{`TqaHO`A3Q9TLXlQZ7Bpt8Y?N1fq8E#IhS%lZz(J`umSuY5%E z|GZt>Ze+J|{iWiad^||C+aAK#9{ z9e%vr`l8jp>INlJBrlhiM~}!Bv!~K!9?asT0C0D%bJ`DOu)ByI zlr0&Geothdz7E!x`yOOzFCIH}&79E=4PoRGNI$-ki#^$H4;hAZB%*dYY9wj+S6;mz7K_z%mG6K^YW zC>qX^sh-lR$XIPEk%|J*HsrH>)Lo7V=@0kMr~8+25DYL1mQUJglbSvfaM_gmv=!&& z-PqgJq8tndUpq#o#;Rfa^+ejruDnv3Gv1l?6%9|9<{`uia#^yxmQpd70A34L-k6 zQ>RZv!>hsff`Zg>uS4@V2&wtmLJZwhDjY=>P!NY$W0y1K0OS5BpPJGb#;7Wb_H5S> zYv;h$VJCGlRuIlTK?i|gI|h=(*|$=`BAPg>94XV#?8EqQ9?PL%nnj=qMik_<_};bB zp-BkVBKEE)K%HBWj&d5A5B|oyR*IszqFEg|mZd=U6Psl#=m=ceicHhBm`Jf6f7m;ng9e6klRPWaH@F9=!zW zn&)?!{GTmGe|dc5fnGOX8v1yQ4n+}(n$J0w_~=2lZ&)1mQY^y6TpDb`E`{UT+k zhierRTZGjh@SC+?f8#^P!f69;tc2SAL}HggmSgpr+n|t}T~7qN(6Xa&lo8-g5&ZGx zO40$&?7$qGhkMDoj5Gg86OY0ZK|^vbGLnD=ls+o&)TM`{!nWQuo~M_u<1iS#1?%9* zf(JJoqq%@o(;4G@%huWt#}6I>=j(&Md>Ln_GFT>8K-;?kH5w|MVH`LS*v{mxX20|F z2tel#+mFPD?AVVBE|6f-#+Wwzlr|`~V9iw|_v5#nK<1Z+UpLk|5^EVqhNyAiM@>q3 zQx`#8!I#!4=MQvcUHN_&IUK@HpHx-=fnz^i^gH2tvzHh!1JN6I0A&LPQ`dI*XF zcF8_f0V!mtldCs%ukIVZYOkkqqm%^F0iWsBieww%x%z0nS#NXwn06e2=8@CjDY(~* zpCelmWlDdUU?vB~j!|lSSET!=H_PW&$(;zIsLQ$Lh9t^R5o1$LaIjrI8()tf#~^4m zS>$Xza$dL*$Ls8rT$*qhrmqJ;KOKgEh3+^J*&7AzQ!Iy_7Wplxpiou+ubqiwkicJK zKRmeS^sLE}NpaUmO^bK)j(*-+G_~;7S{zaq;cOD6_I?_n8k6E!W(V7I3m@yB$ETaa z$J;WnB^GDER_z0Hy!Rxtg<3oXw8HX8yn=wO*IT{S1wxWMD;$Ii?3flGBd1o%1my$O zSzG2Sg#PmM{BUb0#M{95BGdxS-j5l1ZH)ubD5bN_OOIDA1E2Bbp>u%u%`acy>v>8N zZ%1;`B4xFd4KqzdISMPK^-Sqc2jh@oT@~xt<}uVsajr{o8+> zXJXy`?LY7S?ce{4ie`Ivr}vR;t(jBmStj5s{AFa}5qwF%dcnaOquXycE(og0_$C?I zbtH-{7+ae``Y|YM8P-YBcC~SPE74nt6&SyjnIb1LkHAJ43c*KsaQwACp05kAyU5IC zRxG+6Cy9gxTa$s`tm>1RrK1|Zot}>Pe(Mn$dXQ}%$)uSMj{NjwHqqPlZLI16-#++O zLOzEb5W~2UScDI^S%+!;!}qmgQ24516MQr*FbtEx>741=a{Q(nb4ebK`F2Mi-LtkZ zn=J)f0Rq*j(!*kvvb@c~uA5Jm zH;a7^*7CB{I3XLiA5LsP21N=JD^N#KYz3GrMOnf&J-%4$)&%O9Z^x25tbPSoL*_e; zCNr@Fz9hAEtZ$l+U4uH2OgI(+XhOsWeeyjQxTw%aEPc5KFWe=|KynKN>YXhy7ubfIX3e0_l|nx{@&3;-u$zGIrZ|;N z_!K7Hz-3d4HI-zSB{6T0`@69`omZ4;ZbNz>`K~^t#lWE<6UNCthpc7oCr?l3OOW#i zjeC~$-Fkog9gSK_)R>{cbfn)2PLt;qYeGGYb<7}hdI#rWlW)7#X1>E2a1qEfY^e!Y zrokC5IK7CY&Zc+DOn-J}e*zpW25!VFJV~+BC7+XkPc*0SW-fjh*AIpDZoW_pR*fAT ztIliXhWr@GAX^@(XS}>tJUwml42K+{?`T}(jBzk-sx~4`x6RoI5>}&yv&+}G|9dz` zElxv580-u^k`Wt{!iF3*XiSL{jISM`p8#iOQw{R=USRN1T4)Y=kvD>1gkAf%{On*@ z{L_YOdUmcghp^nKNN=YR3-3tzSF^4p#TnW@;-E8hffHvkW~w%%4;74^K_W!XYwBz? zzM8?k!*kg3q){5IJ{E~=icq9CT00K5@~KhQ&qzNBPpAROO!BaBq!>u=z-vrtNVnh| z&Bw2fN8>t=_{zc|4k0;7J@xEc3C1I`dk*^ZdFhC@yt{q*`>|*^DQCjWGR>s3B6JlYEi^8xN4BtzS>HqG_ujDG zZDGBQZorl;#||-V<5TkBET$ULxN6gX!18ss{!Wz<#FfInC)b)A7M?gu10mz|lyOLR=I|}m*fuj9vU6&#?iBU7?k>VyW3Rwjt))!gKu0-P1-y>~ zcDEtfBq*mgYz_ER6M1unev@3({+Pw}?g48}{)mgCReRm)QG??B49gIu;%# ziNR;>F0f-Ir3_yZw5O#r;fL`G^%xY{42Iz8B{{f{Y6I9LT>s#xDzmnmx3w6*>^9xe z3XTV&AX02EZ6ty8tvW zUNey97awPfb%atAg{hXeGnWneTJL}7F737&M7_a@i3-w`eaA_0IKveQo6E)9^`pZp zkV`z?y+2hXMTeg{r)d@0SdRsNN}RB_eh9qac#S!`mm_CJ9G?Xkb!bV*>7`k)l8q;Id+PC zC(qe)>~M=g;9IAJwsdNnB;;Wze2dYwI)hQkIe7=J+GjVLdS=n3t(>0jwl>-0kRX{U z!XW!8?mm;sNg5C-;Wu{_1;@89^_laK_8yTW zBO$YyNs(2QDH9n@Y8F{>$Hetb{qy4qC-wXMvb{)(BCu!-1s=tF*bJyyWGPFsj_oSz zR*i?p17RrWX|$V!N{CiN74>w;21Sc=*5IU!K?wxUgwD3a zYcs^-b@$`3C_KB_4Ye=vQ*`)fr@(G}lMH^g7Ib+gQPyv_Y zFs5gP192f=tx7-LAF(d)rO9lyb~auFq)zqoQsKow7aN~M{gn=X-bCh!R8HFF&e!p2H=^cvDVjAp& z3lZ^;FFG;B%DJ(ED70?2`S!9K1^$N(0Sq&H7Nx1T^l!;bN_v?LYeGQ&z_9r#=bhOH zV>B}qC$sTo+O+JmQ)in~kb4u>1y_I6uHuLFhx>WDwaw!d4JbWZ17}4>!D$O2RM0nD zeBx}Q`D19F!Dw%FGp~3gf;+NqsEs`N>KHi=*>}sfthnaY??xEvRNbf5pwj| zLh}_aH}rsLkZd>0qxv4h`9UT*h#;2{$!XUJBY4JpnFPd_UBhTS^EuB^nnx{iGnR2M96*dWDJ z>WL-O;lw~!a^&0+Y}Hyb;_YGW)>p<003^P!iE8WUa2_$IoC9ltSOyKEe@)xu?lOLB z?|!&y%3xL@p}IajWr-BNYlMc(>uT1yK{)N*1tBf<#W`o$`!S50paUghDa5t7ykBkX zNc(Oc&kuL=@!{tD_kyOCP5wuc`c3lRQgzrOv{5~Bnya-nZ&)tVQ117xO)`})z9KBn zuR+t~yQdXwj)h%cy+Jx2jm#!-bT%ZE;*ivIg*!!dJOg_zm*H-^3E!EDzpaS1S>Vry zcclv>ur)zV@K_(%%+*x`L_W_^nASkcr6-sSsH&NX?LKJ=n6lKkG+aKU{c?KTDKc|GQ4EL|TODDm z4MCST;?4&*iVm^sx>pt_9ESq_BH;)~HzgqbM$W0!mQO$x6LN<&BSL0(kMmi-ecLe$ z@$GCFB)PprmY#v8OEXdEMXMHHVYwf|V0X#Fp6LJ_7!(I=ij~ z&fef`tu3sKV`0rsFu~<+lr6c^VZES=5s77SVDKVle7rWNhyJ{GMR?N+n(Fwp8Tm@l zqHm>|ID^4ixgs>eVAnqV0AIgmGurqzWAp66Vuz~cMwNX9Yixe~xcI-UU2AU~N0R(U zxAlGi_hm#5&Do_mTn?$V{bf+kg>m*;@a^LM{zdeVA}O<4?4|@*4+vItJu)+*sxtHc z{`lpgBJp?m3=JFMgMOvF3h-S82Ad(ZJKU?#X_hHJyd-_EGc~RYm~7HQREimT(e4sU z@X{I|^Drt{m z1!7E$H!dU0u-I&xXt8R$p`&SE~Bwcbp0q4D-HbxO1m+5I$RF5oaUGY9FTq{`yeK;Gchd zey?>@lPy9>(|k9~11MN)_ARSyK{mz)`bXn04O zeWsf0uixhbLMs7Zp&h`J?3j9Nj37g<$rht$bLxQpJt$v{u|_GOhLkPIdsY{LktWJp zb~FB#oc=zZN=Psn6$R5B!FyHjS_U22=E~J#-t-kbxq3(S=SS1XC5yo8hR!r<8Cifw zV{$ZBNsO<7zW$Q89ko0=_|g@0*F~?ZHJ(Moz_N7cIdv&@kwdZxzw|%LPeY#pv*U*L zEF7E?JcCv}IiZ6|AXTl7tJVMKU+;!)ElgxKWiVX$o-BFU<~=;LP`1vs+IWDSPGDa_ z{qMh&-+%l3`+xpbr$0UZ;qe=Osh_8B_yX|DQ}Y!3-GAY)9{$nSM)3H5V+7qW>lAJj z9MoHrLBL8V1*8l5DLt*)>CW^KVt0OX++E*oZ!b1y+vBJ0?P0gSe#_iFLoV8M%euNj zh=7<fqYn>hao`z39aM_-^zrw*4fy~z%%PZu_$ui&Ho@9-H)g2ag4B3xnXeB_#eUW!L-$=EI5 z`i!Z!`qJ-6U%ulrzT}tVblDmka|$7S?39HS;F|($g8htq@P6PQAHL+~c6U7AUTp5K z7VptlKc*Eapowb>3eGWGa7?FJ@xnJ1beeY){Fuw#50?uAgl5VFtkJ_9T?s9*21tk& zVO`sh`nNnIBh*63(5n;8)*c-EQNiFa>{u}Ky_WoxtvG}{yL?+K(r1{h7e>MCoJfK9 zx0!dP7+^^7L2Vp?A2AP?``e`h1uNrZc8{55y(Lg)UT8yWgT$Dp9$`b!+s*Y4Z`$Y2 zKohKr6g{V)pcS3s9dtrRcyabF z`ZnmAZNyZoz_wK_dI14mRq?e{Uzxi9>~498`eDDnT8L@bOtG?{Ckx=pZhB}KAb2D! zcE}r_VeYO!-ENm|qzW;Go!YH6u|RaXP+_7Dqz#;T_-S|j!%~ODR9H-} z#1D8v-noIL7*Lbq5SGF>VXyX!F{`w}XAd%1dx!Bv)u8YB{+2#D&l6&BvXgIEI>C+y zYRW67x_XIl+q?;)DM)IOm6~TEM17-kcno#r*zxnkfSolgp)3SjC&JacY?zUi6r4sL zxQMe0Oz-|?9ime)TLrFT1tN{J4W8GTz~s@YdmAF2ZLZ#AEq_8xf}5x$cHPd&ta((lj}c)lknDA9pbiYPB--q(x>bl^dKq>Xyz#!V zTNP9ZFF+7QTXZw_cz@Wg+pF;8IdUi|Ax?W6Q6>P$> zq6CBsHWMpTr7#e5C&YDoRW%WWo{Ed4WD8iPd zOi;Nn`z&b9Gs&@a{Pq6hy4c}l(E&6^IS_YH+W>}(&}D3vHApp{)x+h*A}RO<(WZ|0 zFTu~P5SUM2z@y=YycaS>HTH6sZwpq>shvj*#LC4OvTD=2c^(Xb8uS{mb6$tKx!qpv zuD5SK3w%y>dh3`zxHTzc=@~2%u2ms=G@+hd;(Rr>&GF{y{_xhrzvp~8LrcoQVsT!I zQj4l4u;wl=5|bazrmy~{^R9emOT40~ktC%nUUDRVSn zh%&bfCsRDcmx|Ik#RDA>&11*u$N-*!IM z4%oFZ3+8bNVcE3cS&sRiAl2FB?Ygy8(!^+el$gfY;be?9WapFvQue{21R$^1Lnb9$ zGZFzJ30F)9&yc42mcx|KH-_XeS3x!{5%@DJ8a2z>ZLKtAtOD=Yjv3 z*7D;@$YitjrGd}26rxuw*Hnvz7eFjCc@U2QdAa@ZV$tQz(~ifY!Zd9YnPOQ~m#f#q5OzLc zt}_(}pDv74OiiosMACT`5ng1=s;7|(dBj|2Di&UjvY?bup|qrK&K5W$gfIk}Hk{fM z<~mbBz=9Jn!t`L3vBkzFYb!;yT5B{zmvF*dXDZc5Q;G#Ga9udVf=OMlHoVW#SvhnG z$Nkye$}Loi!U`l=THuQ(EM)i9`7z{v!d$n75VTZNy~qrEaY}pmJj0i`kyUWT z&?B5M*KMH;P0m*UP_I0gsKs|Mt~^zei{1>k?Fn<;7OF)$9-D!dfJwKAotld&U<*Bh z;SRai33J^Rq5v}N{*tm+*);71wF9&#WS@{jCUnAF>1~s20lU>U2dQE+3w6ir9XueL zUkrK433Hvd6~0+fg`KX{n=-ye1u1A6J5UKfbO|TSMaLmet}hzP>SbDER7S<5V~o1T z)KoFgO6DSTB050f;G_2-?gszz_&5K;+HW<%0q1 zD|`}^bi_vY}v;wg!RXtmO%U06H}COxL)6>t)nVo2O4qZlQnF`B4Jv_Er|DiMvvI)a zCJ~rdo2%Uq>kgt^4KT{28|*Y_h`_){K_z>TKX9>`lZ5@*o<&_(!U~f_&CEMdx!2HT zX046L#^8K&FavwH`LJ$wW9?QJ&8~2W({Qvo*_y0LJ#?$a{Puc(xY?Yo+eXg7n--P+|LQM4l>Oh}+a4K8F8*1BK3<`s%-*2Nk;>|%F zKin=~&<9XZO6(gCPRqvObi8m-p82Pk17(K+O&zC4DxcP&T6QlnEDol%s!$H9vl~@* z6`Zq|q2I+&f4bYsw?2A$Wsa-W$@HANYrK*})D{k8th<0+JwBXL$KCmMb9H_=TM3*b zSWsc{74M>MQTtY=R1g^)oZ^tIsN>!C_OM;}c>D>sAeyCIW$$c{R8n(VAht?lvb0^# z;7+#zhx?^bPCYteSc`HFFsxQAz#g&*y)^K&xg%Y5{OKS2+w-NDSD~=K37-Ku!=g|+ z2^+AcQAx1j!R6fk!(o55y<22CpO3#D`|-Djn-<=#v<9!xqX2)vni>|!a0U*!%Rwx^ zxw`mx)M1fsdI2j8mJ@oog@_o(a`Mno0vxTg%Dx_G{t5Qyut-z9fOVlch?(R;?JECZ$`Pe9<6a!Z=!R~@vk1LcY(4-xXKUmj^s4S+umEyD313fe024NTJnHRX&sUpB; z__Cbk`Z|b2Oa=N1OtFn_U?<$WkBhDdUcv@AeK71g_?_k`2*e^s0Wsy*XdR5MRKi43pvGL9iCxAds6zptbZ^;Kg!w zxM5LF!wXnUD?7NdLnRYLS@57^Sw&Q>Ycsp4{<`_J=*sd1t~I6#F;x?H4^pFoPqWCooAu^v8e~R-vSdb# zELc^?w@gzHgHYTc6iv9B+eL>=UpcERM3#{ql6&nLM#9w1!egRO2D|u8)DWOPG$X%_hY+t4^~LSkM_GS78qI@TO#% z*C*NGpGF8RO)jY*u=`LInS_;WDWM~r>zllwj zIm5kKrfy$Gdtp`Y5*#P9QixZEm2UVxh)-O^++Ytko7;C7ZgVB$M~szqvZ1jd2n2q| z0<*>rgu!WXuWl~aM_wP{{(b6v3thytm4)kU58;FmBk6Zq-kY2CxW+2iV^nlfY)%SD`DT1ck#tfzX#LbelgxRa3kkQov;DFsOJ6AzP%1>< z8nCLWTbb!BA(FIV_3gP$eSWyHZauFn-6s((ELN8ivym2XLCM-daEE$#LOzpjeONH5 zcn@7+29_Qek+O=XK~{@#2}2xn9OJ5P4H&Gi4;NTVh~UZSYoM0|FIiE7V>4}dlA4uM9;@F1iF};(UvMKIx97X{zOzT!3y7=&y@czi z$+^g`wh(E(c`-Nek6Nv#0y&E8TGkc_3zQeuq`iz;ghJUSlgt5+2`0@s>YhRNOic2>)nYA z*hzt{L7*R;4$y1bMfS>6A*ES2wtibw%W>S;@|UnLk1et#@rv7YQyP0-I zFy2Xm?nCFSLV;(TKw#N+5MCYe)Dg;92PRF-I9@*3f&FeJgFjqUAWk}&as$_Gh}IRa z+6!J*l~tvzcarR5CBrX=N3TL#;K1rhuX!A*YL!jdl8xtK=nmwI#XFF{pWxZTvQ=l7 zRbz@9owTI~PX)NoZP)hn)#=gtXC@isxGNUVnw(d*hulMwkj>p@Htg9YrjGRCK?Glt zWt0|K0rLs5k`0crzEuM!bEAvq_pqyVjM{s1|5IJo2vZY)m3R(LW|AS#2w=V!X!GDCYQQys0at}lOPN2aqJN3jx7V~!Ol z%4)le62VmVU2lE)>TuGHnFU(V90WRqs{;40$>78Ltg~=C&)3e@mte?;Pcz>lcwVa> ze1KPdpmRFAk63VD-3Qeb58ETN>Yx71M(|--V5Zp9B^$>}jv(EVEyG3Hscw!JryouZ z*UchU?wuO1CEJ4tnH*LbrZpD0#%$kt>U8uo)BudWHlr;~bz5=O6r0$<$aG~h(#Ecj zEZyOg3-mH)kV~xFUVY@9+^O0%H+rIsE~`Fl)E^Fu#IHL`6t(ou@{CJY-!<8$&o%CX zeSiKF=~04)h~$xtVt_vHmA*hW@jglCb%!4ZPIWjRv5?KCXA9IC7%d^38(6kawWKtv z;d$H-ia)U*1OkAGBuh;+8iR^qYi2H`jC2wGuGzJ|JesX6ZXPpa^=#AEq}Eu;!mvK9 zB-$`hUW|0>iWg~Tinj+uB;sC#R3#xyL0N$0^@jgqZ8U5LGel&YfSIRk50FtNp+P=; zY+WhqAX0e{lA3QOjCZbA>^6qyJP;M6gQYvRvC2c{7E?{ck4Yq~=+QyuYO^|DoGsV$ z+TcB*V(B0}Id5W5x(CJkt)hC#EvW29)pnOmyZ>|WH}!PrlU4_1rG-F?Df772cn{vu z>L}|@FL-9V$0tmcsx}6Hv$IBkP6E+WjUFdRNO^envO4E&ufv{F&@o$;=6m85vtp%( zP>9e-5UY0hYR`ES;4sAQqyuTpwIkgYc6PL_bYE0rUxeYrJ?C|#gAkF+0cRCfWfg2| zp$%d^B66E7r$+J@h_6m&Riu9Zj)i-p;A*^HFH8eBhej|})|}L^ni_M5)mNL7c@2`D zP@#7^Vjm40OBcp!5!kY>OH5%KhgB6TmfybJw@ut%P>pS1Of_LOsvz0>T&NJ#)E&a+ zxMOjy*UPh4r-z{?$S$Vx=xFlllgzpGUM8zVfh|FRC@eVkmC(%gSTXVL`!)q2X+KSd?!(AcAUEk-p8=C|YWQCUx3+ znJ}J6$Yocn#evZw1OvY3=%|8tL@BL!^&QMGl6sGy^yuxo%`dCvtWwnP>(q(F8y$)* z2l)%DQLV;8O6(*(jJ=3SeB85(4+lo2gb3K=bPo&hP=+j>uTqBMy;XXQ`}+Oq>E`Xp z>t7C=PbQ}tV~1d@wR>l@51LM?QbALdA=jRkE0oQfDOeo#fqQf%6e$bTjn3U=)=-ox zij6MEQIAJXtE0u~63gh_hsEkJXqVGeaAdGxrLQ1OJUQtJ`5cV2j)T#$=&Pbq*|_OLu)X=Ij#2I&?d$cSWTx~tk-`pu56 zEdlrP{p!u0ohAQD_vsv-z$jYQl^5Pp?yd2_qC{3-jaTpaF`x9X?<%9K%^)h<);#2e z=3O|va_3kkG&}*kpUqdiEpXTyX2$EFhgGnnTxAaweqAI(1yMJi$@kjkz_f?;BD)9` zYZs4*eAk;oLVybOh*iVGDCGCUlY8V93|Fl&G%XHMX~C^z!Rw;Zjx)i<`>XxZ>{gk+ zFfnxg(L&UzTc`x(XJx45QCl2!BzwQUx;Wb$oq?C`djW166km*2VYxx=il>)XD7h7g zsTzm9K~~E*oAu?z$*d-tYz>|^pYU75?>Cz--=kCGo&ZZ=bk-L7!_f0x>F#6JX^;z6 z%f;ztxmsNyCtR$~7PHjOa4doqP(5|JW(%cxd0+IJqTyDM9PJ}-jb-nC&OaPVFKjQ0 zG$E{3UMlu3kUU^-kSxR6@2#P{S{%=9AjOX5n@yj7-r^@2Lg*lTu2-KeGG+?Vc%%^n zK87fx2g~l_a`XBUBUsN%9xvb{awq!YKq1-&wWb7gMFKY6c@X=C`0M`;zUdXLL=;uo z0VMX?AZ-fE!*%F#RQZ38j}P#Z=!iU#Jv)u4u12u5QlzqwI8al()3OVF{eITv$=j)Q z|JP{>mdA}9u!fp!OFHOOmBHIbHnlQ(nZl@l#(H^mxw<&o99^7U&Rdo*48iecDzY1> zA=Y5i3DX}*J*AA0W&7auKMrALpWheoJ9i@jXiMPKXGae<&DJegXanhffR8*oOKrU{ zfKVVn@Cl?g*74wjW<0HAP}d5%J4m*V19*)9Ft>Z85Wyk6BIsexIfrRqAu%1j(E`-% zlfI;fGf*EPe{Y|@Zuau~+-}-0auUVk69)=-1iEf9&#kQihhtp|{${|3a(y~F9vY*e<5t4d?)K+(p6&nc5q+zgH( z+wXlRo}toG_PT%hqm}uH?DkzlVRPDojNt@nDFRrDmY1d;5&uXxAL(NI2>JE*nLxoN z$l_>HirLVl9q%$Z8X>Tr;`^b^bD(rP7YU>*AQH)%T!{b_vU&U^azISPi+x0TZWHx2 z-Q2bZXDT{HecQ|D^H1$DUGjnr5R&s)8$kwFMbE}(!P?3Z@WZ6J+tBRv(_WTj+b>p0 z%utO4#CjEPu@pPoAV4A4Xva>glac5CK3v;hAQ|_je0% zLkq2$uokPM3Db;j8MvZ3V_W$(DG~>_V$jh>N9~62%!I?iX}G0Hs?U>xiMiA z7H986yE%N<$v3u?4d&M8xu^)=Syo>2B^C z60RF%ncuvev*VjVZiBQ#Cu! zrl>;|P6y-ra0n;Y_{b->>oz+Sc8S@mxdUDhHEgTkuMNaXba1LZT)rP~H*_f8e7wHB zo}1VjVxW`skn|lCwzwYHaK3WKe}|nSAaC%N4?tvXQ;OAPU#&=8A;sm!z9yDN_hz_u zfr$V9@uro#&HsM;{Phs*q@yh{Lm_MFYY|R(ylqS8Gukqbt*LM6y8N+h@?;^{u3xh7 z+Xbv-l{Sm6s>OoJ-3u#Bu(>0vR$GR^%gOL(exqw|KpKi*sZj{o2PJ4wp1VYpc3Y<@ zfANmJyHUvHeQpNGnH6M`?mn14kRDV%9*^yKuKOdnxuKy~$nGd1a~hB5`7xonLb8}Z zb?iKkub4i4{O$UXnd3b>Oq;4Yg@%a{dB8<>yiM#pF*M_S^k?S3%zey)waE*62tm2Y zq7jJM9;TS7-{UZ?K0=a^?R7lA-O*cQuUa-)JN6tyu=SG51X{Y%_;`fLt~-m>a@uy@ zEY_Q|iw}!E&*Zk#x-VH<-w^$b5Ol$zPZaAv9oKEx&6^H=&E6M0T{J<;2)>EcnPsd{ zEt%e)LsQ+`kN?-c^v$Q5_cD|HlM^OcZ-q)i2n1iNf^AZxqqB&!a_FK<_w`Skn(p?p zYTX7DJeCSPPOZq~n$?d{)M}JbE{?zIZ|!<>`;`vybK@*s#A8&52m)ZifQ7bFv!lsb z)H^(*uQ%=A^q8NURGc=(mllMGyjfz9t6P#4Qq-5!hMALqDc|1yXD&Aq(1BPW%^O4i z2TWM#f00Z#F}aY35s!f7%V%hexrth_nFInV_Z8TouNh~e@Vr=qM<1>YO3<%2UrM{Z z{Q@}%**2GygsN2T(16Lt;AKZhkdVL;WDxOhn6DXz_{SWuB$c#@)r;PHrBh@vc4A2h z5+#yl8U}bi>s`7ba=N|6pAH2)7rN%=S}P?_MP{MY=v)#p_u^X2LyBL(@$aDBzTMt^ z`3!CG@BKW_|FmWzo>~hanm*(;~hHg|<3MkcTAlabIp^SyL$fiTge$(34T>ku} zew!`fykvug>{NqlMbs~%(_5`;)e5-FZms%H1SUk_!v$n&#wbsVD&!cvbq%(ybnjBx zqPriWQ`FEB`Xje=%J1p=w>Fo7dTRq_b1$(3_RGqcXn|smS4>MDLVtx^y+amg1+@3> zX71hq4PEK2CS6wG5w&7<(d)NOsAU+oruY5rmXEvhZU4YGn_ui-AFqer*$@B!2maaH ze+qT6jI6Fp)`B7!r391A$faov0kzw;cUU8kMq4M%%4HKlBHCFSD}Pu z`Kxbg(OzFJ(pU%#5ydF_6;_cMN8Z@%5{_G%Lwmdq5J3^=d{n`wpK zh#Kr-bZEX_Uh%m%mlx}^#d=l|=L=)V$oGzY8eTcPC##&?gO-ti>(3LS}|(P z`H27eWHry4raT-0-c2LPkA?7Hmk?dmU=YyJDfNK$5Tw?K<)bsvqt$Y8wY++@Ssc&m zJxIIaviW`U&xd0lo~xzCGhk`>#vT>xPn&IBHc&w2CTqkW!tUAC@)<0Ygo)Bx-E0>MT+BcQ zrR2R@3-qu{g$=;7*Yxz83TO7fJ>(_V%6gpXNYtB_Vy35h zDF~QgL0HZ|ME!8G{QI26+o$@B&ku+7Od6~_#&>NUEX;W%sK`5q0@IsTw6gh8VK=K* zHfP5tKMjw^Zpc}_rerD)!;*sdu)T0oUys_$Pw+GQE58VjNTLM|S-@I?H6cUlRAqdx z(3Qh$c@z92kdAG+2O=#iP!WDYi}(U^vCaX!<1_{9MTMDFet zyFBOKbQ9JIZ(EJYf=7*d2i(tqJqhb>e6JD~dd}QL@I9H(TLEk6yaQB6t*0maqviS4 zay73^=H+Qhxnb(%>#UVIuZ>lb(WwYv9vyM3@(LKVW$)j?m`!8_{F>$!>{lSukzOex7{+D~HItBj&8U~=G?4R)tN+Ge%H|V(C&8Yoa%F36 z^5e-#`C_}F8@j^ur9IYXzVz;a{$Dd8RGjRrl>?)bR|& zH()&s0aqt)-X8Xh+A9`HcHe~wVAifE-$I9=r7_9KNEwi?F0QUFX5E*0!t}^|)pL7S zEAMpe6$}U*HyS*p>4T*W%#-u=$?>dd{DdiaaRIopNCoi_ouC6>R$l86h7L*mmh%|T z6QHRo`XaKgBFIp%LaM@a;%yz;+YDAMU+nVrOp5b_nN4t!OO3Jf@fu*pWHd;EM}!ngro_Yz$XaQhl8^)rB%`T3 zfgiX7_yp~&sERj8e9k%-)4^jEw4w{zMB@kfdJW{Gi_2d(vtRlaw5%n15Jm$bh4rMT zK7OX^h~J_ewPIR7yzwA{=|?TKsAvtNJ9ZcZ886&)1_5=Kx;(W3Nq@ zXNU+7iCWw^16G=$6F_5{#KwnB`0UcUQ()~;>4jU!3^Qr$0)5C}?Gg9L{J zE&B(`_-?<51SoD=^TTakN*ed^;~B+xquzWEQy-&Cx;pS{76Va9l@P0|Y!*B)sKnj;a<{)fEFEuT zYoySaO_b>=tpYJ)2`&f!-AAR+iMze|9^5*;$T70;rVz(ZQMVWkvK<>J%wVq*cPe+X z5VZ@PQxaO#C|Wh4ItaQDO-My4J8~jKy?(r1FGXdBhEPU2SQn85tczk&c4YN=Cf<)x zr(RU^s;s=i&C=qBU7ZX|iNV{ghxtm}ws)`9$ zY?pp%u-o_Z$nI+!l~aU*@(t`S@Ogpb(A z)FguEb+;KPVuJ4W>%*_}I*+`tjo=F833{0hNYn5K)0WBf!dbL|V+6o&~ zsS<@%I>!Pl30h>d6)LMyv0|d$-S6KmMRl^us)35pN(ga@Qv;ftWV5`m_4BD6iv1~Bt=&`Fn1J$Tb z9dU2=SC6~JK0wySN=c>o+NLsJJ%Qx`+j=OA5v`xbJGUL-YyVy}ybfPGNuSvIP)vo) zTvULsX&_Huw((OXmZh%NM;=yQq%t#r84_^rs>3@ZoTdxzhqXx-pCFJJ&_#zKd!L%d z+)7rVfhz#BOc$p40Yq*+tGUZ7SZiIkppm^e@1n_cvyOc!ylqB}RLtbz??Eh_Qm4 zF`if>q|#zD#Y@uWRW3L@yq&>*nzDU*u|AJ#4?gpm$Mtf~=%M$GQ+({Cqtzy}2RK+` z^sUuVH(KIeZ68(-vqGe=ce3}{Hpey$T7p1Dc-|g?ja}W{s6faB`^Lb-*Y+{1VDFO|6a$HU#@ ztkCysqwE=SFB!BP8>xd+NI}3yuK-N>3@QsP<3?(MR?= z9@YOM=Ev1xv4LVAfKa-rAZkH5)|TvSdIgmYHg{<^fk&+t6B;n5%I?h?vJRq;y?F6w z4=NhAuDdY7tX?!T)#AiLaqsjlLMN5W_zsJ)H&-1?V_k3q&l#GF^<`n%aM6Otjxx10 zSS@FQ)m1gTo1oZy;`eCye+GWj$gk2Pz>Y|`=VV$+dPG=+H ze8K;&Z?5iEyXAYVDQ2x;g+-8k%9~QQml&KBA;*idXhL4T!yMghszql5YuzH-Pnj%| z(9E0D7Yx|GUcAylpIwDI%WHDd$lAGc>--jjuwmAx(-;}1bWw6wl#~A(y;x)1ic^@c?u1W zoU6e;?go-qrLHKtS+S-vzv2S)>~Q1>aAJ}tq<$0GDF_w@HG1#xRtgSm@lG#n`^ULM9&7a2eJ?>WRx{c-~~a+6X?QHF|AZx*t!p7&$}iJw7;}CTW%m9 zo}gzss1w}SqG$OOnY$IXw2*-79mCuP_?PzAg3#pYkXdKil`O#eJe#e7@~N(?FA1V_ zq?n$e=W)K%(+vAuV$9j3z`o2FTH^&_%F{KL(Sy^5Quxfa-~%$m zFgu+2CS)4e>f(s|?VW9(~4?cqcI5vJzEikgA#UNt&w7KSoEKhAn?ut!< zZBZtP>Us|qZmUGcrWd2$02B0rsO71}RtiiYf{Qd}<@+pp*Gkmf11y@qw9K;umnXn2 z!&!(JFyxS3W}n-Rxem-H(z0J#=Glzz1h{*nr~ay9GA^HMs;lJ92rt z!MqK{hL}rIwK(oWTvh19g`5-`#;xA8xU~T~O z)QXf8v8#cMZ3Sl`V5yVwAu#tD%;X{knjOPDg~n(z8#VVB1x$5|th21Ghmy6q3*3-Rhq+u_C1`5`oT>~2wSzZhH z6(%%gty%=n1uIZsWtXPqLNkkC5;7f`hT5Pe|2qEtS;I2Vmd#!rGf`nqyeU50)TC=F z>(O4jS{<)8?;hVDujU)muYO0#U4u__v_#kQRUXkn7#{{zrTVgtaWqhZ4KQX~=9p7)#eWO4!{|DCP5a zY_DcrFO0TF;p0DDYs(T8#;D+6`bz?>G<=zC^E&j5tZFgJSz!~%1FWgF8VgoDo8p1TjUI7+{*=ExZXSQc zcQnUve+jZuk~9+(7!|~*?lrkSD)RsNJMQ`!$SwNgYY~S9XQrx-gJfhjR@s)Kz#eYp4f}TYarNt)7|QwUgr+h1(5Vr=E@t1P zbiK35fltzoGUGuimtcVphtT+B5AqyL$kx=EJYc3?BZKJ%)?a@={`uS6itC+0W~F*c zd*ovU9bsTRgD?04Q!2;Kef?=u@CTe?+$YxB0gin zb3~T3*CoqRN@M3axIDJ1t!z*{#9SQ?^Pa4Hf(+O$#i}NauaPwxacrO!SXz$xTLZ2$ z4Rd=n>p|@&NH+Qb|K_NA%r&yrM99H65Iv>sfawA9@o+t>*YYPssdVt>X+eDhdYH<- zL$liqK{SY90P)@97MqCg(&p7x`-BWo$tr<{*OY9}1!Lc|g>`74Qlrey&&Z2FJ<~dk zWV@J-bAyosD|e=oV)Dg!8@=Bg*0UkY@n2IDqbd3f>!aDvPs<`*v&vWp_v`t-RH&hO zSnqdygExP;udrEtgk9fyRz6j)8#^z1(OYrR%2Al?8GCh@m5+A@9UP4!w34wbLIRrV zlMXpcWB9Q3BJ|Amz-P!EnzmLCRnR0HO@gtKprR^A9B8YD+l$CEoA#a|gY%jibbug| z9kPUlh>dO?N6Q8?_5?(p-?i(hTHo(a8K#zrCE)y$uhKhK9TP4iAD)8RjR?%cadUHb z_?7qJ3xO>ZP=@?rspslqmJNR0Mqbh~A%7%39(ap&fA}6?iMdWq>)xQ&9j~-{6HUPd zved#T{)!RbUVR5_bkQ^Cz?!O(*VQ^3or2TnGXyA~A?!IjG&UyB^WSuQ`qmoXq-I(ra5$66Fl>Zewr1D z`T`$x(!!u=6%&1I3Ry}k z3j-V0DI7#MFsFxK4%f%k<6*zW)XzI>C+IR7!D1WAMB9W3KJxVmz9wP+dq3FpPv{Ws zcZb7jULME^x~;+mOmkQ48m=ULcC>IKj;WGmFzXXK<81re-ST;tjg`Z+P)P{qXwn(k zDynEU2OBk+o3LSNHtWN@@@Xg77-@un(#0t6vI`wj7)-+ZY;^Xc5=;{|6x{ZD`yJSc zbt1t>ZN(l3U(BjIh>xn(^oa1%oL_H$*<4-k-?rV(>wbWM=PV+h6IfQTZb>lzq{7Cr zkizJIy%bx1p7ibhO-%8;K8DD^R4PtA9UI$W7?PDJS^s1d6?g%El9S(qZ}`R+RKa&| zD`uS!yjnKQ{MOx>Z{UB}AKqAQ_8hi=g<1xW-PCldIN`C#9agqmYlq_JdDtwM`Bo8q zUULO5R6uidC@(mbuHn!tFlF`1mp({aJV9Pqd&Ra8u&l7;q07L^uhz#1MHCo>8XNLg z$Zvl}cwS`J$IM&^hCx+i&(_wf2Wa=;Y{N1}&)9eSyT`jXmxeq)URl-NV8f#+SaDaY zsZf3{C9pU#ijV)i0d4xQeOqm{^NH@*$^xR@1jtS6b7```UnR||D|HanBjRee1W$k( zILk%~ApS~qo1t!F39&_;OTteO~9MCbvix+WNj~K z34c zCwNviy?cC^6@NQ}FSf9kq|J_L>pF%I5mmKm0X2UXU z&boEYcnG%!F{e9wm<*6t>+9o()x$h8`4x2bD}jn&d>3ZBbc@5BV}ea-466^eInjSP z+}|D-)m_iLURDYc$qMKO)8LKZq;z3<7h?v3Y4zdyrN-MLMg>D1OohvJPG|UFMpX?9 z$aHDk!0Erjd~^K%-1%D#xE3nNBRFN_D_mFDLd(b=Mm}OSt?BV<^RPbdU{3DecG-45 z=+dRtSW?~=XX&eIO0#8O>&m$}IMQFiKJGu>zAY~EMMb{^XAH{TEH-**wgwo|=uKzO zhB~S!`|ItGnE=te$x;ceL7zb-L8+@0Sa?i>ETa(M`tkm$|NGN7EFHVs21dVRuUfe(ZvZ?-Q=UVlSD3M8emq!J-K`c%7XQAiia z%7?nICuHR(Z(q+FmSEt(US=7}W?4hug)#|#Zk!Z(q~y!z{cyF~_){$VeDl*yzPbM* zgn~e7a?Jqt#@H;HjuG_5A&{3KhVk_M`F9^KZvMTlJLYf3TZc_aRRionuPmsK4WZmu z*}*zSU;Bgci|ser?p5RrZp?G$OmJE=cBA3%ELJko8+f$+YIE^w-Ld!I>?_-hNCutOerY9ZgP)mDIavz`qR)|@SI_bsBq%v+HPr{AXu`b+#*}#=ACb~fdEu0@9q7JR^`dRyygJU3R)spTh*#n0OZ1F z9%ZLtk;P*6_1QX&aJGHJ%yZ?CZAQtCP+463#^h zqBSQc<>ZhxKbDrBbbhjQhG4h(o=|0kHi%6{ihjY;BUxsoV3iV zb(0o=)oQK9R0QL}@pQWX^rAw{M`J*iKeazTKU^tW>x!_0m+5&r~r{f0|FO%0rZZ;E@^zHm+J&3c5M0%W7@FzvY7zi)p%IpWKLjj zYN$H^_q{sXP6okJgK<Vw8>(y2ExFfYSaCER1k7q4f)RC?;EWX*Ul9AYTTmc#$ zgof+%9DCQ=MKT9UAhOwdXmaIXu-(*>OrW4a0N<7!FBY^xG6E~_5T!;9cIP+OYg(sw z41#oL&(RnOnaq1%bu|gr@vN?xQEfI8=*r1pyYJOic9-g)oK*@GMbuXDi8w`N$I)b1 z*nGWiAjPv71oR^-N2#||3}@eX;FFAxRNFk7Ov+HO+=)zr6H2~xC6utuvDX^Gwug|| zZ-m4)s?c3x!794_94AO>VBph4go_$)HhAz9fv`t`hVh5_uaj5Tt8U$P6KJU!B-7Qh zjQat&CKoludKWlcY5aOGHka?;Y$qSKw-&1LDZ9cs5Fpwa_@o06HDBeZCkAHEF5aK4 zl04WoiZIuta!p$JDy6cKOtZ{vZp>m2uzn3!nts8-b{m@aHdEN8=m5hOKN_T#V1}LOWAtyO+=0~rq+3h zZJe;LrcGwwI-sSOh2F~Xmq}L9qySiu!#WR`++41bj@dC;1HcDTixhb&rj^X3CTg%X zaL{2bIX;%RCnuAeS-huMNlHXN7D>zqlMtI^#ZG29_MeOEcbA*jVsbwLEN$f#F!s-A za_m@;ZHi6Kq&>;y!Rner{M`_UFdum;6btf6kGZpW7qHqH*Zb)1;)~TahxfaP$Wp*T zt0qEiNN|DkuK}yU6oUw()8sE!*BsjK0(vE7a!z-cpg=+j90uvY?~&%zsQ&DWRWl13 zoD>SikVTu}z-cP?$E+&CVi`*G{b|)s!uj4PQjwG>%`b8tttXuj{-ca;H2>#|RX6EP zQg)q7$N-#DA^3SIb>X><3P5LLr2Ami&nB7b5hqMHls95k7$<9hFd|O05D$6NnzqCp zs|ZKPo0SUrR2N4`tg+Q(i);u?qZ8yWR^udP0zjx98|zwcSrx?>o;WT|0~U_N^2rZb z0)4rv+J4uG5#sq_hT>($M_Ri}DthH8xLx*e;mzww3d1?3p$eC|(GJc4R0yx4n%3|B2cqUfB&hSxuoe}dJs@7K)$-hEWI z`17h%dC^ z4K4-YGJt+AaAoUzHNDAP#-ZVKGPA*RuygD?Sz7K&QmXKcn&{b}GR6+4lSvJ62+)KB z){wlr1AU=3gOIM;ApE{3oXls4vw*p#bbyA^OAAGa(iQJ!OAL9OyH6)m86d6Jp>UR4 zFs)>ci~?R^!>%{lk5oTka59Tkhp=jblaT|XIn~XI*eMo79rQg&cU52guG4U8a_!wC zg+=UWQnlL7QKUiSKGY_J!P(rU2}z0sa2F)nT^FCFHm&>A;;7$X`S2$(br(H=)oThu zwdxhzsE3Yc?Et)Huf@q&qZ)Ol^(=x(#-xgJ1kiUJ(o zQ#&kYq2aP-Pe_%M5eI>$8K@=Q`RsGF7 zO)v=v;Kfi}nHDEwPOj|93@%8gT~Af987HI&3P#nPJ-(_YV~%p11)7QxE;J+kvW=YL zjD3`GpXaE_m}5D?f`gr8SH&X`MBiOYIwN!0y?{#1^7oRw5Uyuxmjt~##1 z!6-5`qAW{kY^=ZriF#Pg`p7X-V`*DF={FTcP|#RQ3aZHtfE%rMN@#&s5S(p${Q2se z%o^tul9<&gaDt;&N-Ln0P-=@D(ma0J`C^QceQ&M7ZKC#oWPqMVb7GT*W4pBHK>iyF zK$O+aR8bKM5qFbN25A%zOgSAj>Xl}#)YbN%Y+0VKnM#U1`QF;BWI~`&=|R&6305Ju z9yWctz53_oYW9XZirP#K$OjRi2|0i*AIXFay3|KAGIy+Bot$2stm<6bWisHONkmtu zV2_vI1vLng0-#Y@j?^D^tiS)ToxGgz$}40+ULPhnn~BEeT!Cj4@^P%=;PJ)u+7Y|E zpOuL%tDsw^yp6XkX4SIjTtjSOsNuS^=bP`h?9!aP4eG`K!I{9D;jF+V9S^!Kxnvmk zbnV!muWz8NPg!PEXJjd%0PbYSG2ta>igOQ5f4_Zu{qE%E-SmAiyty=by5yw8tgHeV zp&COL$kC$`=_kwcg#ZX;Kv#HR?JC%h@4i~oafe6&1UXHj}7q?FbRAfO%- zT)o5!#>rW{V!X}oPF|mFmP(yv?#*OmD~^fTcVuWm!B-G6@V>|k9AL6NSo6XB<<-ge z-(tQyuxfxXibm$dah6sB8E6ZT-mF7qicsRhMkE$Q*|YO6zUzPDKZ> z7*rf}6aD#`Pp-~ZwW97GqA_~3AW54u@F?f27#BH^nA#CzfI`EQeq0QIKH^8t&MVG9 zCIpuq=`wmD%J_(hHYblmjLM3GTVl?|gJRU;l>#227_Sy)zs<=55f758N@w9=s3{97 zUqwcY6osk7L6#3TCy!xM0Me4as0K%B1>w0gX#vBs#S;%l(mvRnJcxA)A!sRok;}C5 zkcfV$(l1r;bsQ0CbGC$`C%B8GU|bYq}eEgOSUM^;7}5 zJOsA=fw5Na@tsEe>p<{*X}ir|r8Zc)uUdg^20$4&i(Oc?RG023=C|A5e)-enFZt7d zZ}`_Y_eZdp5d2xk(2HS_3O*uZi{((<5J>6j?vj2_KmXRAt?T3MJ?y2QkFhdSyGD)x z(UFGg9N~Wh$#?i|9r(>*20h)ky)x@qLHZ0l@0xR2-2lp{y|Em6z?ruNc z{&0+i0U#F9vTNDkw-9+1g<{#`S+nh5xXgv~@M zE(hM%?WbQ}HcDajvJ{)SEq)=f_9F1G#?_Dm2fmo6Ay!{sR&NMasr+=5eayTp(H_}M zI{bhBD%)|9-E}mv>|fbkOZ8}b$$fN`>Bn}3PvL1;^Ve7Po6TLLJI9gB315Tdjm zs~+Qq$EAI&>GKb_$5>flXHfUr>r}GrU}0c?$jnQVLhky|%G&My-5-v2E)9>l- z=D#q1l>IbDvg@`ePYK#qYFAaVf!s@=_Jf{wtaZr}G6_HJv3;Q+Q|9F(dX}mN^RU=FV&Q}~ao|SJUs}lR+ z#QB)hUv#*$KBSfA9cMlZSAp)0RIz4nkfl858@?c^-+!Ds3^gqjw5t3YTVvr!{s#;@Vm+}f{G!1a(ZWFDRbSJvkOHv^fqb>0YTmIFUp^wCZiS_3BT5`tjA~+wcGW&3E5E(g^e}vd#u7 zkjm^OBFzWwbQrq8zu+5T5;=NC_MhLqZ1UIF!g5vIb#PB|J>&*qkFQjM3}o9s6Ys$K zuOGkp;gMkvcX5$@IuBJrhax6$YO;+S)DSfo;cZj&jO&LV|L1?d{p$$}^i}fIc~@d^izT zhO%&IQ)>WR&(njy`}X_4tb@psh75z9lJVZP2$9p+gne}3&A3g6KW*xtzgY&&CkS~X zaj+r3ZP!#U6NEW3;86ayk9~&r7Z%&D!UnaKVL+9s-oSQnprWgAlrXDFdb=Hd__VXH ze*D{?UOkG=7tT!q-_SX0--Ck~^t4x@#Za32Jp=Lnoowpmd_L?SG7k5CWwB6JmQ&uL zMGH7vwCs>$utZv7skay8?@Si^{=dI}VW!&G;xe%5mG-?V8!eL{LJ;rp4T&n(+eVAH z*q>Y?`ccd_vyvPHwZN_;F9jUO=wtAQ<49JVR8YhR(2;7Wjauk2sPG6cwE z&fZG+(%E3reBu83?VlfqhE0PX=5t$ab?DZu))CZzW9F>2zk9qd+s*!P-i~d9HhJ)j z-}}96t(Dr~3~Sq@tHG(=Y+`G3Xgapr`$cq@-6N9eE+$;GiZfoDK#BMPf$6Hz2}Y8E zT60^>0n@SUUq7Ob?t;SQFVJx^R9CPJ!@(%D)_aF-RKeW#djPfDo|bH0$l{@SIdC?L z>IF`Yvz?745svM)A_t~zZ>MuPuEE7gO)mG=ub~y_hXx;`g3cN~SWh?bitF%*kh}{E z0yj6OrR$o!H^uuRBY_5o=w#UG7M?DrCrGUouzV1);!R~cM(}~Glb2p%RYJjjJM78h zE~m>wvGx0V$O&!@AJkR5Y;uUvuwrenNH*o$LKvtHyT19y<=xZykwk{O$VyDTI1@od zYp4}IMhy;J8}QRC^`5U+=_grYwLt4?b3>&B9+z>xSR~0*V+DQ}~A=}8-znu+K4@g=Bqsl9P-kz}~SB-I)mdN;goCA+DyZ#zEPKVy3&73Kbo)T72x zp(hf=a<;G#-uhg{XO29YI>=h~`!%RMt2?y~qAKTKM+hBQFU}7qB_F}M5(~(b7^aELne(C4!y4}8wKg8CwAbQC6?pN&ykE}xC^?7eYMtfy z$UESDF45g6RKkX2-0+WQ-FtB96C*gN6nyMquNi29<)f zz5SNUvo5lPpo)g2?*=T{)HG;LL28BslZ#F!+zwoEolZ;KVa;8ul6147wE+fb;SiZY z=Uj^3O-({Mz5Vz|qRd@Tj@q8UaCR(=g2ckH5!U5wUAqg4Ide-SA`nFlSA+WosR$-0 zY*@&GN{F&P-M(>HboIg*4H8Fr3-QdpqspmKK~9SfZSq9&i@#ZgZ1@Pv8HKM8kles;mQVGG`7S%4r!cNNCLz z#Og~>rKZ4UC4BVuAcv|!hhReWo z=A77UMD-AL#I7bBv&d}C&&JT+K(+0%U$R=pxd1zF*ryQ8lyke4hxG!@YK&gq!3B@G zWVwtA$%xiU8)fi!u`D>N{F7arD-3SpLuooMkccW}i-Qh<&#~Cv*K2N*57sL^rW^~X zc84_@F=RHF>Ro%Ud$8hk3E(#oR{^dvPA}r2o(?-X%jWdv@S$ARgh;%YUd0Jh)>tZ& zInMy(0zbeBpRcN@2v~_H4QwI1&DjA!zK?>nEXjA@Qfe|)vt4!zk);fMW(?#|552_4qrq?9~n7wH@_yIQo1ULB+9F&QGQ<~P) zB&iOtcn#X+czURs=LN2ziovN54r;`Sh~p^TQK7^Gyw)^q{t4M`yI-=Ge!zcHAF&Za z)6#Lida{x=)4AMqFA43WmJ@Pw0RxG!Wx^p_Y$hn+B)ZMtDCN9Nds^o`M~)8PT4%yi8YTJ;R{$R& zX&NqjEqnT>O)I17omqI*>7NXm*DcbwE(eT za?fy@i6fF4RA|6Y#M~}X0TJ!prIfS}uPEoQkcaHQhfvM1KXy?mRBH*BH zetNfLy&~&PIYi1NC>I4y>Sd$>Ca%o*Y`EMlC$$I`=nn^!v0K=nOvxH9b7*!^9$L)_ z8dbp}Sn%aI@f_hOJ(O!Y!%#i24+Z-#PxbNB<@9D1uI|`*y=Dkwk55+bhEAq-m|x%1 z+=@LNo>swf0}6NqcJrylw&qKRCk@(z;yOL4aQJk%And?69x5Mx$t!)b%|tl#236-m zY=MKtdV$t6^MjB3czN19?Jlpkdq@?0WC`i6oOq^c6OJCrI>ALr7@%$u%GAE(op-Y@ z?`v5@BS*Fg@O3~&+n z_|n80EUtz8gYBD?(|(SWQ4sF*2AXt9eBNjuD%KDhA@rC>R^r$msj)>p_WMOBY5SI< zRH{J3OK3ge7MtV~cAirMYSyOpsjTl(FRx$A@fl%zvnq9P)CO~0BJNFh-Rlvv1PSWe zBFVP9!V8-4(dGd6cR5D~Tc|+pV!l=xKzU^~TmvO`Wy*;g$R_sqyy}H0H?TLwAHey0 z<6}@>>IkDXQEI+!S5CJKnN{7h!FP)6-$9Fm&9t1T6fNk%)R8>Dsx5<-SPi8(?9Qv4 zlV-<7PS~nOq6zZ^lhcRoxxG0b)}d1lWOGzd;ap-(#kpp3i&V1O3_ta9vaHOJlJ?;Ko*)Lqq|UHSd{GvH#n;4#uDHa z*ak*!RX$ASTp_vYe&PoOTp}ShvW8N ztDZ-aDw#7dYDmTVpqQTpDFmB{@67}0pUCriV#-NGRw+*{^&msG&3daWRJeEt^)2FC$)yf#xu4oWCZ*#huLoJH#tn9wEwqfa%w2>SohN#&WD1>mG`_-5fRaDZ`u-Ep)W*@}1 zB#Ogo2Eno!&grYo_zb0Ie3uvt?C95R@+!BPO4q$m9vBy~wUHeG;7FskV|+F|XHr4) zNEes;LKy@%)~b{NsTCR0Oc=)H5)=itA+%*#pvd+y9`OF>y}f?}G8Rc$EoKAJc` ze!ZM=NFe~%B@@&RVOpchwc_gPL}saB;Rj^BlsL8J+})2GD8S=o$(yPlBUo1lCxC_m zIRp7@i{M5>nz}GOc{k1Vl0~#4+HwwzVgMUQF!hv1wyc9I^X&d#_}aQkjUrgX5UVL< zW>$yN0;sPy3G15YH|0xz{fu$b-mQ7t2hXh>j+KxQtu^U*Bg0Jpwo+-x=PQ7e* z=k1cSDELc}4DRXq0BLOqWfs~%3f z>YJM*JDko7p}7kF3jfxbg%8!0Vl5*>CX~>n)@i)(w-@-`qQ?}AYYRD@6E3fpW@G0N z>E2w{Di?Jp;=14T?WvsJZB}JW4^Fzm@?}Wr7&PuNlwc34G5W9X#hUgcFng@rI(&`g zqMmanbc;ZT(-?g(+Nb8*yux<7Kb^~Nw`sfW66z~x}FLg&&@N_wE-W<;C_P@mF zjp9RQ_CW=OX)rO^Y{^9oXmm?ca&RT6pZvw4{$oi5f-g7D3T{Uan%X{8FlgHiPM)4e zAU-YO&j_!9r*b~8QF9Y!ug%L=y~(TUvsf2nf+?&?YXgRgugXY) ziJo79_3*S5Dei?4IFpFmZRk*vb>eLj*Mz0L_qO{v^KM=KI(TUoU5d5QXcq^riuN*` zb68(HS54RK*zM~#JN$vD+gyu>wqv^nsbv*fF04#wN%aV3ooXqbFS#y;3w&z@>1kjzB|GG2@aaj7w|i5m+Nu~fo-9=OeRF4cc#*4N<2KQD4R zuYm|a7IY?7hI?&>fA(Mz@YfdmtEQyucOWk7%Vn<3?u_tx%5lDtO_l>FrBJ}QDbAz3 z1}Zl6-M(A&6xs2uWUIFYx|11RHV>U*3aM7#rsyvF_4s@Q)sMRQ0=ryv%e^UOT~?sl z!OZ>JxxAHMW2~orc{)K%ttmlrkjFTVhPlDDWa-u=PWr00+{p+&;`AMke-KdH{V<6+ zo6gl<4D&a^b_^(l4RO-2_2-Dquf}l0C#R*4WWHYe*|9LAm2P88AzIfAy-ryh_#=5)9m+e(0{u?@a$oUI*#F=^=1wSr{j z8}Hl*UGs%XeYDm5eO=m^@P$j`BBQTyutj!ipol@BxbEuGUs6h!5u6G|?@MOFx=+l? zwxG4T)?~a^VP4I2l&7-YpEv*fuwNE%9mWXB<{CMWYBnh?v`bImwZ>atTu1b7~oC)Eif8Of7OIYe~*) zNps!zMcTNOg_pT->Wt4#m8>E8qjxdc5n;b^NWGs1wAXcF)WfR(~<%1X6`w?7m#OwiPP=@3VM)L6dxssdIdjmg)_nO*^n@^ z7Cvsz@H&F*|2ggQo-)ba8{q?==4+(_kth98h;5c*k~8|_u&)=!c;kwDPF@<_Uuv;p zKI=~}sY11QMeZ60qbA%4etG)$w$3~`PM8sp1D}`Z$xAB}#5_(P3GB_GadCn;o<2Xq zaX=(LJ+6n(pXLKTdw%$_C){Agrwa}C3CG$d?y`D2&^ZJ9xa0r%CG=Vhpj%wBNCWi- z{VF4RFZ~#RoDyy^On5)+df_hl)g<~yTMp#nf}`hVyA1ebB`Ae#%WDF49xvr3BKz{} zuNV7@>l4M?2>zH#r$EbZHxuVotBs?^=YIjXD|c#Std#{wdVd^{y6`*6=ISpDJ?OoFPAea}zn{oHeR=TM z&VR1u)jqDx>G!5gh{Z!2PfpavieVJVt5ESOhx0YTWly5ZdIb%ext|wHCM~Z3@X>TIW^|<4td3q5A;V*z=e&2uu;X?2`d- zLP?Y2lM#;J3Z9?)`0@(QlR9_3?JC3qNZCqLISemvHleAcc{c6`gNrY9BX2>jZ&3cb zU@mT6(6_pbHL7Ppn>I+o1j|0?_$>ZqEw?eAsooE7mPf2~=YYWYobhW-!5*N2}t^45CkaKg9?6wGm#1V2~Lm zDQoe-Ubu9lihijm9_PdOE%|y~z^lI5c5Q++YMycuIuB3Cn~_Fob9!N5IqreB9v&aS zfj$mwvrx%43b`V{C0OdA#Y1!_lTTZk~#?<6QI^I{Adfz z7+U;PQtXE3zf;MXAP$4(l$OM?(f}itlfhfp?lYIO*)s`?lCe{9AM@6$l{_iinrwvgckKM$EaAiRo}{rA=+X=u z2D-&zi(*(>hqqnCkL@r+=4I1-22YzMs$_X~P;i5UUEL=TXz-M~yE{GqABC`6gmwT= zK(N1sMg_ke@!BQQ)ppC4BK$P;S2|4vy+l;LKY#P|ot-U%N3PH>HWpV|I{FNbOT9*jx6N28*Pn2$oav z|HRcU4I1S5nR?pALT*GieF#LQ;l15eD~xs>$xM`s)7j&f>g(F=3Yds>(3;tsq=>KM z@)D#oSB`1%IG%Ixx^TOo#W>5HgI8%4e~B&UQe)MwV|fhp>;`Xd_%gwqGlfvSODAr@ zsBr}NIVh*8(>h(E#*ULeQYTij&V)hp{sWz+N&?O@9|Kp(i*S;xl7RwdMJM@>> z(~wdo>A#v zaetxTV7?uVy{C*Ng=&q8ckv)i!eM!yCkSiG{dkVnwq0K(c-udw*TiTSdmUaW55G30EgCn8c)O)QJ`Kez!UJ==IZ$zf7wBw6Uyx zW^Y?>G$~B3t;+!4XK{`&5&Rd`LOl&6tAfprmMgQaTqC5Rd5T~bkixP1mm`ryKzb$MPbuHe!P&fb#J|_ zIohz{Nv!u6_2)0%%;(d;p0^C6;#f%XTGJBDkS);2WhH1`;2(Od_+Ak{zCzp*Nt^=C zI4;wRxitpR=}ka8V2&lHm*$#)w-I29RiKh#D5V84$k1ryEx5!B*^Va|y)%;Afr593 z!YZ?_JbX5^39keXE-A4bMw_m#+q~uCo&#WJkE8%HQIj+mEJ$oA~2i)68hC{eYgSe?YE=8)8nw$&6 z*X7%XkaNaoD_6pe7+{WEr-DR`QXpuLpB#Ulz86r3uK>HJ+fdD;lDBrRRu@Z#qz>L< zok1Sgx|`taN1DB7xr9*h-6(^LGKr^+Rp!9QCDJ#y{PcBxv(KOK>pf{M5+}IA;yK}q zV=3Ob4#I$&dQQo?-`kCFf?lUY-QZNhD%WnqtK?ooUH$lx{=MVdjdZ~sgW7m?Vk6i` z(Kts8-qMrd3Qy0a@3#K)c^3n#K|#1cz+&jNtgNfWl9Sg_25INPvI_&l3IgJmWDMap zq86VVf-iHy?=1Hy+KnUM18*Zb1?%#%Sb^`uR;rn5Adnhc-sSdGqh3lMwxN@ffNhO| zt6M{{68#;rdg_u2g;QC2yCZv10uP85jRsQ<%Ag8neQAUmhk}2dqV(A?`_M($zChT^ zEEl0Hsg;>QwWU&^`#tkslvKM?C$Bk8(4)n3)QUp?!5g+&Pi53=+S0~N1|ck7$2$T( z!mgG9m)u6K2F7??2YpfW*J9(1ccn$#WMy0!z#A2MO49O30LIwnki` z%}~96l)w%fSrOVEYaOoaWhPdJfMxI25)rBUA=vn!fLa5HsWdQ=HdI5 z$zJ#RdMx+!=JhmVO_Q}kR2g(a4%r4}{KAV1?!4N+z;1EW(0SIJ)&v~L2=|x4YRxmv zy(RPAX8dEouD}a4u{Q?6#hHWypV?-jMTL&1KHNCZo4wzd+GLvJ@J=AhtG(AOT~;U) zmUNlq<2Il-WArgdvx3w@b46$`4&b~BPnnBkz~oHF$s51vg^xk(XeYSQNLpb@YmA*G z6@tjAP0xe3KB3_8`{RBCpsn{@QVAgu`3Q}XjTc!W>7mBcrTy8n_6By(({`91v{+Ff zb4G-!09VRXD%?}5cklnlfK8QBf!^uWBUE9SwGM+d75cj#cPG9XfH!-;@sL7;7m6JH zEf$)`-~r}L%8?2*+i~IB%>aB1(t>lEVW-0KVN?~zbk3{H1&a#DJ#2qB03U-`6iQ8% zN&!<&4PHup#uMADH1Bv!h5=YFk7K_9$OiS%G&Y{ABp)FRDg{rQeE{^y6oEGe;1}2} zuBtBfr$j%O8o}vDhy!XEMqo)iw^F|ufW2VHG#at^d|W6v)g-ix;bOfnTsp3oxfy^r zd%y9J;*XhPfE-@F=~|a6yef>9$du!PxSIj^7^EGW-K`jTLw_^x6YU8uo_*C*I8PpV zYXCk5v9}BJO5&P4zj&6sD(hN4tQ$_DowBRT^R4~Z6*=@?Bx2F#$X+&{a7cL;&vW_*AfeZn??h3PCEj>W)jS*|=lZgq}UIsz*VT*Gs zwjflX_fF->-$DIm-(S9N2|<)tE>YPq!s>AQ*nA({4a!;-t9;L+{ioqA!yT>vU|WnR zcpmcO&6_{vosiagUZX^SsJGF zJivH+9&nVlC&ibRS}$wr0I6>fPow8tbtqVg9JfpQwT_p&^Rllyh_{6x(P5?5B4J5Q z{+t@sXULX#*E-%37&jTrC}Tk*O=$vggssn`fG~O6JD-dBKjm2NGMb`JlQ~}=DJ5kM z&5FMSWzcz}(>#sWBKMt%Y&ixf_%(gV>iOw-RhXw^sZAYNd0k1S@zMU=39)7J z5(q6`dcQ<*Z^D4CY|t9Q<&?AOw@U&3ledM~GgNZ1!8=RxeCb{*xOuDEyf<MFQ4~8TuksT#NMUdaB1e`v2bfbmNc`RLi~P%_}RxvZ44mS zT!-NwuFd?F5ac2WLx`=MTRcx-ys)4@o}TR|+$}b!Pz&Zs(w8Vg>+{k<5+}9@K0Nh~ zm#HK>0S1srAYu<1EZm#g;3uok)Bp#bb{Z@K6aV&r)A-5$9JVv~7ClzL#hV>^yApqU znNk`BUA>>eymsc_i!sGok}GbGi$ZRlQkba*pNd&~It^(-x_Kl1pCGqzUJ8~cBu-vL z-XqIX9q|!ty7}z0dkanWnetvO_?)g;J5d{6PDcImpXkloPopfq*O_cY>e}1AG;C$@ z=k;|-uGKDO)>TgVq(=n)u#EuQN-hz36iaFz18f=W3IMo7f zxlhk*o@bjIPaoJD%Ux{pro?uJEQRJ6605*_XDFT$XIZM8*_rz3(HviF{NvN6M3jWT z#-ZkeZzFyzEr3UIFDb^eET!hcGut?(U4O)hS?SY(rbk5IyIKtiI8I^v9R} z_0j@iR}kGsf-i*P3T+s?HqVAT?Q>O(mz~!&ehA7mR@7`uC%(Kiv;w%^+q=O<-f_Ec z10%Kr6{)2v2XELuC^yZU4Ybhmq-C`U$ znL^;Cq=ApSKK;@(`*QrF!pX$~WgR7mh2?`dwD=n?<>TpvzrWaRgbAjdy=zU5-RqKV zguZeTA`6f@=L+`S(VKv3KImb zJAEd{Y~GwoV=n!i`26gs3^V=%wrEd3An=^9Y%*xjWiT>`_>cs%q=xUDMg-n8e%=Yw zq{j$t*jC^yGD`M_3AptH*K^V)?PAL>ZK-yLf#7dAR4rjx!jY`iEmB`~=(LWba8D)0 zk3XKas0H!@Z?CDHfaG}@gCQA{)E)=dWe~;=-2JeVd zjHf^T*}vK43GwUG*wk#IbeL^0FvgDZ8tSzvvv4)SU(S=n`VZU2Bs9o0&jq1s%-#tx zNRGk^X+!N?R6l*4*BYozGRwfQbrz!KZJbxgqG7=(GUFnb^GNe6zFqJ}0b4bN=u;4! zno{yC|6Wsx#VgS7f%jT{va5opRN?tw-mYx9sw3I{QcYRIqYQLg(E|AbHr-E(tRaQ2 zK~iYI*MEOkZX^(d&)%nxqj&%#A>u68khxY?4yL9dF*MxW#V1eqKCW z4s8}Wsq3pr0s(D=hK|UB9jvh$EAZEGKyqz{ZO?xMNX_gIsX|h@X1!MQXLvuIUK)d| z+p*<~OZd7=FmArS*0hf)oDp(r_!cshDSp&>T%Q(#(Xr;OjqRk#M~qtDae03|6KmA5 zxuNzP@WbRHgkXc<$IoSWmF>#VFIbm^e`RDk1$K&5)tR)^qSK_b5}YLh0Z;fliz7;# z9O4Voi>r1wWi6d6)eOox5~x6y<{fB{VM{s{Xj@D#O-=ye>Sj)rZH9O$*yA1{Qz2qT zErxh#t)rpsgw8*1E|xBcv+z~xbIsA~Pz$9sCUk05o1c4%j`v8a!16uP*-8D2dM}l$ z*TTW-byz%9e;yJ@YW15J`|}DuLYz}V=t}1fdD4@&5zxGL4g|Bv@cp0$f5UC_B!ViX;_ezOdB!ISon$ssHMhQ9qX5edVaErc#2&-<3P04RJ zCYz2Bu!_c?-~_uSylV(0fyRo?^Bz{W-A~GP z5{mAzD$-e7oLq6F*CCcz`HVt@Wu)BZ`fskKEaVI|x`U z5x(4X$shUpaXS1p-@Ts_`LrS8>h_@%?r@th4IgQ(*dr{OHc#iS|f z1wtP?Y=Y{7CcR{FK5e?ak5~Od{(QQcbLu^?x46C?J{f%58&AFTVtfN_EWU1DYkd%^ zAAX--@TJk?zGReH);XIMxVt55D`Zz#A8Gm2#e4xfhmPT%B-@&03mM%?2CloyVU(^p%`QV4>eL)t3I8M84{nS8|GM`xUoE}GMK?}g5po%YD`~C~ zybxGW*zWFd`@@ieb9aK2PNt6(2PiL8SA{t(NsDRsZy@`_5O)`20Kp(4k0W3qYs3I~ zWq@|uO-PpHz0WAso=$O`0wAH13nrw-wGJ5jZWjikY2$mva_2(yutqtJ(4BMI1Wt5# zl4um#*|{Q)qczB){%Hfz$q&Q)6tc^t~>U7+;=r=L@$bWXzLEl-)W zpSmz<&(_j`tr%RNnBhYAXlV@=B9kFTR=Vjx;b7(kK0f#%(dBH^zRPxm;D)c?&-0U9 zyJL#hfJ*DREUciIn!ET-0y!~YVS+#!meNun?M!a1F)>)3*b*J?@qg1WaS}ww zSwIMLArFZVy|-S#B20BIY1^nhJN?aitj48;zS4ST;y|@}b_qAuMQ7UB?y{s6Q8Uv9 zaWR&T4@=^KzGGtHOmUzLpx9dz#-G~dDTUT#k{>-3TO`JA3t-_mf@ej@!@m8N{)lc? zi8X=$)#M?cS!qj6b|k|~d@5DDJ@()WzKc0&IzvaC$ABU?VDL+fr>W7;b={VAWg2Y7=e@kL^(AuxY^+uG-|xTGv9b&?7FlGMbelo092)bIpQB+B&M`7D*qc z!M&Ot#(2UCww7hD)tj6`%aTnuUdyOSVv~?dw>k?(D_~mP41p|ncUs9^9MWRdVd2^V z#j=6Pw!>L{A}rNxUSEA_n}@DveZ#lSM4K$<$kaes8xQ=D@w}ID`N)@l>hYUmn2Q7L zH_Ll(vffGZZrB1}(v~4QtIYMwfbPdPw|a5OH-AohZp+Y1BS&$0B(~K^ScLl`tdd(F zc>OKb&+l$e;7%HQsksr>+dTXbPCDfTSE-H;+ugUH2Oly-&(3eI?x)O|9vv3OfX&9r zT>5a)!-`uhrik_1^W_xa`-}7UAMopL{xgQDkTI?qb_2%SY0GWL13p_*D)RR8;8(hH z{ekbG38#dXN|0JC>{1oz2JNffJA?wmIf9IDE9vtegK|(emRfhka32P{UXs)_Jm5Rp zE)sm2BRW3toXp^CJ(NKuGDdRpdPuW{T^fCfi0z*rdOH(k_o5Gi(SuPWtrHBAgSXu` zlfgGu8QSN*80v@W`K+ihRmxhG-eaN=QV%&x=UgDVbIW0`*8UHr^RS$A;_m@MTF;>- z_%kR+p`tsj@9U;-&kg;yByTEIXfExm)_uq$z;#Nj)r5gPx2=usxgt;U{-+uCn%S@o z3B(*?EBlNe4;oMzT|!?U+P*yaUfeDq&u^~hJfRPlL}w!mTVu$cMzKD%;KYPRevf7? z`qcx%Ii8WVgvfQ<&46(2zC@UD2#B6b+OEBP$lscTuJlUmp!+GtBr$1`vSkh48ZGnR zD_{m_)54*d0vK-#)VN#iMp$(`x>n7@?SKaF`GPU;e!#B;mqyYYbO^&xM}TY?VQ43G z-oC@_JzU9nxW;oL%EG=vt%|9(IFQCfb~%WkkhzULQlSpfGd<}%cAqWuC+qFR6-cdE zf|3W+Z$9<$126fL29B2K@Yx{(bn8R8vkzTFQqz7AH6?5m~goHJAh!8J-+ zsk>E7_fswwr(R%%q(v?EZ>a%f;b5v%Hqo2)y&L~GWu0u)_tWBIYTe=woY?|Rx4AgV zX?>l>%^}4wo0y6wV6{xWiGax91{&{z8A7;xrLC{iIUnLpHZiFnDYl0pri2)+m#UMW z#A`8f?YrIaa_{b^tP@uqbnl#9QulMQ@Gs;?HcTb?mAH;#;`@V!y>1^UEgmT*8!8@z zpRMsjf-xjIHGy{ra27+(w+Ef_#=a-0Z-xl3#%ih$z8s`gEcV;}ijN=H?r-m|KAwHL z7=MhLheicAXPUV;0RR_BN83zr!xX3QBQ?e0J$NvdkyfPmv)_I``}NI>*UyjM9?bw| zWEp;%jf1gdhx47VbCfc{Aka#aD+Tti7yo&F`tH>XTwH?{(GZ#;=o)I2e1VAq;+%Qx z_PvVhm(yQ<{bdHKBv`5-q{+%Yg94XPVHrwo*jvxLV-H`? zr*B@&Kt-iWPf%9FVX<^(+ECUKyy5P`igNypYSI>tR@$fnvKe4TM}Pp}SfLk+t=^Ay z6Y)Q9&yL<5y`HjMEisO{@y1x{EQwW#R!|1Wsv#?_=M~t`FHWZ1#7aIl?=6^J*0J4& zC~7>m6qB-F>GGqXUY^3FzC8VH%5oVHJflUOaEYKX!?`h(@C_&&>MN~%o;{yz>wPT| zJTEs>T7(idjN54m=PQKb>&gjJWN%(S`|bF}yD3W+yK~-E;V?)$h-m{St!5xxiy|v# zRe_xxzk2az-o4bCY#^fUU9u7*^n6P?CD+0kwUq{oPr%JI{m$0z>{2FKG|wXE(urKI zI#biHB!)l1H_c({98#vN?c!sPuw#F=+8eK`dl1#u%)PTj0)zwPON~VllwY6l~o#d_XxZPcR;=9A@L${E@u7YEe zn3S3!V9C_5o!G(Nq^!)SR%CFpGqG7<%TcGtFyIZnm1=TJwAwN)?q0PonF$K=`s(_y zi1!veCnJI>c8U(Z6!Ke3nuZ;{5Ol#x)wiN*A1>POGf?T=(WTG)&}7;I#;9Xsi0sG% z%wsp}ST?kNm-X_zzxv~l$a4mdNTum1AGLx;SXcNH_7-*c=Q)h+SJcc2iAo*`?fb(lV{x!QKVdp$2Pv*%Y)T!rZ0zIh9~XD+{hS4Z`l*nRp+i&PJAHsw7#R)su@S9g z@GrHxZjn0bAIksRFKSI5>P=#U@8yYFdTUooZ%WLehpY6m0=n4J+xP3hOb$_5fh8&D!PAy5fwJ-1Jo= zYWqk^W^j*xWjmDwAhe#YuWGd%{q1=0%0*AW2t1EJ^0Gz;WN9l&bPSN z0v@o}RotKIAoV6IN~n_iTa>+KaZnVW=|!m7?gpFr^?S}I34xZ9v?b#Q!Un9YsxK?^ zoXVSVfLhjj+qJ2D4H^BO7eIc(H_zVu5M?i{;Vx5}P-^DU@w@LS4oKh56;R__XARrnJM zu1bcUsy5hdl>{?vm1dyB1!f~n@8#CY^msi6V@Z9>U{lS(Ft+om!r{x-5EPuU7)cKZ zhI3nyDmAc4L{%A3Is)p40bL%tnXUiIOzLI%wA)P4{@=1@j-aTjX5&Uv8)UFebS?pi zB1pATCxW-A#ML3MzjGDXUXW54rx9HaJ+m`?W=IKF_bW6|(9rwC zCs`}~-~@Q6djN3S2CPSpw%#Bn!ymVZ_TEKof!Q_d;(QU$0o4_hXkh=GwcN6DgY|fi zJA<3h@Y)L4SG9D2j*3g@*onGU8$xQ)1E{sf9l#a>UXq_xunoY-ISnY%Y-zQ^qr|SQ z#aY-#et@lm^Md$*TVH${rUsuiSViG1?mkKZ+ek8l2Y1Sk;hhO@CpW3&O9K@Mk+5 zj%&BkISc0Nyp*AGXA(kAxFcL)r%F6d8Or6C7}44)Jb-tSSV5hFg>94y{=`{XhVQA5 zvk8D#S2~JW;6#hLejK`dS;ziTPEW8q4I>*Rbm$|^Jh0#a^!HVaH66rYFmp3K07jbdiIe~+E+ z_Gfv&H5RO8@#_N%KFd7?e|9y`&jL_@DFs{URcR6=I#RGsA*{;+PE- z-p8bJ8mUhG!QcPu)ymUSS6zX7cA&l?I!I_y)u>0cTMxn+cN+VP3wDQD@;DmKmgAfZ z^R8N8ub%|Mt-=748M7m0D1KF>Gq|qFC%8DT}&&hFGT8<%hm+30Lytenod0TdBpYn}T3?>`@1&E?c;7oo~lP z-4*z0r3-JgK~?W_^xE5+jFHj6Md22K6XdowqWt-sOiUB%XI{r+xTn`DuH7qoGw6(>cE(A zBSB47)W!jyYl+}c)l8rv9!z1*HmXs)&gA_ z)E0-|fK89tdOMZM_NXh+z{Z+mg;_J4E6-{suy=u#^QuDVOx|Vve3qrAKu&=#Xd7|Y zRpu0&A&N6yz+-zc(>w8MB`1!RvCw@@_u$y|IBy*QyN5;P6x^Ax=jF1=QXWz(bV9aO z5v7XB*e^6k514XsD$YOOxLBhz3qwq9eHbA+BpMWz%!j? zt7~m^B9w_7k{WXe_vq)q#aMdS8Z8S;RSW>l&@OZIX$+z2=D#ZJ|36l3f3Y}CS?R%c za?KB*H_JiY#j-|NKp+4a$4p|mJpJ*w{MxPM*N*?3cCx$J>EFRx!C-5L0u2Ln>VV8G zE@8-o>DEj?&Hdszatk;sR%n>q~5^YCG@DhFpN`*?lL{z*KeNv`sN98 zT@I#Mt$>eRiFJ6(FcLfQ6n06N@AuWukCl4&kw1xzTUB+EteFheS_H%iI;0lj^lBcr zEavy+_--kA8p_()O{h2y2QS%+bT2SNT`4PWYGB#mamggG)vc(&(Hz!P#h{@xFo7J! zYnqptz{5&m1Yk{7%{VP~gzl}85?yw5Vt^lMdRJ;$OV*9jpeoKcCPH)-GInIL4+KBC z+v#b_@dyU_=YHvd(+(f344_h{sVq#u7?xuj2@LK59*^tId3`%T?mzKnz=nfgq$mL> z11{ftC4f~;uvJ@cYlH5Jdvu6pN3T( z);cvTUj?-IT4PAXLx;f(Zks}XS86Zk!dVhafFz~B%nRdMP0DH?nyF$GPJwx@Gr4eO z@1G)<;|3f2m&U%KO0aqOvep;lHJrC{_sU&9Wj)F^jKDb69nQA3;6etf4O~x+GpWyB z?|%99<*PS;Te+>AuvnT^99}pIC#%A1C(z8=V5RI2k=fY@y~biSqYvQfUMohfx0%GZ zJqiCxnczH0f(k4%v252`eHZ18)WT`udM1;v%vT?D_X+q2f3o7#`Y0i|E*TavR-*<0 z8V9+Rt&{ci(q6#Iz5h^-D=MrQ|2KRy9IaMZ<5ZNV_le|xX{t8H-1O*?kv>5rDD8MmGB`XG@WSeJ+oGE@?_Tq>N$0Zr*BpuW!K6F0{WFZFj%X} zc!4Sdx1jM6Bd`-cEJKXbHQYUhrHo$hfc31-nw_;^2!RK*l{omEh_y`N^cUr0Jcz_g zj^`!!n_%-p1C*7mRUw&EVXs$7sWm546n1eqyaVZbs`$!x@Y=yp8}U(SBC7V7LJ6!S zpC0~KCg7VToVFpNt2aWcmPd1}Ua|-tSz5o4mW#Av89r>^WY_myw1y;93fP310>*YxJoGJO%qJ z%bX5Ic_Uc!fW-rUZZ3Fim40w0^HX86-ElDh3grU1@Zjh%buHMfa=;qh2GG2FM6Pw| z&rL@Va}d^=Olish{U;zT*fwleriL9372j0ayZ1{&wX12}`sQ@fKJ)oQH}A1YLuc>D zREMAPIpIM8o9Y0miZug64{ZD>m3@##J_Z zQ{tvL?J1iDC1FxT?J&ODQ7jktv|nb{*PzBzSzC_ z?UxrjT-|^E=f76#=i#~gHYYV0-*AP%ohql{hKiM`>7D^$uFIL_k`R8y*MhdV5OobE zfk^?i8~aBA09P?wcQ(G3Nw32F*&&a(V~2I22FX z{865A4<9Z+u26*#vto8pvpG4h6XaC(mh5aqJM#29Q)c^kJRMf96MN%109pqURI;-P zK1&HpUEn6IyZ6=U@bUcN;}c{j?G%V^2kQ%PP6JEP%9&s|{kin1i@M%0pBa~eYYEgS zi-%wUEYoAm2^^_814K}#CX&;xcPF`!?RS@6*gZTVG|5w$jRzD-z##&nUpNbM{98sNsw0hAE z+>p4-GGH9+Xmg%){c`sN$pMH5p4N-k-NzJD4nDy#0{#VDob0J z8-bu5=z|682aOR(9Jo&~m(i%Crp9A`c{-PiEnNK`XMNQH?!!^mo@q_tC?^{Mv}jUP zbi*=m*5Ea)%)R1KSeE%0EdqC|gDJOASg**>T|zQ7#qI+E|NNd^?)v^zPVaZ;wHbUN z$DkEhtCQ|pVTP$vcRcBJcoiS5QoutB#EaYSo%uX5m)uiMd?W)cqA}0KG~$h+3119xjH_=j3z;6Ux=j zBcWenP+P6r%9n3JoU;sla<;bN3^-?O?pRWgJmr>Og}+usH+Ys53ieuIM7z$V0jh!j zfOmrWP2s~A!OJS=@VyeaT@qoLvGragGcg`~{=STtuQt6b+h(N-M-<1{0)QfJobhIB z{V+mO_tLu?>C&&|FJ-6}@43(6IT|Pz6Jzw+Sk4Lua-CwQt4`*-^Wkt&%NqAdoGI_% zNHqOf3AI!Jsn)E6W1Xs6m#tm@x?tS^7fQ5DuJL@5;-I&!EpZi>%=|t7YR9Z%U6IN` zC7JV=wgDY!OY2GEam;DB`5VUdb{C~uD0Ikd*;L06dM$XeVr!j$$;jra<;N?Ut=YAqxd$MgWk1AKBV4) zNaIyuo?j^aa&f@Rr%72~4_zDvZYjDDnO>)XX%rKS*yvZ8DJLd^{8c`_il`)IX$WSi zaz`+aB3A6gRM-?yO+C)@HNda&m(og_bqx?gitYgIJkm=F(Y(9C=Fh+LvWuw{2rs?` zh0PBKfYlxoh!e}8*}8}qX&JUX^i8oAX}w;ST=hdmt}?l&o}#j#Cqto`hzQ{k=l0At7zY7Bn3d3_1T z{v|=S_R`uVILzpjh35ij0Od{9WYEAorzI~2-W`|H9TGs90tbKtVF1zK1I0QZV?Q)6 z)qb;mU#YOHU^a z<~p}|E=?~#rHFQ9M&IwAz8*erVPw$~qq1>oup%X7!-_+b0N;QgM~gY1JuQE44eQBQ zXo~Sk8_yofNgM&I*0yRk$^(0mGxYFu;&{DL|M5ow0Ry-$3S76DTkcd>AU`!IPj(YioF=qv!GN@2FGzj(_dy%3PeXmA>xqrU*%xIfmf7oCqyO-Z?C5J_OZ4tAANF@|jdTGY1abNSk@Od+j0?ymbd zx~JOU0{hUJIZlXEHKbgsv$@uJ+Wu=xE~Pe8GeT}I+QfDifMW$r(}DY`lxzuNWqz3T z`r8~Yte=?LLEUrK&9!bq9ldfFt8QIV@FIDh#r@ijE${f!I*R!s0l5nxz_!`J{s{1= zu~2ebVL!z-zjuCb%Vn7}Q%jt~Jos!`Vo_Q$&b7kkC_w|$`b&4tx7}eUyZha7C6`6% zt)`B>5kDC}m)nh|oR02etGoHu42Sf5d6L zySbDBIy#zA?g%qb6S2$4sW_X`bZ5L6{R4jD?ZHRsjunQ$Yb+*aW((dwd3uX(^jXsM zLD%pWu!0~Yuq4N1mMVxFo2I2IiTA{1dZF@i(l7M}@9OC+AbY2ryN$!^+H|G~nTcDf zo@J>k4&d#fOVBwa!xA3e3+rK%HQh%6bHIF@E$IIEo_`CWytV^C-H%~14?p^*B}PdE z0GM<(31exSzwkMS`t9=zr{hiT1AYc1mK~UaV=^6Os@SqrtEnl6cL??~*xUVu8bEV< zB7l3alo@U#W@Bokjtlj9Oisa1SLD>5>&)4jQ?Z(jI8mj`;woZ9Q)qF|wEvEJI^ zGp2z}1xNv7JbSRVCDvm2*7v;pT|3#cJ_ZqrE7}3xH8APEi7+d1J774s1#e*5WQirgTBZXrb`5+N*W z(-UH$3iGr3ZLPf!kiJRqbH+REQp!v`4onYiR!fzHRUgXyV(P6iTQ_5-zN`RqZ&hO3 z9b?(n#AiZur*cn#De8RP|1E~6x(4E%W$QJU*u4vQ1QZPnEjxcUPxs}?-m32jUMjx6 z#`g#MFsx{#`~@IL3PxlacHVx1O?2Wm2n}#CM;8JBtj`5L*m}4n$B*NDx6^3!cP5=w z*1wefr`<=7Hs0=>5G@l4f*I^(7%Kbgcw>#ErA#yWh zSiprG2$p)Y!#V8ci+9koQTx(%7gZn64GLBnXKG+#zxKJ6p5=?nl+7zoi5!Gy(1X0SQ# z{OY-v{lDSoR}>q;X%V$poFqw&>Qi!fOW<}lbklR?JIwd$kRVF|GwiZ&E%T1MsagW| zLS_qqIfMGM99DkXpEygHKj(UCs(_>EgAL!C~dLW+=Zqizn?D2 z20xV26u@ATe?#m%H}wAN!`&w2{AsOhmPt|r(WzJJ;tN~DsM1AcU*HF)cR$-3H~GXr zi}#Ea{AsQxZQ)PA0!m78Sa@Y7@svEjf@uwau-<)sJpR$wYTZPV_DvfgrZ{@!`97jk zts2X1ep=A_{dEHyfRS#ZMf^9`2lku-7qu}lWv$L7N9Z%Q_)Qh6x%7vMr3wA zOkm?d>;MfjVJNTsIPW^)jIY$S^&wfWiBW^43~c38RA%jfn!t;ltryyT`iyY)ne2Dh z5Yu*Jx2Ar_b!ONk0W>&hSF!W9-wSV-RqIZfzV$K2msDZ8 z5gh~5;>q!c_i_WoR%WiaDS;ZWM zEQuOMRZs7ZP8RW8@^OD-n>wY)rSSj*Zr;c8#quT z>~)^)6S3#$x3zOlvSZ@l`Ha)u{f*7hfZ+jQL|Ev{f<1{qk5ty_rq}u9*Y>>o$4W|n zW0O1xBSL+$DzY6w?5=>QRSl41;VsX5BzEb!u{~sII%M$Kg^C4!+t(57B+4j6IkL~OIKM-dzcq$Sy zwPIs(ok=LgQAya=-|YK6kI!yuj|DH4F>JtIQ^E!nBKYN|@;ziOBYBFm>`ix5dyK*C zG$u^KA!1=`>kw~4&9RwU=hbc7^Zvp(xAxOxAbJcJQc;q?FM|Q+zyvYFt&HjU`!g=u zCWZ^fvo*Kxv4)?r*6z#+cQKt>iYuH+&m`2I$ z=u>wt`C8`ZGTZY`1@N~GWo2Z=6m6#?<_Vn0X5eZ+nsN3;U(mlb=!fLSZQWvkZ|=EQ z$I7?DJ`J(N;53525;;Fb50A(GR2G-@v%^7Y zRt}#&dVc@T2nQ5zv1DBoUxLZGs%~Plx{6L`#6V9E0zkahf)<;mY=i2$`>l1+s0Q(z zrJ3xM@*4|4cJ?1l$9ZQ%1ft=sA7C*nAr;u5w!+G#*g~BjE1n*7>6Pm_6WGcljKFqL zJ`O__hi0%{oa^wo^Fr^_gAT_(zLotJn6CaHzO&bb_O8YaF;r(wGTsAk*)p%{IX&nW zCJ5$UwF1bCU1D`M0;C65tJ1btrf}-#nz&5u>g>h<1NT{Y6;0K13N>~Yyup;j-seIS zFHE?T?{{0@&}(fe!G#VM$4n&86uQx^unq{UE+{o6O{WL(*Vmd5TpMlhOa#bj@E`(n z0B0cp12xYxc7H6-{^{y3(i<=d6ZnAi1DgvEGuTMAnqDN%dC|`uf{1&q4TGux)hBq% z;765+7{U@9uSJ5IU%LFTVP98rRdZ_ag<1pq*i=n`%eG;-Qh1}7=3(^Zo8LW^LJwVvJ#9_;WIth~6u*q+=3Pt#5Kkr)q zIr7T5t+!~a^-VeqEC_w0hQ8I;O)g*dCiyusFUVfbRTGWZEXmfI3KLA$}?j0Y)D zDKi;+?n0&a72gRb)w7pqSm2;zXPa|VY}DY4)y?aXmfLS*!vy177T`Hv#Sfv_A4V_y zs{WtpmH_l1>b}V1QmcfBxif>U(BKua4hX{50%ku}Otv6S8Q!x13=Me9X>{bc zr}XUALd*uRgXrJ8G7>WT0Vb#5x<-;>WS;fKgoJBenFPrW_^4uyPC$QwV!R#e+p)Lz zl$gBF34Z?Ks}epLRtS4&gq}u=%4uxB*!t*+KK$&$J0XLU-r>sVH6uuYnwSRng-_6n zR>qjPA(pvh>upAFY3pzSJZiAoo%e~Fz!vz(r{U+x0KaI!zOQWd1jxN5=mHcNL~0+E z!8gy|l;EqI?y?){J=7{CIxzC$jP6}aunm+HagvQT=E||B;eTo!tbA`3S$+{*8Dk)b zF$rEGkXRoq@1LFveB1f~ulUtr5tl<@JJVF?V+{gH_SRy*nR({@w{=$@6vD=^4*09t zp+ZR=uWQ-L8E@^U8~fX0D%S|fO76D#x?MwJsA617E=D-8I{lpA9v;73=DB7t4^##) zhgnP`@vy%&V~D!kXu!DBfnNj7Ymn{$t5R4~pp>GhP7}bUD`l%ZB1AD#E`Ya)jd8OUgp5m-a6Z9TZ!HzKRjpUS`GJu6-mg`*7U9JbTCH zXPt)$S&{`IsM=d!iiwnx)${%Vh@yw5Qu{~p`CkuRN(OLHrR74Asu6_61*0bX%tp`k zRQJ1&xSrR8ros~U5`obS+)WFbJ>7imViP>jT;}bM-P8YE_rAc#3LsrH5lUtuSK}#e ztfjG^j6Hl0Gns8MC+tKED+lQlRsZ~{+#TXU2X$>6rSQp$f`xsK7yWt#nO->^0x z!7kV^PHCfzt(0txfS#%Qxj4b|fOnU5ta~%2qf|=nrnNSz8=%!%vu6+XDZ=wyjIkUa zm)=n_Xlo3nN9$~FCr-!9 zRLb8BxAiTFY#kQ6!fO?97afXCaD@c`cY4rd%H5y4cG+q9n=z{>ydGAkLR9Thf(F`) zmfF<{L3ZAI2-xI9jnjTiDVSI+LmS@)2}&u$BeHu0R(#MgWd|59xRvp8AA^dqK@DmZ zh)be^tF1F5ecq%^uws@c)z_4Dvat^reUCxF`w%@5wgLA@BvAgJ zwQFsP>&TKnY3u!n*e`8Bh8YAbAdl@Y9rfIGtSxJ7X?A}8X5I$cAgbwGwj*o_E7aLl z_dGJ|o;nA{fE-a^5+FrhBza(*;m0q({Q7A&X;f9%U%6-wd=o7}&g+)5Lxz?)4l&{X zdFnapq9EC2_0W`ov)5{oK|=gA1cI){XJi1JdXCGuPv6)zp-QNXAOfr2Ecu<)c}Oci z{l-p!JRMyF`oKD#wpTVMokcp9sl*PQfe>p06m&!+7?(sdV|_Vt_Q(LbvZ|(vLVoGN zYJ;^Bw4UdpEchE=2m|a|V|ux2ggWF7rHnD4^NFp?dFKz#gCsgWjLfVkEdO&{>*mq2 zy?4g8n44%w+%<#Z)#xQcM5%f#Z2$QeYAR#KGltuJv){R?jTETvM>Gw597f;L&PckCIiNLAXJZ7%W2VX<< z#g=hlB+v*7?3p+2uGr`l*6Q&fH7JrpS{L2NTAUlt5d1`|sz*`ym*;%g(rA{6rxB@U z>B57IV7$r7AMoBUALlVaDmk7g85=aD68Kq{JlD1=d#%SkA9jdSg9B6MFj>J?8iCUh zjnvgF?U0bw#~odEh%+bFi1Z$GT0jD`=RA;Hb;1NB_X^583{5-4nO>dLw%R4edppm}1bSwV{_hKFOJV4ZguOy9p8KU}Z(InLn&GdzSZ&1MtWBowdlP+S$pXB51M1H%Y8%0`u)l zc_ZK3$?l<>-T=*Cj;d{Z8H1`trglPCUz)3t*VUoYq^yZyXn6j-U+MOzkJHVHL4-sr zCV`G}v<_l#gl1G65yoCWZ?E`IfM>*XZTO-ZYD%%23Y-UNrZ)kWHXcy+-x$RDvE$6F5Bsb*L z;2c6lfP+C;X63^8#P#7TeEAE$LQqYyo>;Lh1$Mv4R(M|Ko9)89`Q(2;{wutE;o}?! znOckIU`psYAPwVtNDJ6QEy;i161ml*+l@S|efhrIe=V0f7tSb(s-byPqeb*;(Zp1W ziq7|3iWtwB`ShuMtVfcm0d{8Lurk?p=*{4o4h<^QgTp}<;Kw(xj|^)AaLC*gQ!f$d z?6YY_cTfdZ!qtUMZ!UbzrG}T-!ZgT`989j6vWR86N`M)3c!2ifW-wI z%Of7F(*~+n%9u*Z-Ss+vKDxu&Yub*ecnz;42;$o*9XV-DU~q>RL#%tocG3ro&<|5+XwYVvR1RsB4ve>1%{Ce5fB*#y^h z&6?kyBPHBhpmP+ow;roaL2&T55V;#`jKPH&08Q5s1((9ITf)qQ>wBL^ zh7HCeD8bN%o4{%F%HL%pi_zsLZ5+`mA@hL@&LG>Bk`;37d6)WkDzn9qR7yZbD)-L=+JN0d`N`zaBUxeXz2FQAQ4&ay4@>uISN2*o>YMY=>Er)S9RngEiB_E;}nw`>;DT0F@i}QvZ zXN=!x+E?Mfxp?i#uG)M9TPh~Ij4UYG%2`1Dvy-&eNPmY{f3fNX!1>Yj@(jFFXs;jz5 zUQ%w3T!gcB&AzN~^eygxxH_^N#r2WK_tobvPS%8u(HSsKC$vEh1I3h_sKUF+`x}Rn_uSq;@k82%?HieZeQK( zlXu%AHhKB`ySVMF(be#7yQnD z!#D$}?LB8y4G~W^mzPtBNf@tNVJ}^01QL~!3c@H94~^aT@6x|UJpWcsA-39+RYqpm zh(U~MnhonhGd#I+&L2WtQ+=~X6ue=I$gDdQeJ~J%!Xqw2ih<_+VT#0ShfjG#3ov%6N-+Elh9R)uH&23kn(yAr&HXy+^^ zCkI)eeT+t1_HTjSWeu!>E`cCBgy^OaOK2H>7d%LL18uZObdXXdO2D}T{`leUY|d7z z#i|m)gbeIGDO{xxg4q<%xv_Tuan4q|=H+9bQ$z$!9YHh}>3J7DfH}#02yxC<1N=^T z+j=t!6oN!e+ENOl0Z&i|ykUnpXRA!c5^=_=0-pd~63~|c#*{q7Z5VKb9pap=swRS9 z+m75*vLyxZgPM4~U!-8vfSv6S=WMlC?^?wkb`DxYHdeW~&Q{JE{^-Zv@wX4}=W>UW zMYAadtNy_GV6V0#br}oVAEX-Z>fQOfReApj(Q?#umIW9>umZTm4yeMHOev~ylskVl zSsk6;(*xZYruhw6#2-`rk)NElzYjifb9W4JSpNRvQZTIIEBuJ_Mmx)sMeIzfl%m#He9T^SCe4cV65D>6Y% z$g#~PEmbswL=MOgr9zJk1(hvZG6W+cKy60VqzY$HweE%OeO8RT+8a+y<*O-qFT z%!;XoB;IXfKMc!bMYwq$=m$W``?K%oEVdy^$c9K2*ma{J9Cu`bywGi{<@i}%onLJ3 zrXYLHJO*L2s#mlC}H3dSq9L%vT09X>)hJU>;Sb`W2?fL*%tS<)LAU z$y%8rt?x1cKJ`H=c*I;q&i3o9JQj7l)LZn@`Koh_ZinL}V_H3oxr&}WVaBDFNR@0k z$VUoTlnw%B+^njm4hZ2R<|=}=UuV|}5>4uAtxQS!o?Yq!IUS;{$AO%F#9T$u_L$M7 z*mV^k*4_w^LGTL^h0c8a+K1_6hq;QRJz+}YjK)SJdA z&bf+3lp{GTB~QLWY*%`hbF zFy~x_B`>8|&;r+mGjLG^z^aV#IXWwcAz^!cazAN8r9g;UQm?LrD5Wh81@Iu*v(t|u zuNTZY6GGBbP4yx({KYBl5%V|_PsZj#6vK$H!<;jrY&m3U(J+*!wfGLkmGO&eoIZ3I zbIycnk&cHa%f%kwA}lo*Q@|E_1j8M2uN~%`2|;Lg!?6Gu?`-kpq5bLfe}kKqj)@^dsZIZ&Y@g@jsZyHm{y1|O`5rH(W9 z_M3~VSpvpaFT54N(kv{HYp#VY!Zpi_hLP`^1#>PWK<{S{mo!c?t>~(1^cDdjl>lWn zBz-%~$*j^#O|4|nkUx=J`qC9tB7tY4Odc}$9p+p}5C%{zU{!i)Hq}D4#u^ep#QCw# z-C@p!1O-aqBIt94ti?l>0l#6}TdyQ&!q7c;m~$Z^v)3eY3%gWsQc4io)(-0t&}nfT zhIW{9A;B0VP@!YW&O0ZSUaH{`lc20~u4Aq8;p*(aKFo1GP=V~U34!MnQsEb9&)f^h z0y4{3U0+|Foqkw#P_~ak2&(KBSgZw)hXz!tfdxfn*?-d>Q1sW|->zu_^at5c?5+~o zIBR)=pg?OS1UBe3<@ONr`}^CgwXa0?2Q0Y$c6m3s zxzcqO3Q3`O|5hci>a?8B=LXq3dY=T$%gyD*w{r(kt_C<|(hYtZI9@gIW@yPC@j90$Iv3_`D?$~k6)aXJ zj#})xRG~ntYf)lsx|_R`i;I&vchTUMotJA7VG#;B?npUbTU5w$b{zf|%pd1ZwDAqp z@Zeb;dGf3t7U4k0mjTh%@VzJJMF)k=WFBdwH;;t*=61b-Jb)TDCbS+LxwEqz0yzSn z$KK(`qU8OCue^@sa2d2+s4dsh0m z$JOd&dd}Uk%PCoaa3FWx1$fE$uuE+(PR}-%r*|ilz)6Btl?nmmUDPdV-%0|p^)fgF z*&+E++xxTIyR(&rbbH+8Vo9zXgVBvDsX4YKVF_%Ow(A49%iD#!4{NigdJOFFKII%> zcwLvhksn(p3ZyZr|r+%&US^bI_1sfyZ6U<<>VGt!J>p;Hg1Iw-AvULoFKeOMu#S@PVWUM+FP)k71P* zc?Ro3bI?G^LlIOZeQ8i!iS19Kj8Y6D!UB5{+&r#OqCm=a3Fs*^ft7a8QW=Aq-Tbo16eShMDoHu7*A|yl9Fk+r_N>iifEhumo z6?{5~d~q}HzNSH)BnVPg=*ohp>R1Tlbi%re8I?IY!86yrY z_U>kL`zIW?xsvfuj8%nPAkD%11%BtPb(Kyp6bdL6vM+nbA=O)%87v`^v~jB1Lq`4oyj^K?Q%RQnOSvx} zjWHAhUP3lZ_a{ZJv?IXKUTS*&{hagU4RA`D<2GZ_j3QU<>^drVexc@X zGEj!oF)Imo*fnf*YAiy_SsER59XyzqkA4Ms$%+!3>Qcax)T|sQ zF~N!u$j7zz3uff_==Z0ZkV|XIsh2*8-ozr4Ju>cxEwxAJG?+ZOci5$AI_uoC0oitfMlwUu4f`FS_W^ z3XqUg%+e?!D+@Q7IN#mn(fjLa!C`lHpyQf-!kvNq(32$$3L#>%Ex>hH_oQ{~{Vu$i zy@I7M%%rLcG)*)h`yBf;rN*rDROc6QzB!QrCkZlj=CCOpz}K_`kC9P@l-8QD`NtaT z8*R;AT1YPeF00C9yAn}U;YX!tPi%a?dj!5qt|+Bqh#eP6$SUw{E8{?bMT2xE5q>|`m}=d}x^*ey%h zl8t8!b&cfHgO31zJK$^qu!S9Fher=%$pKTw<_h6Hw{_3c7oQH#cR6Gn`@@+xDmkxg z54ndVL7ThH>`}0eOdaa^Mg%*_>`M!;fb$gD{w7!4Z`FXw;HB4zUF>j~lMU=23VQ%J z9>8_tOsd>p$9WHRM z&vto{>CEC$OkmVFV}&DjY?o2On99Cx)sNoH2i-VXfCbHgpo6$7VE@VntHx)YgQ{y$eT1CM|ZN^TsceKW7UmJBg+yxo{(bs0!5w1H< zZ860rwmL9f*$lU_Zj+^(f4BfIa|XD?z3tUU7IaQYvbkBXx>2R${X_kHv5pOwnAxbM zca||OU47R?n?BdLPVDEC9o(Y?3=zr08^r*A-r+cevx#LWomCQ>4d+%ZV}E1qpv|Ud z3#m2GT7oz?plqLNNokb&^Y9)NcX$uttnIEO+YQ2Sg2g124X;v$yNG_>={i3{LFQEr%r zc^9_EvW?C4RAPz#@Zg@XCTf<%z>bq_DWhW*?69B>JO}JMzA%jJh)hG`n-5#1#Xjw0 z;Y%x!pDBDSX+Zj59Hz>elNzRUacw}t6aJ;R*TR}S6$)=+c?b8 zSTG0puY;5KTgET0A3R0_{;CPLQ3Y8=Z9;{hk}WaX9M@dV^Yf$QH=pK7O(1au1`%*e z6rW_yt#bfk;lRf*Hh){ESU+v5^*vVYS|(v-Q(8uQrRl(9Oza+tWb*ollXs`{me(1n zq$J>L9>OcWE}w*wwV*VkWY(2y**0ErFH*R!d=DPnC5iw*An{Mu?N}<)M=t~LpYIo^ z7k?dX`QUahmDQ%k0&B^Xz??i&^j!;z36G+TdLmKk#lhjn<#xIHPf|O8taUnY2109w zsZ|Uc$jTt=Fg;;G({Ow=&&oH3o4~;p02X|>Q9?T`x$M@mZ`t@D!Ixbu4rW@15Dd_x z6Riu_BNB7P`g`EaNFYD{riXuhT>fKmv{nA<(>-+}v8+UaA{71yQ&_FWMM@1Qb7*^E zllXOyPruBxN(mOQ$*C9&_E3gt_z?S-dT*5;%YFCx)2HQMAKv|A-gz=P)yS5qqSo%6 z(H#2YvAG-zTZYJex~||!?g~~|gMS{ql%@O%ENYs?7gI859BUiE z5JUFBYXIIltU1t=3!s;s=Lc+uU(Z4Vbc+yVgtIf zEixw{ik5X{)m_THHHJS*WG>hE_MW`uH=VbyGP>Fftg_|!mJHl0KrNzjj!8$uc;f0b zU$Der-U`#>aWG94Y;MLm(TckZXQ)8x#wU5jZDzVX+!xtJsJOfMitu;6DL4dhS`S+_ z3~j-`&v)*TbsMfuUFX>aMrpx^X~CLRX~&-6^z+4wZuXT_n>|7kX#W!bzQ+H445|0x z=-|`xXt6kb6$O9sb5{nUk(M*3MQ3e60bmS8Qo8$?bsCuCXTsl|E{+ejA{;|q1XG%N z>Y&Mjv!sFQM6W3tzU|1-!|*d*7TaRwp(?$w*(5Bruv&Sk*uy|FV&1?ShRNK|RC#mo zcF;)bl_!C%OP9+&{qtt|^Tji8dbWIbwtRd`TlY3Ihnq0K zTd*Q}aIID{-yEE8=Nw*;56hf1z$F-FYUJ3jB9-OH_}*CovrhW?=a(upUwN=@ zrZ&Vsr_){r#^wCz;%sqx_!`Ud+sCb5g#bBfvMuQ_%|^l2!WCN?y-Z=$0pvmE;py?& zb_V4I6&!18;TnM}VLh5oxVS(BDdiS|?Ss92P+`k{wJ*r;e98#GJc0L$JxM?iyKcdP zSV*@)KAiMcRP6-?LIHch7D!IWW(+1mfmSkbZv|H#cD1ZI|2f@4@kU zb9Qm`C!IJjZ#?~#u%D|#Y!S&Eh-RoFis5fAXd^>Pe}6%4r~4j z+jVzUZeH9>{`K^?oA>kSlcx7ZP9zfyTeT@sDkLeQc17bqII`tI+8-lB?yi>C?caCp z=60LWT)ztM$#J>?4iczQ2KEV6T47De*i(^)V`09w+a(mZy1e@Opu}t)L{+SFWQtr& zon--CvWG8Y;|6SPq{HRSgXjlUW~(4$uEjeMt#>fm4h}Ikqk(a8l3aV$GF<(*z3i7I z@$Y9V0zeu?aYSw#xvt?=w`o`r&rBSp%$maZRE2jaBSabcSUD>{A!OV#B9jAv;_SNVt)~N>$kBC^|hHI(Kt-GfffPj~3E5&4{hYAPZHg0*G8C z+dy`(@$pRb#}(PDHk_K4jE*=RsVEl}PEs*n!?J%AVDM*w#W}N@(c4b={S9%Ep+rGyuILb=rR}_jOoBEvj{# z)F>NxRAqq!XM}gbZs&62G1;Rk-E%r)?W)Motd0)gg3iogo7l4SH75Swx38K!x9;8q zp%f=cwG>0PnM6cE7xqbo!(Hp&AGEl<>OXchXa)ztXJ*bgTbQaHysH8zN5j{s;UrS1 zakVd-%29Y6wODmaL6vG3SEOtAQJNy!-mKT`%+9%}bZ|mCU(<%VV1- z>i05tetUj#aB=h+^8CwZP5vUZ$lQvEt+%-cVJz-mNU`P$do_5}ycc->dAppwKcu^= zWdm*q9>T%ki-cD_}P(jW4Zagj677;MBfq)|?;$8PO3Hfcbi z@8vH(zZ8;s^`_?K*X8B%YWN`X_RaebC$D-D`Q@`#_zdv06xl$TVbGlQQQ`DjHhhU~ z)Uo+q;PKJ%>0+zNYJbRQD^JIP*6ie7nsEWw#Z(_-RCSmF_9XxC-Bxk8Kjbs4T5zh_ z*i;3fTkTV0-onBGIqH&qFMqLR4%#2mE2vx2Oe2Z{)Ty!1uLzD123C$@bf{L+w|r*& zLpTg%)rNr^pIv1)H8AL0T+@!dG1qVX;^^(-;Jkad{26x7jrZ-pu3Os3)$f05JGy@VOVc%TNlW-gk*c+#WfyJN(Z+!52YfYz z4+K)HkJ$muJ=3GsD6qY;W#w`0LzIGDEQ*mJA0ZFv>4cCX-7x42dLCT{)$q~V z;*79Oco=rQ7TcD)>+5!PyEk0|;FJO_jbTc<&+Njeb7I9MaGe|T)Ap^E{i)%*;23Rq zSHZR$z`6of$N|QHiODh0ek7>-RVO##%4kzDU8fSd6E2w=INF@9cSwXDc=EpVh|L1u zf{j6VB?~O2Xi)iVfcHWj0{`E6`@ZyX?1uuw%=s=rLvS0qYcGoRRdB%9Sf87_?`>~t zlx&p-va|+dy|F%fs)3UX8Y%VUv`5RbNuBMj==UG>?pxbrMel!VJ1ctsOOy3#KEXxW z+6o8lwkk|&)mH7R>RzvX_w1L~_~xq*o4EteG%+^l<|OAXd)N*sWp1X!aaZC9xmJ^_ z_MbJ1Y`^yX+mHX*u<-nm?N*(?v1+;CgK%upW#NbmCtRPm!IZjz#bK@S~GWVkgo56S3o~#?Fwl%do-(e9W=jEvJ zllvt9$KUOGNBN>Kzovj^8_wNi+ngyiTCnApX+xj+%R1~qkM2U?d?^WqETDrftxQ=i zd?}K0Ot|lWA9mdY7JTXg&jenb!NI`&nsN$AG6-4p#>21|C3MnNNMoYt?2?@UPb0oN zqYnvqb{zEJ4%}VcUVel9of-9eR045fR$>(eKw|w;xU55;V&J;GIl!oU@JlpqxzjQ#-r>`_mv-1&2_h z2E!9f9jY$);+suUde??zYRX9XN;loU&h}7`%fR+qI0Gux(!gG(mLJ)sXhfD+lMPD1 zoTRx1@j(SILQk+k+IAZOnw(>%jp%He6vX&u-m6gVZf<{k--`jtKW{e~ApXd928h40 zvbed{CK9MC*fThqO`+;yjBScNgoY8jtCYVre$32ueo&yQ#x`i3vwA{y3gn#~eSlDc zhF=_d!M!P91@q#(O~%w{0<1uUXbJDvaR{CUsQwWHxxCqv23aVWTLpOykRS)xVm4A@ zWuTTi4DtE?H0Y7GmqQtX1q@;Q)`6$T0E!-6-bhCGwi&8O$X6gLv3c1w$5&PxqW3Ox zq|*PJBA_XZj8q_1&?cc$QprINz*gA9K1@e>3`*_qbm&@fEZN|pvB^~cfCgxA!n0Fx z-uSGv$5Vn8u{i}-;G1kOoVm%y%mDBpbo%i{sa@TDf6xNIyZrW}-2U&+w%k$vW(AUI zGFRte!IbeOs4TFD#uywQ$DS0=HH53YdzL2o>0){CW}8UBJPkoQ*!{SQTH)RR`Erdc z*H5)u{ApXS;o@kUaKStcVR@rS>_C~O5Ormw&T#%`2TOZu7xS`)=l|TM?sA@nE(X?u zlw>2@xl~`hfqkg>d(+iZ_5VP_ts0)Zcn>s8HI5oXKUgjiJ?F^;Exdrwe1eP5@4@#U zfAcC7u#!F8gAEG6qm*W2D>zk*O*sHsAjs500K0f*z5ZCCs?A7I~=TLlS<`^VS<-aVv~v|r~k*>l{H6sEX%*tdv!kwBYYLcKp^mq`=sdA5XRs@ zHfQ&jzxY#aFpPfCw z+OwP6L;FLDWNTiKP_L+M-uE83M3c>R*_T%$W;pa+-ccpZ}89w z!zukx0FdB00Myt-&8lQ=QY8UcXOBanMy+t8GQu)bbf-Ub+B=f|(7v5q0P82zlMw^E00q6db+C21!2`LmwWjzJzu zMavSHCdk;sHCJOoi1M|ERHtL`*B?vi%L#@dBl=6?Gxac%qL4Te9FxWlLoNUI(UPnC_BK$qKN|d*|V_| z4VHxmyxr@a?LDhtoVLPl2W^+u0kJYzJiy;TG()nVTYa~0gV%d@@s&ELq;6?+!NOr1 zk6J-Mq7F%=NijX`zJK$4PqnCfh68(@0$6GQ?lcmjiF4Vpj1P1S;e2lR{1tKJ0X4z5 zmdB_voX!^C(g?V;MOqIC=oJY>X5AHN`Q_{X{b4WW{GDN7q~45=+Im)$*lJl=PC59N zVxK&?y_@6LdujXJA8cbx8Nf&E+S#ItREA-7s8R@(X3JMzEblEWbl-TPa-Q{sq0}eV4zLcz zqLb@x7!#1|kumaY8PW2~i_3S%n9jPIHn5PeS(OSl#TG&QD2R@&tR})>i|GUM(uUzs zS)r){_6$N}WNXx%0JWCeR#hf1JP#0T$8B4IZndrATs6K`0paSh z&GMVGx9=_c~~Y2M@Z+^PkaY8T;it#Ttt=mO12JL`EqA*J$v1HHLRII=E~PrzNqxzUwMlu$ zV@(5A^x%O0S@KT1wf8{X@YK4WYL2cN?&A+74K8Iwmx!*16rcHdWG6LI(SScpQBA_~y+2Zh)@`JWU2BI&4P9 z*ID4<_hhx^fmU+^ok|YL!!|0AQ?0vOYS5(A#m&^EetB_ub@pn{lyzrRY3-c$1R}=U z)Y&P%dDc%NZAso{^x_q@x(>AK17DnmLOpp;fkRAX$1V!JW>aTPLl(PCl6$J9ZWwMB zw8^`ou#i>C1`u9&3zqpWteJ&58h(CpIpfR+lns8(_2cJXca;s^{M0%G9%87Lzs62G z{GmOo6PMpv;vZk@+MG%?%JWuDMeU3Yjx9cQo?hngvX9@eUtNFv@1f7P*KX8W0IF?L zT?OPX*PJye#MDIFq2~`bZ#0}1M~?Qv87yERhoYq@DhQ~mMMXb!l_X~8)2;2k>_7Y0 z%SY{){p(G8kNnCvzjJfN%C84D1$L}#kC=fJq_okB;r-%{N0dt?!7+os_C#WG)fJ4i z_R**myz2ty+-#Pj{d66!Ua6hX6+qThlI4X8;1g)P=4EHJwmavP?9|3N{or(+eRVBb zHIPh_N2FCOQUxXQ*6n`X_ymI#N--`_FK4I{JT{Mv6te)<#FpkaKp(F+yto~zL#ra! z-ZaCI-+HX*#` zkS%hxotMx)jXN!A04~S(unF5*Toh%f+1zUKHtf8ye6(ASmXV#EOI<$dROck&#mWWVbfr6F3Q!4Afc6^H zrG$b1om|tv?(>TmFV5edzuNa)>dvys>^RQLxN*H~J2uf-+*M?!l;=PnAcCIRQn{e?dpO1xtx1M+tbta#|@N21#x+FfEVry|M zv)hG6PUuSKliXN6^*Tz5wkqd(1vIjJ@iTvY_8l5Ip+{A$*1_tijiuZ^<|pPt4hnEG z4`;Owhh( zFm*9;+XJ~xcaBT{Yj;Z37#?;g=Cd))ms5p+zXcKA%WXp=PvFjApLd@|O-SAWI|~U! ztiwVI5wo^iq4a6oib?q}LU&2ooUL}lA;m;Erh&L;NAMTC69Yj9n-Gf|6kq$PIyh>~ns`^~zQ>h%4gXcv) zLO+OllIDX?I_?yHe2QTj1AI?YJ>G+|BSV@2EXG2!Jc6EUe8XHhg`|LwE8EWLmMfbAQ7A~wg;So$AD?|R zS=a=46zp({59?HNBq!CoHG`hYFJl6aS|XLDge_DI-|ThkFrTb)dpPF4xQV%M&rb$y z*VV#X&REO-N33N*14y`z+*DN2aX9Czuo+e<3x=CwlT}G-`Sjb-2b_x|O>3_O3jtHk zmU)!=lcga9OI~)z+0%K_dqg-h6l{-iBoFdfo9hWE*S9_|9{;{t_hb&KA>Oi;bar0X zzLb@BJG>6Aq;7VK%bb3Q*!`w~3 z)=j033jWChYMfrGtaFBrJNn6 zSi}KUa)=y24cw*Fj-_~bX~Z5L0<9x<0)eT*_SY=nDcKuPG0-oc<|WGX5RN>(hb;_a zoJu~0C}l`DDP#qPShLgHlo{w@|JFbM1EZ)~4%jXZi%`HnG{}boZ>$4cviLApK3GOE z|NBS}bMox%(nB$-+K~&pwiR|t=9TJ(7}#b=zRutJO8M!pBMO(-!?eo_HnXcGc9jrS z5Sty#08GTT1-R%N@8kY9OqXZBobyT0)h}l+-|zKM?m$oBT|`XLL^rk7t}EC{N--7@ zabdo37f*TP@bo?kEAs72B5@ZavO6&kj2M6-&j7l>?G!%V;7*D;F8fG z+ICn|Jm%N+^;OV!wd(k+{_VJm{W}-DvwXjzkf=)16%z!?Z-%QNwcU$MT)@6#sr+et z2%=SGJ+5*o7_1IGGNtfL#)Iy<^W{{+zp3kO6m zO2S|BfjDIl0J6!|>zw!;E*m8n4<3N{Z1~8;?U;|nlT%CyP}MMM){e9&^)5F{3#Ja{l z2Zic5lpQZvDmlgN8uSb5Jh+k1nmKr$HA&6LrepY;6km-1ML@d0Xd9a|y3g++*4HBI z)q3pvtF_uW(vTiCX)C#L?Vo-A> zyQdfzP^ZyKM_vKLH&(26CM$M6vBWylO4h&mLN(P-M{i_mixy;IDm4Y6;x-E4S;hR$y$WpZSIg1U9H$rKbB?YoYHWmR4_8Y0^fY> zQB|#4(AD?tZ6LqiZfgAVudm0F$(&$ObysR0sRFKRD);~-m!8=8aO>ME3X&{9Js%z5 zfILPU8Z#1FaSq&?3Bm+Sfigxr=!!}uh zl`E%UEmUI*CSidVQ?pPbvpa;j0cHWYcXDFU8_MZ8cwr)=QJ7e!>HbITq}pWePb-! zTZjtY#a69nqe_v8Si)MnX)xi3Q9I%^UPRrQf`?OL5k9in8I7>(G8f3i=cI6vDQ*;$8#bOQTXtIsT znYr4^ts|>^eDwTe1!7_G&o)5P!IX6k34an3gP$uPt^BynIY$pijLbMr1mAraaIR2; zh{2fnT;^$S<;DsR9P0G;^5Yf^934lJ_uzf4cye(H z3i7Gq@G5|7^fAphD>s%Vh||<}&^5^KyKg~O*!}U}_V*hc*w|ay4$q9y61x%tcr_cM z)mw^YK7GmUHlK};2e+bGd4T{>VyF~c6&Ed#PaGq7h44L;FVncW9Q~cl3s*w0B@eHg zicy>e9Im74<&-qF>2~~heYG^;KAk7vOG$AEZ0XoZn7e7w1}b?#|B0XR3>2e7)#v#6$YMij5LDmXUW)RX$* z%a`jDt`egQtE&Xe#5Dnzq!33mMTc5c^YbY6af}o0nBH2|PGaS#AXp-OWe0c4V2kBB zGXI+Y_;BpK{^Eg9Pt=6rLtv38vX=(!2VEexuy(pzxJEyG{Pn{p{JmNuN8eljesi^t zyyYmPC=?LvdZ<2lyQZQ-nA_3>Z{^1ypYosM%hw~$L2j0aD9RA&Aq~VO4K+ZT3Kh*% z!n)Z`n|3*}e*)oX??@p0k^K?~e`7g*D$!ao0NbwX=wkqs(6u$s@R0L8!QD-}OMW~~ z4EolE-9E5j9Q#9Iw>TuVn5cU)OsPN`e5W(NdIJ*Pzw7Q{J`VhcR+9tr$RL#|)Mktp zzWO6N-tOc1+qbXZKDmoAn^b)0se z&P69vw;mR4hlqP^Kc5-$EL8#6)m?y;Db*1{tNx$2Yin*J=dpi@_sgSWD~W4t%WEr_ z*%uW_kTa@HY?tHA?Ed>bXv?yl5oNWcZ>pv;xi-)w0UWS_0|!U6$>*)6b?@hXpHxw< zHsuO@9*^PGh)F_MgKa4QQHnN}ir&}nw-0rt(Y}YIcmuCb**IxS8_1qY3mSR2HNtGX z5?{_xKhT;ac>K_SqeCuMXJfgaLKN01X-6BY%?_&x&?E`u{5k+?TV0B3kwi01HsY>| zex=#N{Q@U-UjMh$gJ=>7P>Gz4h>^y?W{_++CcgAa^Yp#$nmpXzA5aZKPUn<{>nPSK zvPqE1UI-Yl&D5V&`&jh$tjC^eh{pat2x}XB2VZHr42)0&l3!E$aPm)1efXFh$asx2 zA`}EhPe^-gkReK+B$rDf2j6JC{Jg_xkzH%aY7uMY$@GSAK~(m9*9?T&A542#E#S>l zla(36QWI2-Q%%5bOcntdM|Vq}v|t{OpWhC(Gr1*%7;Xn^DFNK-;Hs1*5FCiMVIznA z;^wV&*|N|1?|GZw%xkCD`OEu@$u`~2tEUX{GqsHP9hrgJ!xLqN#MU5n02E3TI|ONi zsF6f7uf!VVvtVAU%6&00dMogs1=i6{10p2y zi9ZYHAY;e~69TYmgYel$W(U~s-3d;J3Avkesg;;4w_w#$k~dC7x9Q!N;`e#^GBhd} ziq!$#7hAmYm1I@`)ycF?+s?$Rd%d|zJT!%6^2n=HU3KJvQUZkn>7DZbK{k^?>?J!1 zZxYw8$m9`o4Yw7^=2hU~uuXPI&sMVcS#nzn5?eSZiJfQi7p8%6aGc5)Ra7T345Cjj z-cR>??@)Ziv36MS$bA;xDkfh~N|}&#>$t3E;rqO}L+)6((p(XL6_c@%i&1dOfLM__ z#DRlsob$8!`Q&_hH7X+ia@>5i!jQvh_ZcY~`y3>V3NTcpv}wz)CMQ$&G{>V@DP9{| zi2|p~MgzynEa}o>6%^cC#Rm9IyN;lI_4{5cPdsNvA`nNxOmvBq)-mSfd0JnkO_nzK zdVF#=itpu>WM8sL!st>78G9yxF!)tq{ecm$b*M!jh0ZS~pC@B(n|?X!M(6Bc+m^p_ zV#6bsMj3~+FhZryiZ4Zfy7&#?U=)G*>$C1%E+)#T6wVw_pK8`>9uejfk?i=@rSPM( zFGs)cZsXitT!|~;xTc;f?<=>j29dlIzHW*rXH&$J6Snl-mt8xRq&CbN-z;-M`;pX@P|PR=jqAHS@Zz5@{*lO(j; zH*Uk1e{0-@`0^hOW=-%X0(m1Gd6CHLRZv0;k$I?dbgM)5s*B;i%oh*!`<(x1=PU7N zCEnHo=q{I_Lw3=o9Fd<;W1eUyiwlVO;ia0d>+Y|PPiOlS-j7lfzpxdFVJ6Ztm(0ai zBwT{NMbxyRg<(+he0uU}=h~G~iUKP(I8{;sK}6wy#k3@)iXz+QHw-p?RCFYX*3N2j zbuiQJ#F}y@WWI>=2zRD5RehHIZnH-* z{TpgdzFzM7L1dJgO8c6Wb?|In3TdLXk+LLUW+ZDhy}6pdeLnhE6ionlUNs*yz^sXg zzp8SV9Cyv99Qv)AkBVMRKKx6Hs%WdzJcTPpo=;a{@XadbV=S^f&nf6BdUf_MDXN9{ zBJ(|A5g{Q#<008d*DON26sg}?`n2`4e@W5Oj7P-HN=wC_lfMKcYo+s&V)SpF(X;9C zArwuzc%ArWl{mHV0~=kaz$C$l)SeyrFD*4cZhzjF`WPJV9(MgU^g<71tkzB<;i^a} zeRjEd-;n+x*FuJEI(p1sDuN&G)5Glnv_aHmj&DG~YH-|<1Q7R6D9$}y3cXpa{~2Wf z#+x^q)d>`9k&Z9S2u7*6U7^Ye zC~~+}(J2H@Rb#?$k)DQ-^s!@y3suDfs*+KgJ@@o2d z{&{kBeY$p#+t%d4cioXA*Oyz399ceM_>c_ySH4Qi1m7V6<=IqXWL_$OeAUVCSR2K*JR0;;+nW-J)7pM z*IdO6m?&G+otq?X#hhl|9^G>RlAG*yWMw@#u~?H)Z~3 z_v9RZ-1g`UPg%xWp6^jCe3O-k`88*RS=)pa^rD>f*H?n5u``Zd$wg)l9F26og&<`# zwGw{T2=KoIRXZPn2f6QYU`G-#zYyH2t%hRI2nt+or?REMR|_g+-$ajunFXKDCg-~g zCfN!pAa6$!IjuG!;Kx=aFcyCe-yrzx^kVHBkG}@PzkTBh?0X&AP=LSic-7ZZFwCV- zMsW#=m#rc(l6Xz{g^dMIcpN|FG4k%{;g@PKCny1VWbT-kIhdeHtE@>>#JN6C{NiS) zp?663;Pz;82^=VHo^a5_{JnetIY%2P*&C>BS);R3CIsX-llFl2j2Asx!Zmmqfff% zs;r1I8Od~01u!(g<2t6T!DjT?a#t{VLX5%0+%ivGHl%86 z@ZV&}={~3QkAo}1V+Wm$rBp4sHl}cwXHY860j*dxh0K`R>POukE6itHVzNrS!rMtn? z52KEhfe?XPh}vtRqRVg}7XR^RPtx5R!j(G1%q`~RVM>oZm9aV$Nvf3~3al4U(Rq!C z1B@<&tZ%Q);!tmH%gc)0~B^$8^6J-pK-ctv{HdK1#iHt%6kV(GH7gQ>R z(B9JF+if|B8bziNLqNV5BSk5Z z4Jlia_Xy=0Mw&RiWisox(&@u^Dj|VnR1`cs3%FOcre)BP>mVEzvy~QnH?=!O<;pa2 z$s%yiR^tK0GB%5p5sRa-OQPRP_p2s*U$q3@lwv61RALQ0PzAz=)J#?($Zw>|EH^Z- z-_p&lSD!2BDJ-%@S-6iO+2*wzCWpD03qnZjZp)81blCb^Nu2{kN-fiat)XwoI$nL8=K!)gQLq)ZFTG9 z3;xzwn-j>Z3=&S+s*AO9^BG@sZ>b&Dh?9s{R-CuDrr{N3M|8!pN^yK*CZ`-`t ziw6y!G8$u3#r}~rykX2pic@5x^`h9$m()jT(@{qAi(uG=wm@&h{9=ZAM?H_(GS*w3 zP1!M+N5Mx|quMThkxaq(N>sT$(c>*`ppq2ARmctzY@_7gCZoD+evw?gDp+RAq=XU~ zw+2?g8fW}AbLDgn(4*w@qp!Q4o;JV5f&Px4aZdoucI_E@KfOAiPcJ7ITO6Z*JaH)N zlSq*o&nfe8I?)zU1m!55VP;6(>2n>#o}ZkKS=hDWQ_K~9$Wuy&s>mU!7Nc!kY)kr? z!)%TC+0nbb#rtZt;5t1-qF_znu3}QgH$dZ(M5p{?5Pvnfo=vYteZ*g@0#Y&KA(q)$ zs?l7>Sm2A3f}v?IJ0J5O_x)lv9Tf!DDj;yIo98=}z*AJJuugc(_o0@!nQhYvKp@y( zzE_XaG#XD5XfySaHI-=zJ(c2Ldw!o6hg_DW3UcDd#Q=fULbjz{p79t{xzZV0Z= zORoEfj{RXV(9a-c-lWF~lo*y{etr7ihgcIW5fdUzpoFSJ!OHMtK>UUV+A8@?gP5NI zMy{(5+?pBTuE&aSxg96M-4mMyFfe}_=9yGg!Gx-4rRaoQOIq9UZxC`ec|Wp)@$RWf zw;+?Y$_m8Nz@vp-)7dB_(D;?^@eo|jM~1ZLS4>ffK(eajn3T;5WYB`NYe?u|U0s1! zryoA<_Kr;ZmMx%R6D9=5QxkmUFCisoO1=YLAoAUGHk+RBB-0~Qlg|KcT6w3b5g?n5 zA{xA9A(%lxR#Jb(~<|N^R=S#1jGrgOP$8X` zJ@XiZU(b$apRV7n?b+J;-mJfk*W2ylkLP!BR**_OZZ&6}i)qLXlya8GT1MkLD2U}8 zPcMJ(7QSvHT2hG~Fx=$mVDvg=mK&7ke*GFrKBc@$Sz%SDyb48IkJdBom7G4PQG4FuhtHl-9|1FbB9>Yt?!xb$tr||(qeJ^ zDMxlJo8_LK?<1G0smRD=fjf4(A;N2(j_kM|5st2N;l*+*=_#r&RE}!Z%PA&l-aZMB?C~Lezd_u!2y}MVf5IP+~ND5vtCaTNWFlgK+1w)%4zG*q}`O zluH9YeyfjV5~WSV2wsOkeRnhvr?oWPr_$(9(S z>tZ(5f|L&h;=D(nbK&60rHpOZZFaY*xIBvNU>VVAr4nemLZ`jOs!CDpWxsunyG>*= z88??eWyBIy`qe($8f3vgTw6s?F-H1(J)3t4k$wN3B)m>^&fYs&HVaicF`5>-osUnXC`xkcL#T}%+oETP>O-XpnAt6tnuQu z@9o~cnWFO~=;@f~Nka0po`%EjnzW5}oX_0Or*lRN&8v)vFcFg8X+?-9I9us0T}B(3 z+q31(?4~O?v%iw9jk*}mBa<8i%ER+k4{WUJYV?SqI=g+}75Lg;$JH_xq%JbSMC8CV z_%YMoz%N;*N8Ihj<$7^7o4lfe^{HwB1wvOmvoO9!c);KxenM=7X$bf7c74@V)!@J= zQ^`P|B4{}lQU$Gm7@@rVr34dO5#qr-uDVVv_m`6vCAtU*dW9u0qtZr74&+6wnT^cr zhuM0vfs)vkE>)F5)SNIiGT11ojg$#&ZfrGyN6jV^8Zf6^In)4T9YmjqTH*KVgja=9 z3XSdAUw&OKK1{Yyw03G`t`e1tXop7Mi^=dotc+@ zp(?rb6uegGG8{u9K_-o=u{bxjk*;oEA-@!ujh0x&0nz|2kkTW=^T#3`R$dy>BsZ6@ zbPKfzCppCtu(Rk*g@hu?DQgb-$?S-!1oY+k`ORW7w29I>frU($U6X*ug>{xTP+=BK z_K5$TzdyU0t){QBBItl0Gt3;Pj7>TVU6Xf)GZ+1+EEQ0oHCt>_K2 zrP4Cmmy!KM&VMSZ)dEz<3QMOlaK@C-L(?5C?;KSB*lrrg^Yr8hn>)0E?n^c9w|!Wi zop!x#_m#orVbUa&E#sxn2k8?hxkzFl-OYRKJ@=BOrn( zP*Q7!2NZCMEtfk0cSku7fMakL3p6>AsSdP&9-w42nL%oT*|cu-5%7dS=K*w*g3dOw z#K;aej)qK5l6$VPUrj@jY@s`y-T|&J-fAQ0A)jVC6&HYO4Ta7X@WJMSi(^lvceONt?TZf=K(Oi z_s-c!W$89aqEe*FfyzQ;I~IH+oIL>d#yt;kHAXXe>|DztVL}J8>M2*3kKVLW^9;BX zLZ63o%)JFqb}7{octQgr1a_@4*ur=#Bm4l}9sN82cDZPXU7#{6JiM=uV0VFzfLS$Z zY?8YJpa;M_2dr#|e&TmtU2!$Kl<7XZS5c4kxHkrRfD6ZNVWAyGG;9Q^FvY7Xilbm(>?vZ>W&xUJ-kHQ;n(&=Vq|hZb81unP}<110QXMp&&< zupFy{MYChm+#L)(04@>Ef_X=oMx%)ZcC2Eo9o=M*ypBzCH;X&~rlg*r$5g`?qpdA$ zHe>@BGq?$;$rx~VH1u$RS?!W`J|-${$qFT}q{A9IKH{)rqvpEB=}t79(iAR$HEif= zouM?%Sw*^!En6|2Eg|R)iXK{#6g*Znkg=@bEU1=qP}VuRdTPyBpC`mc51}QMCx$Mm z-ZPl$;yFEW*OZk)$J7voTh7@V8a;H6w6KHYK2b77^wn`$lucSV8}`cB9VbD(GWc+9 zE3RUqTRN1!O#%>GNz+k-4aTU38Xce~h@`{ew#8_Jy~G_>x75l*BL&z^>6wkJbbq7=o0=Ykao zu(Giwwmh3rt(e!0H##Y`i1f}wN|&SM0>F@o1vYey3xOBB?)_8P&=tD^z4~qQvH1>a zwmDn8xqW-;Tc{@=9i%s1OyngpLs$1;v8+lqYAOU@M3k-N{D^$kZ?BKGBSjDo3YS8T z8^|~nZLY~sreD}{*W!PV?bwExenLYkORyz5qd`(@Dp~4iXD9rfle)>g9~*MMT3-K* zc7)*>t98j&jtv)`bjGJDrROm1tq9PMb}Y}jPDNVFBh?&}%(+AkmZXF;@GdEUxhlH( z{!%MeXI-(k=A2YUS^7ZBJrCSi$X=5J6hgE_C|_#D)#~zN>Oqc=$0RNY{mTY;(Xcts_;ld zF!&y3!%)4tnuQCHk614jK0;a6I)2`3$h*b-7ue{Zu|z~-mJSbNmK448Dg^CBp-gJF zdGzw6DHGmW9%+gXFj&${y2=8rlT`Bb5M)Rl4EEc&B(-N!7w=~mZ@Y@fG_7h_-d zSF1POisbn0xKtBo+$IhgoH)iLda^38Y+&p!ZsFOAglPB6*kcx6QpI0cHCgmXDS2yA z;Ll3K6>|Ner&%wLag1(b0+SvBC}mD889KR4bi@Ff8B9^FqtYL(0Kr?H|BT_#SO_eW zIL)PC_arAl@q-K*BrKU18#4U+ZQogDYi2-qRibo|#rZ-`h9#zza|~SDweCRdt&yv% z`Edr=y@9O`R`|?Bg_?(fC55Fx#i1NM$whCpG#rmC8xW!hLO*}-{G@Q4Z) zPQK`KX7l;t=4Sb3xmvD&#%hTH?#39nJXR|``t_OW%V_MM0;2Tdz%1zj?8Q%Ej8G%6 zX;xKwTEvCWaEsa}=`B=oYDPPL%+d1w)oO9<#d|kfqg-Ku3OiP8B*mlBLYLqf)^JHD ztlL(_!IR(Pw9>?%mL&d1zI4f)=njl@l>wGfQdP)|0uHb<%Oz=1xU$((%>)OAF5&PP6(PwDPd{{XdDiv*?h#UI7=beM zs`{9{$#gB!*#ep)q;BvWK;Ev;-6TXVq_b)}p|$|s`9x`Xg3%FjprPM^_~v$rMbs<~ z@iH<%xpqU_i2%xTkx3ti6oh zF4pr`6i=ZBq|N>2Q`5f9FIT6UxZQ`gQDYc27(1IKPuvtOls52bmL8BX(X={*cD0)I zkK!ISC2e5v>op1+vXuoJvRYQD@Vn-%@YW7{w(h!{_XOHeeYgSvqBP}h0Qa*&x)`~F za!ajK#x3-_K3Dk!xk4!BY(cdPFI)uJKn{Kv5Jy2dKDmd zv1(Y#LW;)|hrhy?J4!qK9cl6YYW?p$xRXY457!9+@5Ho+R*{0PH(H8!l*j`;I8ZIz zoAvB^{R&*=jiAb)R7WA$Sha+4w@@We`t*FT+24z^*PsSX_jsUxI6XhE*sRzm9A3;R z(A*Z8yV<;Um^IX3gcS5ngH(H}Z2~6b=}es7RJ4TH&E~b^tU0zwD3r!ABrK=susc{0 zAVl;|zZHwV*}QI`z44Z=cxaVHurh0-G2x(q^pXTBunD`nJiA%^y13|KV*6{_s+?3>)YhCS zMJw!U0aXA&X+#BgfNw5WU57=_(G^c6!KtKdz=)twV-*qe%7d(-B~*9l%k^xv>^AZT z_yKybmI@DQy>g7f;Ipbs3|S`|WkZ+3M`=kFSQ)gG#I9yg zG);muz|#e>U%KLb&+s8zG(4PCCiCdIsDYvbPHXUfrCVv=cKC0vXUAd#Cobx*n}6`D zl}2Fw+xnzD0*_xAREq;ci-K-}>EI&gnkNcLX(IwA4eH6R@YOZEU11`h32)TCZn)mxK<`sp`4I)KyU>>>(#&=ry>Cp}V=M zF_Qy2MMy6%R$XzP!=4w)>Lr;7j2S}?h+>j(wzAT%#zG5n?W=X^hj$dV*?nbr!jvi%(*fHRMuBh^!Goz(ybspawn|YBQ2$zQW*=rp zvnHL`pU-GTt2gh@PwLF>6T1+-D~x%{kIyZrPn9f5Lzg+A&s2lA6VCapFAw7$=#VP_ z%fVKv`y+CK!u7%x!?jSRb)}O)$w^EE^^TG=$q0`Oij|-ca^OKF&77pSLT%6w*B}}b zQDKlR##f-L&O~}uZ?!7iaBR3-ThVnwy}0}^I~uD!ah-g_di`HC<3sWJ*aBb55L>)^*7(+ECMW);E|>;1xC*r)stk@SIkwG*ru5 zQi?qvRU-(66k&3SPz@rDrvZ{&lcrf0s$oK8fp^|DC{&Mpv#L`o8;(0rmP?Q7?Ubf;_EcjEU z|8&f!u;-}8+wt)G{guQvpOQVXC3mT-yzKL47!R69_wz)ix)Ua66*QGY zko0&pHmjmMnVYYntKR+dc3`_ZnX=CChzT}97DG2!;UjHgKJ(Msk$x}f|0s_6x>6%Q@LqoOAI#nNX56Swz*ln=tx_-s?a`fiBcE`?PZXZ(a_P2g-|da1{Mp;_Qaj@ z*WJTiy!&)_v|h{U*6QPn*6QPPYjyhPAJSjHzwrIxM>@>m-Y2&YwS1`3Yq&=Zj2yJp5 z**L3Vr!j4y>JDQ?m)b|$bDzsk9ULXJ0%LdHai$FH0E`2V2|V7wQkn6!v)7n!P@)@b z&Yp@z$L6YLD(Yq`XWCQdSE-?8p`+nfb!k^*eA*n!sQEB>kOtiGkH2?REkkZ zZbaoVQtYY1-3u8(RQ=w;`f1GypYA^X@wu9MJUQszZPV^frliml41pciCS3d`u^ zsoOT{OUQTsyskk;GOBxPqM%bipXt?PmRi!wFBpZ+<*I$j_tG4?e6`b z<&VuKe!Byu`QrrxJpAnUAMG&2$1sLv3^(QjW$55hX5(eFlHRN+#x@Yav76Tb@~mrDJjmEPp6jO^ zbUXnbtERzr_J+p5={5j%qL43D&JeODGi9hg&Tmhu6$xC4Xrn)#~q%t?fu!rw- ze_xMCKG5VHcu2YVtXR{^UOTO`LwDfeXCa-El<MI;0CZ^dtoY`8oqvTgSXy#f)-4 za!t_$-}E%_xEax3(yq=f4m>7deRJ)l;#cpgnkc3zZ(0?dQ@v;5;H=8k=Ziz{H#n_0 zH2J8Ck$^o{b2Q90s@WO&ez9`rpsr5;I`D9`sW8+PdOp5o##* zqCmVcWpdm;@BHEn@$1I}52@G8eiQ&6O=5>7Vp2{~+vJ$2-1M~J+Touc+8n9E4i!zc z5=c8QLUMCP8Of%S?EV$I`hzUmhc=KQRZq_MI;0K6`iZ?Ad_b%L^F1d`-ahY|dj9Z! z7Qzvbv361d*(!NXQ=D>b*mlppzbH)(J09DWu47m>oL5znkSNM_4akIrlWBN(<7Z3z z^RVMF2D8(#Tp0!timk1KzYQ(NZd#N5D*a!`IkcS~1Kwk}kcz4)L!(UbBwQYBr~aKx z>26$ffgcaNUe-zI%BG%*hr^Ep0VBfLsMX*R`#revOGVgxU|gg_2o_M0>@LG0JQM-i zPm!A}+_UTD)8|7QMS+_D2yxX`p6&s9beL;qf@d1;{VN9K__-b0F2b$rB8|o4DjU1xFHz|}07b=JB4;Q9~wZ~y%9{J>^0 zz&1;k=9~!%_%*m#VsIMHU)8#QiawoP$$8nM)(;PxuQLMfiI;D_v>;Nx_1J5019+Z< zwx8uE?BA$js1ds;SM8_od-I^vUM@c@J=OVX5DuY&#lCRtuMkYmO^Mki{#TuLQsX9r z01AEV2zu<=z}hR(;)8?x8O;DRWK)WU*}Z23h^Q`>u$A9Ah6vciLr7rn7>rd2Ikl}O z;m_P$x*&yO8KwNUsGn9%LxFvg1O27A&ufmzS@WX-^6{rRO_Y`x#37sSg@m;mou z+`^LP*s$1pfI3^;J*i3u8A8R}t6qSsu}iGZrn29ib}DW6$`rGG(}~X?AH6XU=KEax zh%yY*-dYwHyup-2ku-H7J44vi|82gMzdk=&H%f4U$t*dA<~0EA5VF~jW)_G|O(U}% zBlXxJ1lI@4$6&LJ89eR49KcvqAdZ&jH)mhfQ$9R;MTBMOFp|8q2(2VH%!q8QWTezM z55B(b5V-Znj$uTaXP;m#w;kFd92H9?y{e#2gKKAJ6Mz4MAnXQ%3QbouA|%hOLEo}FEM+_W!C5;cApL9>=;A9e&Y z5|K9Z^GTWO07pbk!N~0qJZ1nP_Y!+1)tqPj3`}r#!t>bxv_BodYhaJZzdlKMqp~XI z#QwCY#Q+^|Fnuuo+E3ljcj=GG`MKQuxv870DV5sM$;)5fJ%~;1*0ZiPD<@ZbHN!h- z6swbR4J9OtFjq}efUX7pYgv24pM=YROwCsJTAR_?5iLt97I*_uc9>ML+z5a5`cbk+ z2V(8Rv%Z9tV5DCzPVjF4*ml;0{zmqbqgPLs6%PF*-*}#FzB*2CqxuBv0W8;9?a)YUn zEeKR}Lp)SA`BZrW$HT5VYmmN``(kMs&n>|&s}fzdofL0-x6z_Z^dF`$8b+Fk)_eOW4 zVr5ZxnO5*`#Xfzpp6Zp58LsZ^)8##gFpYS*Vr%p!0j9Ap5w>xX zO!zj#Oc(Ou^zuqBuAjIt9{6b#ZS(-J428zrj^x~&9>ON( zVef40{iE2N_Pa809!NBNb+J$F`0)c3?)#Irn@MbEike~Zx6OwA*{m;xu2^|JonXAy z=JFgSU`aCJZPBGvkJ zpT68(Q;82AP)~_(U87U45=)Rm&^lWZ48yar`l+#n;WtO~V;72}%XWHtvQ5!>@<&;e zZu?cVH;T@TnWKuGk4q=rF+xqp)YAE>g%pDYSRrsoP4GB2A~bJNK{s^@t+S<5W+KqR z1sX&wJ*%jA(glRXQU;dr>n55_@AjyqktJ!U-`=owA0OWKVvT=~$+b#q{1sa|0+`>wrK5_QnY|!PL55ovnJuEoX5IMj|;htue4Q+1A#1 z#)^NMzvI~&aVapMu&DyiYp867ssfHFs{)K`njRS9_10{P%<9U>deP>Hr23XJBC#lg z{I!wYW?%~n;0K#T&DE4bSIsIy-)^z#N7Y>7QY_ZN?K*@iW$NoGP@5%<>Ap$~nJqA#gOmbgfRAWU34pt(ir!O$|LOC`v-7o1 zqmvK5de%HQr4WVoW~J5uq@in`%EbP!tJcvr<2suS3>6YtPI}WxtC$6bV3?`4QsAm} zRAE4xu-9HEv2-GXYaO0A6&PR&kI&)!ubA4fq}uNLQdauh+I0kY>ON%lr4s9*Q{uyP zdt7+6xjNgF%O#fu-~2I@GsEtv7j7Z}U7LNTX25ULzE72CTZrZ&H0J z7G1;yXBDf1Q<)WbAN|gAnqS^OTQj*8dqP8xgog;84Q5Jr4x2SZJ7jWtfE~4Fe*|{> zYdq@939&TnYfJzl174W0}J52&q6%cI5)_~fA+Bg4zj^T>%tS&X$rL4FY3 znTmukhB0tN0e*p?C0#H8>AsN^i1r+4~ONU_@USn8_P;n?%rvCS4 z2$$0#JbcMEn=ji+k?GRie1887?>=qoa#b^(&C}r$4D4ujxwK|-h@-ZqrdzO_pUbDd z>34aHhsv%@J2I~N0pyl~SQvDNeW|z!gG^!gjqJ1f??(s+U@RHVH$%E%?!FoUd(i8y zVGqi^FTNxb^heps2{wJ*rVG#rG0Dd$Nnt?AZJYUzaJxeG=Y8r(P6fe$=KGKcQnExypZl&@I_}JKgtF?AJz{mX3Mpu z6C!fkT{CsS-?V8Y{q?<dL`%AJ+kZPB}s-q3h#wz zb7-frtFz59u)(Rbsdq(&%6lgrk6LZK{&V{2Px;^7zTxJx`)$_E7q2e~ybsNG8?9wE zJN_DrAI`_h9zwHLu`ieBnd}H{LDf27BSVCr)`Ix2WNTFpwn6EjQ*;2obD3|C-@Sjkq+8OC&`M&xlzo63hxwD9 z^9bIuxE==4&)Pb_+?+gr@oY&F!mir-=t&z6H(MiHo*-yZ02WRe-{6Vnll%JBySJ}b z>TSu*!N`VCU9&K-@NT(7k(y^8U_F=m+wrUSYxOoN=xY^=*(}b9WC3ooJZTGF&*bLa(k!;o>7U-d4Ka%9Z_^uJWc{`kqW5%5Ia&vrwS6w-~rP>IO ziEul>b2!6po+Q|XzpllMJxcWY>G6~2N3Z^|Qg9%*T}j$l+M`yp&p-)10Ye}*o)V8@ zpT1ef*}Nk&0v}lG*p)~e>vJq#Rs~&)_RUPGe4)N?U%Xyv1Oa_rfKh@~@|bC#GJ!mG#NC0T1VuHrm(7q~y6GE#fNPk(hXs*h436EL@u; zGa^hO-nCiPvY8Ma;~*imNP}vstLlsCf))LuTF;rrVHQbSm=KA7&~k*^Ot=3`kyOZHqU~!?!bE_CUDhSLG+|6HmUVH)p9?Z zf^B2PmEeRd>cgIJ&%KwD?~;LsZ{h&sSEGx=QeF{IMe9BiJvI(l>R{PoJ09V{(!vDDCF z_kLtsflmeikwk{1Q?V~zog6Qs_}vu>Gbh>v(VoR`zSEgjT}kFw)gEB|6soj(f|DOU zNsPXdE4|qS!5%+41)ww~^nmp{{&l5g@TN3&1a_83Iv#q|nCc5GHlVz-s}x$le=g!Q z+O_dc=@tu{+G;JM@mAhw30|COS>_J=?P`%V$^f*Cx=~x(h_JU^iB4*3_9ft!DjjO_;}@Fwirpy&8~W^lu0n{uD~|wj+Lx> zu0OBd|MuqS>5{9s-Sq^p^qqZVv3|}n#?Fl;L%sx8_BfYosY}W#c1pa~qR@&gMw>T54SD zWO{_1aKi=1b``p2GYSGm*a=DbK5bfZU9lq-<_LbXj|#umf`duyS$gqJ3k=gN|NfiQ zyfS10KA zg1I^!JzW{XI6qqIDikd4&||P%Q5xIo0fMVGKiW7seslEp=-t}*5I8}GXS!FDtb?yP zFdceSAQuPkH<$bVmv?WE*23SG6_Y)3RishsS(RSlZDKRDR(m2gMEx&*TYIXK(f}s( z(Bc>kJY4qxiHyBdkSM{@H9EF!+qP}nwr$UzJvR2(wr$(CZS&6eUqqaj`<;ibm+I)~ zm#kcwYt>39>k8)LooVv7v&bLsI~P(OV2j?M@>!IO_$CAuBqRuF!_3_VNPg8Mgs~}pogurKDMBPu)Vp>(xZ94LD0F@ z$>d^fzP!mu<{+vaNAiMs1a3x+i*=Py0NGqN?;fmM3bv~zW8tAw_ zJo(ooFCksY7DKJMDf4{OonQNG9Kf>!?^8F?+^Xexz|PI zzum77bEBFb(h!tn6%-^nSEbcEYSOQ6pba;l2s4J#avSSX~G?2WhMVG%ePsrFHa?Fqs+3C*$Wz82mWSg5F5K( zxM1Z-n|73Eg1A6%7zhAP0WPn~*6uC%EqLX>5#X+Q@5QqsY)WQs320d}&fcyP=9OZFkzT+PEZGsk1(>~_R_5*C zNT97&wDh^d(h+9hn80>Ga^lj=qt2Z&2x(zNqu`E_PzW$l17}MQ%1+M7k$2!UMAJ>L zhdu|jp-I$ISivoq+Ze?Rs)dzJWu%3*oH0cw+AHcbA6XoYk{yR^7OSyAnQ{{X2ud=? zoF^6ehJ{GEJ$YA*{R&nzAEVv*%?PbYMU2(N5HK)b=ZItoK&#< zro9LR^v21KSNCu#5?Rg%Yl66}s)$Rrdqs1bV;;AzEIW|;I+ia7VbTHc^pId3$V}0u zWcR%??SDmxEg4J(7CFPMDni3tK$^H{gTZ?2xhKt4LnCL$*WX9%FYYV$+djVQodan0 z9oYLx#S6%{R%S+DZpeVTr}6<8`REy z-UIcvKUkGf2jQ96K~S{;QIz=Knu+_MoGupf^s}e=b#mP8TcNcNU!r^z1k&SxE%wxX z)l35~-i`T1_;)&ac6?HDwv9=FzcgjTr=lQe7m5ulE~yAu^4*U9eNen-+{kC7B1^7b zWS2nYVh+6T2U~&XARZowXRBY#vajTmGr6+OtY2nrUIV5KW6Ip zaL*+;J+N7L!GoPJ+y7BrMNI?W3e;kfAAKZ0xLIMJHRxIkimn<}5)-(#SysA3%w%?x zo!QB<+u7dQ+_Bw3H)d_=OSa1rO-~{~AV%E`$s(RC@0;w2e}M)BFf~xesEx!uC!bmj z#$OK#56BEY=<_GKS=MZnY}qmsVlohDP@#EJ<3b@<5@Wou?Cy9wUwSH1I>|P#%NSiI zLUv9cofWH8h!cE4)!Ustw#sVY3-$Ny_I$~_WfoEm4gpmc7(rt5sx<(k3pi|W`wGAP zm?cskj>5(Eo}7y|4z5%am=Z#9v-PpsY+2#wE2*u8jt14Hbp4e)Xqgfhoid+7YRq~4 z?%DTv-?3_(R>(FZ7?&2ODta9y8UNlj*Oi+^D{8DuDT0sZ_kA^2>47L-F*N7)0n`IR zX+Bd5C}%AiaqAW4{{mmB(Z9iqS=kLF7B=dOaAs+_=W{p@TQ7i$_9wQFkcib}?K|2J zq{!z|lGa-?nhVSy;|foBwU2QVDx^ZG=fIoaKzOx4W>KX1!+rTK5XY#$)4esR9V{x# z46Kk0JDL$cj^?3Aa!2_6E)!s=pLu=FCarNtsB4!EzKpBGKcwa<%%xNb=41`K8|D3S z-m!I8E;Dh`nNGz`+=}8NMj2l$t)$kqipTdb^|oZ0h{lyaGI}Yex@O9Aq~=4jFH@}e zoSw^Yd(35rCxZW)hlRYH{8YH>Mx>i!BBPDZ$asA0_!m97dJ1(Zg@DmZnrN4p(JZ%^ zKc>x-Ab~)sNEQw6SZ~PyS?~;i(TWkT?Tz91n9*j+K#Q#ozCncx^dFlI0PMo-E7jfa zhM*0Kz--ay(0hz;2KAaeQk?ZzSq)-5qq{VQ*^pD6q7`Pb37;n<8?^T5;pzOHk5)sQ z)0*(oU$2S$Br@1Yx5I=n)I1*3k$GWN|SCI z_0q)8LHExq!sB@=d{1;m1pfQ$UIZNs{)Q^e-uc(f)e8T+aKp<_$j^Sc|La3d&dAMc z)7?qx&j+@GG%yGX00004z1AtGrfnc4ZXROy{m)1zP_`*slJQ8?eA)*@2am)_q(U60to=z6;!7FKaZ;iGyovT z>F+21&vKM1ZNI^RFmm&T0s?>WjL?-+c9ps3;{el^nv9p9O|$P%~zw3#UDw zLIp6CPRki}NC5@I_h3KF^PcAxl`Qb+{9x*6q+)}p0iP#3IsIZ-LZAR`FrfTTrK;JC zJbCCGMXvF>G0}t8aSU{?w;;4FTP#mqln{e6=CAZt0SZ}dlL^9%FmyPPkxKP9@~wR4Hf|hw;~+D2x)W$NM(Z2W8O-@! zR^pZxLbGND2L8c%Hm$Yi)dQuBUyPlgfMP0a!q&Uej94pNM60J}UJDnxgLm9fIGOFM z+rmaSly91L<7q-Fb%j!d;fVm{B#yl7VC0e%vq`vpzcG6bYL@9gBeNWzO#^BCz&*s; zSQ;Yr9N+>g;VR%c6qpxtiq*xanwU3dKZPb6^W%KjuP#pa2|9`hku_T+m*6#7VRSwi ze-et)&j8ndpH^@_@bm;d+QuJ-Q^xWbi}>R0fY!xjK1aVh2?;5i7l9+8sW23-Etmv^ zG1xyForX682JNi^897ejD3Q) zb6CRma~GY%?IWAS1P-Qn3O_ITWS0(eqRj%Ol_rNCUcW71$49)@Q6Dk5sM~$;7yYOnx;FI5H_PVLd1Ao-f*v@}Ov&dxuITaWHz!g3K z0NsBP&CV`P`ub+BcE-;CTO4m;$~v!eJZ9_X-Q`(w(>4==B?4tlH~-uEX9F+G-M}u^ zY%)8T(wTtM|NZWp0K>+TM?xZ-0j=RBfAb;ihu76Z=j0+l2kUoVlyxz{PjG|N3=kAu{;nB*q!Epp&IX&Gvk(m0Hr>qd~(JsV6@AZwEGL z_UGj0A<{(-`ZL8*n4$;|=u?@F?;xlgsbsNlMl_-+zF%5YF(M*yx`}98i-Z1hFw2nq zkxU#ST8$&b>~DM>dtyFSiE`1AOS&qLDqg#Z4`{8qRtZOKHik|l*FF0)Zn+s(Ng{Iq zcHSbThG38)z?vWq+CksIeHv0dxsr9^A{4L7WYvU*#f(ZpuS52c zjmOGSYOf}vvR(D?rjY-&ztjLbe^(mWI6 z>Zuw?A!z!#LD2?1%s$4g@CWzCYzCcQ8YBsG zQ3}aYYIbiEJAlM0#wp76fKB&ud~TB5D=bC{;x)qnmtY!o8E*Y10{I2;Gf2QM zj7IGxUgJ^CmQM&;?yr%~-5%4{Kwo#PE?p+gv!o!M;E#ANr6T}~*=6#c<`Sp32 z>!sj1$yW12com}`s~Xy|X7|{Qe7ED{&*w4XiTPp~Fb#gik!bQ0k}IYZF@C~#5c=Wa zt}l*uJRWCVtss7!MZfHQETL=trG^>?1ldSx_Esko4Rpwo?D3mvZByUuk;hSp&Q^zn z3Lc>mWW;-RfQSF1xp@Ukc^2l#5cP{!x>`42=gFxA|J?V5TF9Cd;FL+L>0N&H&mt9m z3R+qrZ#M`umEKS(A1x%8K`TJf9>*nJ7lHe1w3`TMuJh9}lL=}gL&EZw@XTh`QIF@y zPe0=`o68#^nPAHG_vNnO&VLh-I&R8?QId64X3PdVxk)bZNqRfaHZ#wYZg16J)!koq zW4b9zedT-BG56s&DSYB#a$1;^FilUGFpcd=q{CWuLQlNz?ZC_Z;gxXQgnds_ z@?ib@juDhp%mK4cBav+{@E#a)C@VDPf%$RW&hkC+`2X2zC#ypIy79-EB9vH%IHgS&% zoJvQUfQQM8Bw=5=l%qE^QWj>^!^2z&B6!~J+j3Fa%_$Z0-;Rxi=gfLTb4Z0vG_GEE z`PHsgwh;YyRfyk-ANQ$y`{+}9tYK%NgTgqAbE^tghz?kw<5&xyQ;8wfjx+9fb`Q~u-d2WsK=%y+| z8$7C1WWC(w61%7}Ws<{BMIItjJtjq4K;Nbs#=~AK(~G>6UyTnBW132!nH8cApmvsh}w()65Dwdm~FS2HYVVVZ$%Ey7yN%_q!S)zy%JCWfNE9% z0H*&UBe`1Y>$@5n{+C(lDnr`-fDIww<_$FnZpv=1NkWmnxcNoFKz>7S9n_1N!U+SJ zS~-a1ixL&DEPv-!$-A&*@hr+j` z_4{t7g?_9_c}WI3

LHg{$Ygy)!-XbMFqV(OB)`{5Veu+Ff^LDEee;XJ_VmjE3Hx z7oYI*>*M65=M9VPGICxG5qyR5o}KF_(l(b0Bh2mFds@@B%fYjo^1ceKAxx^(`$UIF z+RWw8)=~1oq&pK(VZmU37wv;>otAB~Cu6hLSk-az#ixoF`&uP-<>K(?{HXSwnAbI~ zOw)W*IkScz^TTq>`S2QA(9&-0v)^?SP;@O~O)h4OUQ=1z!$(LfhkD1fK&E9hG9;toZ|6QKW(#RajP}58g;jW>owP=hh|IatwNgR z(iUYFWuoE>qDdSS%>b0?*8(-IXc7v0Hv)J-5Yi$8P2NZY5Jn zkR~(2xqAJ5bNTsv(%QZ9LH@u>rHfxv-p>?f42u@uOjqBpr&Gq@N#!%A4G6Say{v!F z#?tKU!WQetfo8$zcOCbcop;wZ@{m1iW9-_Gwv;relE9F(Do=Q|yNR5@lnU6sL9?Ux zTS0~GR`Lsd~$%6R-A zJtS!eNEDDc(Yuk3?kqSh>pS}1$*f&Q&UY_+5L)g_Qi>tM%oW9zh;e> z1*wuY51%~<~x-b)aSR! zMsZd-FsNnAUGzH7+(?;tbtb-ny04hkl^jp%?drrL(0#34<2 z2$CK=Ds2>ppDTLvU9MJ#%x4RmSecU2d~p_fB4^TW4SmyB8Ts5Ao173GSSZzufJVl4 zb07D2YpJ{)5Dt5wIf$r&(nt`WL{X&BAQ5X36cP~7ib$Tsq2MmSIwNp|K`YwV;4>fc z)U7>CFgWLkms!fhR}KSpLS(`@`T~-s40v!5 z2|PRk9&i`@^5z7F5FzyUFN1wFV+5foV}T__T#_M85F;?yAod;5zD}z~B0eImZK)VI z8p6WEcZVS5p6v1lp+Ds_4jX-)B zvwu<2YKJ(}cicL~|F zD4h@EilZg}|4a#7@5M3qKmY)%(EnYL^4oH7&^Ok%_%E6bDYEh#3>YIfK2Q^>cHrk6 z^uJ?swhVz_A=@o&=Iv69`6a7J-I7`rp3hEL;+SADL@z}u)Z%|;I{d4$9|xMNXHgro zn7A@#%h&~TL*u1V*A72g77jduxF(toljq`O8~^!e;1$i=*h|fhdS2MoU8($8S!RP4 zs;;RiS?ODBB)#K}O~6M#Ni~9HvfXD>!o5 zkmNxNJ7dWvQ;tX#hbVGqLxL(a`7^O>gEg!%fgD-%bU>bx zz&{PfTw!zM(qIWnPy`)3n0|poLPe$LMu`dIHay)4JcquW%5>-=F9nPTYgOuO@iUK4 zmpPXZwnrm_grIo3&2o!8`ap7`xF?zoGXPLf%*r+}MOKs4`vt2>%2YX(wpg^~-uB+f zB{|kTkX6}OAEADZ-Y9K<(WWkSopVL|pe9I|hg@a#E{6*MYyYN%-Qkv-%X%{mS!6;U z-F;|(qL^sVRxZF-nNVU{71>e0yd8Q0pG$s_X2fJ9EM})Cmw+zV z7cAi=ildw!0x-{u$`l|m$agC&$;Hdf-}cEa^{9~4zBjTAU+>JL+ouf_(M_lD=nteS z0Fo!OPjCo`&T{u4G2HgIFZRK64>b(P_4(>>A5-9s9z;sBbO%r2`vU0bM?RN?HssOV znJdwS?tvi6_(d0hwpE@m93J%i)8;ADzw|a#;E7qxYk~dkxfMz;wPF{bdem>CeQO>M z@5b2J-{Wv#$q%%{vZFAuY`Gm-_YK2j7i1s0%g%kJ&kS1NPio=-ORzE)q) zUD?rXK&zYQ9riyXi&+l&)e;H-Kt>e+fc3uwTgDdl_RgmNE#A`6w%_1F^1G=aXzZ2L zF07M?NLnQ0*T~%fb$bE$FuP5MNG26da9-2@o$<25;+8q+X2lKipgD_=ZGDnynnt%QvEBWnGx_K#vzr+gcN>^ zRsB(c7vMsf@;;@jL{1w2s_U?b9uo(&)p5JB36s}~p^vJn8Q@x=4UFuj z%JS{@)c1uQ?o{5K5x$Yfsh*lIcw;zvZKMV^Kv~`oJFw!xpPlKKC+e-3^?W>T9xdWL z*Q^Wt?e_g#PWRZcu)SHO$!j0Y4P=gXulgq+F?j7sM4SAHTi4s2DrdZyK`ld#b?7!G zq6^h@-6fwQT94u>J@e-}Srn*gNR+f)(Rg-a0Zayt-~7nFxU4#|JVzHU%$Ej9kV1pC zg-w0lsEBgdvKpk0r6h@r$c&BLlMH2uQ-v_oN>rH);uV-|7)%{}&s@==#^QLRgtyh- zFG}r^{y{`i(qghOVOPm>ScOK#3RXP?iZ+U9kP7!iY%uANZA@tzR9OQv!@B(DOkcO% zGG~3_?DiTYr~m^-`asC!@;f^4tStY5!Ew5>w7Ha(RE5!tEpX)Y;&=R8Za4&ATeZ|D(5YaHnBFWuDRrDX#ESe|ENdlq={m=g#k z6We+nSn>yI#wtK?|B>9!Z@_`YnHC*&eFm|t#vJT`-XWOZVz3L^V~X}tem#@DGY9^g zWI_TwV=77YZx_UenT-VWMHR_>(yT*(M04ecipS{)nd3ad36=&5gIxR>WAny;7K&k~ z-Twk0>BflHr5#_o>$&`qC|AGa=G6)`g!YWE^hW@`dfuzc29YA`4+2s>;?$L&F)2fX zBxke2IBT(HkYu%K`k+dQCDp1_<>NMUpA`RBF|({@BXaBG%do}SM8q`AaKl+m7Od`9 z$4d}B?Y5Wm$?%rj3q~cL>p(m_gf-UU62d%dt+qR7L-xomB@I2|vNl)12n^Frj&GQ!`Eu(2H{8Dzntj2 z175hpBU=D*e2s6+y4u%_hXmut)SmC>x0aclAJ+isUXzEoEE*xUfsLSFuW=T$C^E?e zCx|2+3>nhEI9XP87jo&|5rJmsPg_E_INyK(-21*RFspP&QrBW5vO*S3GF2%<$v+P& z0{MGxGAV$rwdd4w^dpufhl0Q?6Krs^a)&qq&h4}TDM=c3oH3y$Wuyqj;bfFkg!X-P zQwR94xf|CT$rkVI2oBb}jo4(3zWj|#j8KV(bl?Nkn&5TlT-xxhY3%Uu@W`suXlj)iT(6?){Wzmo?SBK2xZs*J^cwEbI6*v03 zQy*@PU0N?w_M;oLBhXiAkF+-!jfRJ74-LYln6o2ANRv!L>NFs!klqUFHqsl9GhCjy zP@XZa+vOKRhs3^s?<7M*z(Q;{zRVJ6BpF=A^W$9RRxyfb=P;awl_rha_Ahxf+@zsn zy?{$$%Kr_kKnXY)mFv>;!r}hTn{c#oBRZ+&oM>*ok0Xg}`um_$3z8oz0Vs(ow?E0> zn{)}3p)Nv4TBn8sq*lzh3uul)9-f4d%@~hR_5xB|CI%dqOGlH&sNaV@g7lVt?aKUB4=pMj~6%yT7 z^Yi)H0$h0F^FYr2);|Ut-s0wzI2u)lW5C9m(t8eBu}PANs3Bf7Z1QfNf70D~r62C} zSXhwf?eUHgEr7FmFBBWkeNugBYpz8BDGO)?rC2R}>I~J*^r4*Q8-Cv>Q^A$qK9u+Y za8Pqj@H+G$c2}Df-0_8>Im_4@`QR}F*sJIlF-LF2@OhZix^+Vnj$DMXG_OTUq6#Ug zY}yneieSDvNZlNR4%9_YwFM8)5%W;o6XrV>xj(LQz^R;HZnf(fTUoG{>gf%m z8i^*`;#Ct_FOvUt%&`iI*(P(+j+czpdYR$K39R6-I+Qb5;-oO645d9akzd))Ap!}EnbT<1F#PnUihv8t&O+in3hHG?0H9@1L| zpt>0+BoT{zcXxMM7YvXZ`f=jNYt=crzDwIWCeq_Rp$7GG+FBwF8pQsc9gEJ{qCzm% zoB|a@zFCN-_Nmi`DrE+nETj>GJba*WX;fBwx#`PS2k%;JkKaB)PWEU-m&I)TGedg9!4~3Fp)nn0m}CxwC0(6j?%x-v_cD4d+n(vq zr~TpJ#`4sVx<>vG%N*ujVwSEGG3~q+f(!p-t1dJ)tT`K=X)HM2JQTz;r?#O(CWc@n zNn@ONrZW^EEOX>Vexg~`hLKk8{THHSsaWUq`Z5Yy*t|yP_3;J_Xc708q~% zlW~Anok*Id&N2l`G%AjAYs@;mm%jjfCHO#!8Odd3!|TJG<`Y5x=T3OzXJ++NB;?Vu z<(Bs;HV3?S?HDkV7Kmd5D_~>MmnF)y%p_BjR2dJN2EtfF3U4FJM-oP;oDto3?)7O% zxZ)V^NQV09lF#4vpQG9;OkYgOjGUVxQ_;Xl*r~VR!nVnDk z|HQQvRQ#ykAOHYIzq~c={~xaX<+EIkU7Ypx9h^*UEbUDHH&*K?&DMT{4Pj*GL>a-W zj>MKGypCid`^t`j!9bve{UQKx0O5Ae1UXqUun@~U$4`7h-tyNr!jd6Z072sL?v?zl zgIRvT{X$iiFW7bbbd554K^aI3qwLYh?fqjU2keN_G23#?s=&~#eCn*@ZA>NoX$pv? zZ*E<7S#B!7=w*sT^0#t4xG=Q2Bx;jUPXD}{2-_J%QnSf*aUdZevf!UPR&NGcVF1+q z0Vhs_ry?>`i?(cvT9Fg;c0`Px@W5HH@Dmz!F(=L=lA0zuv8C?pXt$zSlsX{EN!o4^ zFEsO-s|TITf3e>YE36xy$uB-?Lc$Icr(-=&@5m~!bgJlLR@GXp0uiTCF?1u%sBl_i z;efBs&lX)5akh!7vrWIO?C9gz41}PDUFS~DiUWMuroy|rQmfrJfH%wery8}=+WL{= zBZ`R+HB28`8PHF0gp+e`13@`qsS0GSPs5X0scuA^TB(KaX+W1UnZUka z^5AqTY${bzn-o2SUBA;bCQ?_fCzD8=IKW|s4!1;X{9x(y=v2FU`LsM_xWlfyNmc5= z9;!Np?w2V3JFl$RDz3$=1h&COZ^=dw#;LHV-h(AX(z1^;No4x_6MAE;o#t=d;U&)- zVJBOC-eabZ4wd+=JT^`NWJAgp!uCEiWUw{C=diHMd?1_m4aXg59`HiIP%TlFa8%5@ z11h#+nCMo8&rTH!`~7j?mq%s8_Aqvt3dfU)rCJ=r;byvsrVy}{Sb8Ey|n*FlXWeIfBU!lsW3ny}(u#dyc`=vNK2gqYV`)HB!H$)s+_ z*$i!*CZMaaX$qF6&WylJ!Op*9?v;$!-VTY^d)NXT^7=@7yH^*4G{cth3y#@v3oPiS zkpB)@Xg#PFh-m2_)J0bL&<#IGLL8Hv@NB|Lh*z&#PJy(0D0hN%{Gn&((L+=GWG%vy z)Gd=?rKtd!3}^~ChfH(|Rfi@V-y&wy%bj*}My{qu6767z>FZ#K%~=l=5_kJ`cLnXJ zxPGQKvF<0(i(cx%yT?5~A0zBRrYD2>|zENDZ5z#@?((+0T6Fe(9%SHMEY zL}>U4jkuYN?;GGTa;>8|rzoy~$+i&|-oc;=x7ARa2M@(VEIxurpV%$nY@$=lWZ zBn_5cvFGzHAiy)>AeQG{U!!}tZxF3xXYVhqjlT0&1}{&08+bOdUK@vVz?Ap2I-K40 z4=cCS|DaR6@>k@FVDm!A(^eF`Ekag`#`1f(z`9%C;->!S>g}McYj0N-F6-;>^w+j@ zYZqzx`}*FZ0XAbSCw+!Nj+6E{*j?N*$Y{|5Ze6K>SDKLzHO zxK|| zA10#ECZDM+K`DmzeaD?*%$%;Uu4Vg}5P}nDT5y^@y>8Lpk&$*5Q#oClarME*9iCB; zmq$8BCbJoW|w!ECjCG+9I`Gcpn z2#dS>!fU#u#a?bSMD`94T@1kx4d`2#aA%Klft0LL*Pj@&ItmpnenDwDXdOLW%lUw2 zM(!&6N^^i5;>nyR>FDo5AgfwEoj7rNw0J=$nnb-g=)#@!TwzF;^qi+c*OJr>&CZij+w3ew@CaZpSz2)%YdHG(8U4!>wG$UCT zgL&D_Q>PwAQ5m|YXLdqd9bSy_N|QpF32W^*7d1!SB&%vB3=JWz2nmiGw+T8a&4b;r zXx|=Ql{A>S^RTKg8n~ItfstL)-Fv;sfXq11yuUKFLNkV`oKLM@%f00m?oR26wq_i>6aNUv_5UZ%IaP7p^tn)>V{^OePK@;?6a%~LZ4RBIrNW-5+CouM(nj|+9!jY5cp~{!!wz{ zfrpb_2rQb2^^) z@vKjKbIic|4iK5^|B_d$GRCzI0WA+bHtwT{7zmD1ci2ewMg7gGjFDN~5jPgWuXcq~ zd$60@LhgT%`Q3?;4j7_&*&!@2$R*en}#!h%Yl$dqDLXL#@-!o|O6PE4?*OY=Jw`h!cQ~uyZZ>0&5 z?wxc`bTYtiCYkodkYOx=&7Y2tq+2)fABQXyM;k+)Gn`XH2)}yY7EFaOjbdupx@b>8 zepBioRKl9a-#(x-gk#&Y4HQ;jl5J`If$W6QN}?a87$!6|meLTAEQG`^TeKCu{U12|#TJ;Oo`jAqe1F#K2%y2D z2Xt7V6r_S*CBwBX+VD43)E8|ZhvNc{@(yf1&-sxZ#)x0`A3Hq7l)~%jZm~-TF)Q7% zvu6jM#2<-(&E8noT~LZKwnCy&X}Kt4mQjZ+k_ikNZ&(9(33CMJ>qU1XWrf9b7( zvn3}Qw9p~F?_4>)!ZrF`BCWGX9Di}3zu|3i#Pj=vtbqnh=EF-$eb*L0Got_0ske)EK=(a!#6-12^Uy;7y>`}-oOBSLR+7Z?-LndEK~+ zT1Avwh7`A7?=y6kE9}(*zK-xjtU|-jZB~C$F)_H@QR7`WB4ZJ$eMjTbOOZt*4fSBqcg-N znyaSwU%$WoeB?(=yZ$0aNq)X89Yt&QOJ#BGa55p}<@z5_ST_X~D}goNPt2D#M-nj| zl~b*3$KYdOUj;>uYD68Si8LUGkjZnQIUQcIXe}PKr_Y>Bv}LeAxbel#WJ_MvxTTZ+q{&Qmf)BJBxJ1g3+)}q&?&VA0N z16IC|VV$zlpeXNK^4sguxn6;VSjjYVEYqbYMEO??P@73cGgTR-pqhHcQoh{oKz5$6 z$i*D(({{n)^1(BkA@47@f5K>2INh;a*8c_l6eY8tS~DZ{J1s}sLguR6E{gCYxqPWI za0tpLk}C%`eK2DR5C z@n?7iH;I6dWP!lr_4-UoC_tQPA!E28?98~niP@D{eJ{vg=>VZ>2C3C@yXA#Ka|{@1 zw|~+rp<+JAB6AT6$*P&ws0?+VCKwU$01b>@L?}8@<6z4 zu&wV0b$($Z3gIhb(9s~ue(4oI77Ef2aY{}SDKHNtwyO{e8BEePux!U#0fCLh;~S2x zoRLOa>1U`avQ&!>*W^t(!`qF zP^>0Xg;wkJ_mv&J;?Joiqwe zkX!1T=o{+W|FXKjr^xT>^n0S%|F5OGs_g$D*sc?G7>~PW1jTwJaVQ&f&~{rOlSG3I zfG)@+j)ZWjLII_^t{y+}gaeW(@pw^tZ{)Xan3Drbvv0?RMTf9u$^!I63JCImb_S(0 zE5^32H>Wh2UeCOu(&*tKk&LmCqduMVsb(Qqld7$=;WAKd&z)?iZoQgdR~!uOJ4+0) z&kud{od{I6eTB1a#Xy+9{^e6e7PC$Hy@)*wp|`#$l?<}hT(rT_i8lB+kwGL9!GCGt z0ghjA!bvIG43b%OGfe-Dn~*UjHvKD6&d_XUgz=;BmB+fo_nbu;zW;nEskq3ve7||9 z;PelQGO6*2o~USsN6@N>QuX6LId~EvOc=-?`G^~SIhD3F@fHe}jd{lWz5uB&MWX!U zm_8jde&B*OEH0THB8hV(ZmE(@LQc+D&>E#*+dN^dTH9@{hYDRh#RmXon0oa@w{iK9 zqtCacw>kMHTn{X7d6Znb#BY&0m&wn^7w7a_nx8^d{27m9&@voKxJS~xdI=2@zTF2O zD{3qb{2jNas!)z)I1L`$CH}p6gqL^m14_TAc%;L%5|M!0WZU#yE>uPd***<1IR&L8~Zt3Of%Gzd>Sgu!$Vw~^`h(43G z`_?m$O-n`vgYrK$HNUKuXKkW_eSbWUWHzdZ9GAS5ZKe6!?0lSBT0>+WNiyP0zCrTN zs-nUIocN_vT${FN(4bo&B1MDrPL6C3hXqVzY^6T6M(ZJp*IUKhQt{TYK z!Oc636tN_kZtjEFo|k8lW>nESrMj3|I3%+cu=ut#>6+^u+uB8Z z(wCmRVvV5xD}KS=318<5+s$)y?LK3}jP7P^`j_gkmH)X7oL=1V+!1&-5by4br{`lD5$d{oDqHOAyGp!M8hQrxQxXic}*~oI(#XF`<96&7>g~n42 zi$HiW1vwiYc@A_Kj8%^iz?B4ON>%&DQ}Ns)a8c@Z#JO+|vgXxXo4UHy{$Y<@wFUy5 zl}v=#WR-00bW<@iP5b6m!)6G)q$;=M74c9sd=y$fG%YrXYZ0E>`lvYsd76HR0U3lc z)|0eHu}FNeNVqUGezL;BT#4|yxbZ@=X)Cx%reR;S0f}=9f*5lt1~e~XQaIwFFkvYi zWF;qs!b1zc4pt_L5tS52GM1%DkiKAJ9V+SEA5aQ-;-cTT@Q{_P6bctLBsgK&BKeZ{ zr2^v#Y>IIU=!oOV*Vrdq-_Az{@(B8Jq8FRPIl35!&=gQ(nDG{a?~6wECn7y0{=w18 zG>+Ex&vQ@sxUAwPR-2a--n(+(6&l~V`Uvjjmd)EVs#qE*g~@0p8kWn>;xUP%AX5#8 zGm12J0dnJ@+%p1j*N-yr4FJBaTVjXNSCN)T2>{6H#;pqy^mC5Xv2SCoWYi4A=ivp{ z5JMrl-@OOeM5lzAZ*mDiqAnVe73Qvj78OL=t00JFY}??%AQs%P0%?n*P&@IM5c!qZ z;xtnXI)@BV&w?3;FsCD3MIZ4P6mS-Gt#M&c&nvES<>$s95F+1k+Y-O&=tLTT_y9%TAfr!J96VudeTJsY-1Qjc`Ig(@Fp=6%b0bus%ra*ClS$+d z-c~zkE#$pjTBFC)!y!`q4Z_qjv$0^79QK^EggnGPRh}(F_Vc3!851_ycHI~)oWuZF zNJ__q!s~3GG$AB1Mj0ADW-+7xM-QiR@QkN%TL86K$8HKqVKs$24Xl+0e>t#f3br2M z1sXO9w_`L{wOc0-Xj|ZJ2**^|%*=hA53BQ0H*~-I3NhGkR0E%B(G(bO4V$Hkc;&7p z_^s;fnM{Xf9h8xYeOnC2A-xOkR9lx5IMQ@Z0P%~}orznvKSndxmceYph+ zh93J{f7a)Y`suCWg3WFf*Y897|I82&f7>&qelx@a*8ife{=bP4BZptW0$y>=r(Fw2 z-H?nha+W0F`-300!#)t7SOf3*M@=VHXzZ+r+ldY_RfUldePN=<@6!!gH^06=VNXVq zuPf>N{^F9z@O;m)k2GklzSEatXi-Z|)zNz6<>jTo+rRL!{~2eb>F4e1BgbXT*@w80 zQKT)@;Im4_e>XD~w7B!?r<$S~1}d_=Q=!SEAS^*s+aZNJLhNp|$qXHWG=nj!M54A# zRjs`km9qTJhg3?Dk~c>ktsU8E!!i$MLNprP$Lj0;tsxYus=Q2fkpy{BD6N-4ChsUd zb&9CVl*Awl^8))o)#&E2X&|&vbIK!}MIM$)4DG=}6Q!2rk}g9_Nt4+EM^M|yUaTe2 z&}*#hE?`X=x^u`axIzlr$adt2hg`>fKurT$hUxZFP<>KY1{dj7@ls@a5Sy8Y3?w#d zQM&H;Dl78q2VCGnIfyMoL1*+c!`U#4hRd*XVR#Hq#~&r1@giq*TcT+ z=Jk6!7k@XkJV;{!QR6gOef}8O{*9(iI*6dlXb#m79A%620SWT9mv*=T3EI#l@Mml; zX(|jO?l@yz_)~-^&hz{iyW@aCqHC2@P9%5~-#gRO4yQwS-oj#w?>-Euedt|wbUFFh zlhkE_%6?Kgx`CVu$%z42;HBH*nDTQ(la0O6SCdygI$Skh}JDsUVki{V-QdF@I_;hA#*!s~byx9BxE1p!Togh$};+ z5e|q+LOb$KtVG&NTPN~@Ms6Pdj8CghV0WHPg#U}Ne+;rLjFN@n>auOywr$(!vTfT|m+e!w)n(hZ ztuDVcckUPQ&HXVm|M!X5vEw{zttT^ACaBs!_-nTpnSUbr>m4+z2M5S(oyV6Vq#3(8 z$*@O6C5vSN#C^Oj!5)|Ukq2Puukry4-^63M6ioxb-eo!~-#7D5EGT?F8>DF0_t>TMbE|RokIM|uh(T+bpIG29$=4aJ(gR=E_T~wDFM{tV!*}_ z+05R$x;pR+?Bx_g)QNxlT8^7eV&b|i|Cx#Xr9w*p`A$K;Z5w5=W#KAydhV4&-od=9 zvu@OvNiwFP3lcZ!lQNE`gYgcImF7C_DwFS2aUMhTmuR`XaJP!!7~9)VL*YZ!AARy`#VG27>CIN^*XhTVLk;f z%l{sId#Jnw6!mUz(^e`ptu0TTFTwbY>Z>+E%T;Nt)ark@ydL*;mACM;M%k@Zoi0VI zJ?z)kemkIMZFE{|tGzB&UQKM+ep=l-w(9g|%M18_`TWzuXV1E*_#LD0?f7Mf#6KU) zQ?6l~FPKa%@O=dEys@+BbPL3f>$MKrXudH*&dhRcG#DD#u+8S}ZifY*bJ$-CtJyaH*3GhAN zHtBWi93Nzn&$?~c02-68<{tO@-zg5uY^a*-TlhW)Jn!wFqR15#+6r(KvJCLPuQQVkN?w^Xj?n7Upv>3*5umR+xGs2r#q19);<@?Wheki+52 z_4<{+7d-*Cm+Z8@5p4Avi_TiVKi^1uE2zZQ!{oZ{1dMw&ty>*yXViJL)x);1d8^_| zU*h??E#59P`0%2eoS%~R?Fn++2x>HXBHeo*=4*l{7`y<=@LyKUpR1d*i$^fsmZh#P z8*=G|p}54fFNyae7X$GZPsQ5D6OUGfWU_!++7}KjPdDY}6Drun9#VtiscV_0=kG=7 zy#R$$1?~)fsH69i0aOJlho1~JS#t0*NoFD~;NzS@<+BeP%`z8{3Kv+G+cfHMyg<7; zjji2;4j~apW=`9nh@cC>Wt@@OfTKT+ihwMZ;H|$%I~mRiu|x-_`l&?_Q=CCyCB}bK zV1HY&kr2k~q6R%HHz*%rf_JHKHSit*5d@ma)@Cw^Sc%IRiAVnKS{Q?nU5B^`dcb8& zcrM9o=QVJV7Ek>#-J$#`R;-ELD0f zCY;yPTLmKpOKoymx=fa|E2O|Y^Xf7w0dj{*uEhg~Fg@=KqJt+shHM|6M(0NI^YoP( zuu;jZMNpB0QZj`V#StN|8q!6V5m)ywo%uxuI<0B+W0HHN1ntdrV}yjel}*_w)t37+ zN2@1~QXh8PqSRT^tm%LKwa}n1X!PVb-5b(uhvNcYdX!FC$`{o_!o0w>nn~Fu^2U?( zBo#SxdV`WI0dVzj-k{4dDe=eUt)E?J>SfcJEw#fCJW#C135jlf(=0bqkwgqH<5<0~x_0k!T28CyE;BujbE%oJKcJ&y?zL56!-svw#mhLT4H( z&Q2Qbw=1S4_C@M8)1(B<;hZ_2Bb910MYnE6Q5{cWq*Obx^Z83p9fz?`be`LGTjM?E zRSx;dWwjb3s@5W6hlS3js|^G|AO{sY{#IrEy&)VUbf`#O{!*5<6-6CB>$=LiL4E~5 zcyhb>vt-m7@9O+7*UflVxmFSzTx_1IKz;2O)pQQbZ)ZS5KO}k(vT`a#*#QE^mR#77 z?#aty|QA0Wl_3UW$ zhl6Ny6IIyi8{;@3j&5cv%oHUM!`s*RW+hAl&2ebpKmH4SvA{Yu5!+KcZZBYwM&iCs z+?8=@7xBiY@Im^KtgsueHW@7IWN36Smp!z`=@DF^Yo?_7m;T2O=EW*8#XIvSWLItw z&KQ1nu&TilHt9^rAZ_UFs@{oW>`8`pLi3dsajTxcqc+`(RK>TIrDeB@Xno!VS1F|3*jcn7;^5(xr}y z%vTx0mfgF;x1?AO65341d>Zqk`9|!Yv-gfI&Xr#ND$Nv_!C_uQ^PH4_ z%+{e2lrt@Sfsc@_tkIfu*O?oRup4G*M~Ql!#o&3O(75U}Fk^%Np6X819DyVY(rWIH zW`h}48Lm6cMCr}qIBriBnleTEHGbcCb#7?bo`Ud))v#QqN+0B_X8M@??s_1%I3dCm zrqOg^(w?A0Q&(Tt1-2vqE(zF{*9)`TR>U*$rdiydjp8#N+HbRAZ>3qGCrE zF1-4dr^~8L4PU-;{gkXF-e43)m_hVe>#oReEX$|I! zwryW}w*9JTsPJ{>hY5@H`((CC_ID+^~3%*%Rw8P8s_gDkn z^Bo$R)exae%#tF3p`&14e}e9SVMQq5v?uL4JrEZFJP+kYnx9jIX7`xv;J=>60Z;i= z+FEBRd*uxYvQD$lz1YT$D8R>|RQil>yxw2tsH#1qBN||ixRD`VVoP3%snv-=axg4raDI2p%8|sT(fUZD7IInikZOSgbLyAMcL$oqk7s zIJNIm!0`ozX6{C|V4)0LCcJkj5!==GeB_0K1`QMyj9f$aJ*f)2L|5Vp{OXqaX)k)Ab8=6JzOcyW zsS;U2o=qa6*Sr-LQWoeB0i{~PwH~O`6Zbmf@}L~wr+*ZgV)^ykO`Y{}X3Mx|Te$hP zCM%y=|D)u(ti#HbHDdiW2}oa2CMOq-$p!_jZ9)OGp*`P>2prRFRhJmCe%6_Yz;C%7 z#R~~`?_09iFt9O7(!LYpo6;|(xFB}{6Euc3mF?RO(eF&5&v@EUk}syowXAHb***p* zreEqKHDjSF;e=umQV@h|3o`R#jRb~+Zi9ggajeXka2SLb3Hr{381OGRX&ucPBP!H7 ztiwW=H6HCH0mF6{{9rU^Kh2}|725nU~1~l)UR*G!qoXW9awe!@01T?g$e!E zE>xm%cb#5g{FuLIC;^yyt$ZT3o8#5{pA2QuXH1?DpG2^)vS2RiWA7LwNLrZsKUEUcAlsl+*j%35Dr>fu416N`*}c;k z&SZJ@XG4SUE6?Y9|K}euxB)NS)l6PVr??oi&Ue)u$?h2l&$_@obRUZAe~zxqbe-Fr zeHJcvP-ySFbau||UufmnS~%s}J!N0|#;y3(ge}%se3crpVd!o;2~R`W1T4#b58X^8 zCG9+%Vpj8Ue3^XUPB7iE!u9uFt!{&%DsCxK#}D9N!M~WL*IDzH7F#o%U#@@GhUbQ7 zgR`+^lJbZ5PM6vtTvv!53{Ez%`sU{+6Q6a2Ld%>cM&_}^Wiv>!2XEoq);2P2|qp>Tfg0S8Q&li#jRl< zRzK)+4@xhtv|0PO30TqWQ9C&9&}#oo+rl+F(EK;|JU()j?}N)VMj9wF#wI-fXQDjW z#vX5uf87Y`^YJSK9)(u?Ao|m)XVbp&(+U-iyhQ`u4#IRVn~|{$PSSc5;DZG$;-D-k zJ^X6K^JzLQ;_@PEE{z4aLr14`52)h3B3mcz@HK7N@9Fsq=+7w+@7v2iA-VH`N-RU#A zowexE8PA1UTWSc2BZPVUiO;@G-;F+{I?z`<{$7_H^inHnUmFJh2E;xrW$N$Um*&VT zS-zW+Y$P)3yfaZcJJ$6a%?Sk$|0Y1}Lv7>`u^on#IxUUdXf)b1=a6(|^7;ggUDP7E zF&Hf0S!Bld@Kn7Hf3C2}3}c@ffAUr~xAj!`pL_o+a!a0*zmxbQyV3feeAoUHxpgshGcd5U zGqe9ca?k$_cB>&5U-UzlztqqG&*u$L(^fR`-23*l}jOeSCCz!C`Ev?VEMqwtM~kKE+?U$ejza zNyeFgKV#?p6Eio$6!}Mct)#WXfmQT4I#g`#OtAQa2fm24ieU(D@DYEq6JydO>*!jl z&n!0XMRH-(&Urby#vjm&ZiI7&Fr|LhBvpN){l7o3iiS_!qO)oef}Z^-a?$!WxWc;y zPHLvQF#ZY|P*U>@(=m6sNNOq_jrCX(Vw#Y)YFc&$$HV;l<4JMVk2y(KMsJ-q0^};l zLSbty8aU&{RtsY%-VDMNgo+ICB>CK=5W}ROP*V*-g4=gRO)|}HAk-HlqcL)L0YKKQ zRp;|rsk&FgFcmw10xPbcgxxSpzNFlXJsNKb@w%bJ4}}N^S)_S(m(4NtAxXD6$-2x< z;M=fYb3KrE6oD8~!~72D+V#0-wt9@k;f>+DBnw6QNg_}r$<(mx#$g`TAtp7q*W5;j zQL}jd2s@)I;Dv{w4!wwPU158HR9&-BU^IezTDEjC%5pCzP*m!&IW{T%SS{ zr=QKq&4zLqYNBwz263OveqZHTSmt?n-jX%DaKNjlEMbj|BEHv{2q`n|GU1gp!UiYk zKO8@;8FS_s@LZ#^B5EoC8m^V>PYQVji%%xiRZ$PSsMDP9Ga!@7{<{H$Er$Hje9dxaiv>Rre2vbp%nT{im!;Skbgo zn6&L|e9|9MAyjcGOvSufqd#F64VozUC8Y54q^25@7axej_nGAu=lp#J6)OkPmdd`KwPSX9d|ISrhYaf#45O}5m%}+o0Nd!*9|3SaX_W|)=k3Z)9S>GN85YWU7 z5D?XWNA3UT@i%n-0ssC-zUDIL!g+%;sjv6OAaH(J@3PA1;8POUUhZo8vc&4Ac9=Ui z=gEhbO+|@PlcH^S$M=2$DJJE}k|)YBV@s$|Nf1M^6Wv49bq3mHf8Kd9LEQLkp{l*! zLvaTx1TfehMcwZoC9n8@UuXCLXR4>k&s5I$`Y5X_e0h9zvS{e-=>9_<2>^c-RKdmv%k^fAj z*X%Hn(WOg&z-DxH!a!ll#ud+_u;0_TAa{0s=;3u>`xaU;tq0dB&AVW$gJIS{*Ln+c zaIs-@&po$#a4vkM$ifK&kFcp;l988d%Yo@cRt5&DkEM=#0 z^R#sEw2@UJV1Q;SbGv4_-NuJ*t(GG8hIN5VxKnd7GoWjbowLp5;~iJSkg0hwy7;y{EF|6Y-jJzZ*=}Q+yli{k5&ND`z_LVy0s(S`(=Bsv83kdRm;aC3&KN5qw>gx zeWB5e{h}>W!VW1r+kb)e&T`ThE@+FUdoijph64}c3c52!h9zCymeT?BZ zSq4A7Vha*N%vZfIVYR2=T4kV@gD%g}%y$%J8^EK7f>}Xpl$FCcN#3XauteNk2qn4F zUY7474O(WmGC$vK=gZR8y9j+HQtK? zF$+UE%7|;-0Y_<@g_jB}NZuA`d+C1f*`k!ab3&A+kC=<%zUDm1<&GkBR|c2LZ+71v z-kM3zHh6nwwlq82dZ)VWbiz4aP!~Anf#PBQwy$T5@b*=WpHKt*LlwM*O+k}%l7|QK zV57XXqTOX<9(9%gBbgP1k8Od6K`BcNI;t+e1YftsS-B| z9-0CVMm`9ZeUSJlT|`x6CzUI2v+Vep}ave!}i#Dy#T97;{r&Sk(*S0F5R6oS- zkIb2SY0*QTEtr->h{ht=6c{3gFK`r87OY^A4|54}hXP3sv36}S*49TM@2~i9O>R@( z*W>w8nEc7fP+nG~t zr^jC%2>M<+r=DMQbA2;$w3QS7OA`RkIXfS1u?!dsoGc-H+vrbUC7X@Wkzqc+k^qGB zNwp#_XTGp)>al`ZSn2S6-~~H+PH`Mf@~bNGmd+)F?dXJp$MZWeR~PH0kpQb++54r zT(PvO(t-ztLL6 zW{Rj`u$tot5lRre3l`u4%Q`Xg1iHSyycIze)s^DJifIUCa4bQ`Im-$P zMrmSV_el{i>R{AG4`h(LF{F3kh0Zp1C_i~iM7%&7nj9%Zqoid*U?dUEEq||K7L$_5 zuJ5+5!jvo_rl|KSy?IK9vtBg*JeDerh@Yx}o35D21E1rFu1pGSntK^O&sgg+(_c}Y zfJZe|VgQtRS$Yii53#?^ml{~eqtZgOeb8yd-@9=+9b7p zb!%*IVgpID-W`S@K5L7GqRpIzUNzIEgE^i^zb1HfD5(W{7eXLqH$ihV-d~ob?Flxv z+N2W~xobmYo^X>ME-v$WR(=6ayBFK_#9eqLc-#gtD{*n4zL{Et7N1P1%5JrRfaPT5Jph z>*rf+uhjFlR2&hcS`aUsFWcAEc-k@t*JV9Us;{4Vlb_PZLZ|0klalP4Wal(bU~eyI zmO~`H^1sKHzHl)-i^6<7=pK-pN1tYyvExzM+R}%!4a@WnOV{k$*;tP7zbkTa$Fj8z zmT&zk3BF+qty|*kh^1TB)|qs_Zxdy5{Tx8>hcH5Gc|;heOq(;ycB4Li3vp}|p7A?%jv|Rx zbQc2^x?GbO;vl>={4MIPl7y~+ly)@gcA6`XC4l4>0zM3^hbYT99df%ocyGuNyDT?! zZ5v`)-t4wh?ot5S`C#?_9BBQ%!$y_!x$z;@y|P}YyUjlc^=P^iHqAeP)TXOLN)nZ? zfBrQ<7|b|+MS?ClgT2GjI;cI^ogZsn#WVFU`JlPBKe2Z?`e;)C;-@`8DF^<8AbNYF z=yc{il_bWuB~5G)#45yg4tAKG8-1G2{LPB0YxTA(HB<^Xmbg1PGrhA6*r92In`(P} zmK${bGtYIptdAdDXXxu@hvQ!z#Vj{# zLvzbQL|8SBJLCzhA8I41&>GsO)|_)|$t#&S4Z&n33jWlWJjZ)G4^4p-ge-&#EF`5T z6d^=bNPPVoOcCs6;s@w2M&1ZRZVp-z@O?LD?VO*IZCxt7@Oppx>ZNoM1^d*PIm)MZ zW6q4$<2qv>{RnUZ`7sj` zd%gS5yL!TGdk%#yF2(=K+>rPp@_mCdG1SDmw-ZR*&wJc0ZChCok@IJiwLdiJx-(l=2R3CiW|VKIg>u?bN$6`07dUNuj_PdR=<@%p`Ic#r9(n=m&n)sb1B}2vBDu(3R1u83C#ID<6OYM!Stue)qx=Zj~n4Rf)twvG( z2J{7KqB&sMJe6J75A5&CbFID73vwfMLuhVCLO+Eaw?;Yw>bEqUY47H5;uedIZX5v6 zyz7E_CTA9Nsy7^foBs>NAtmQdEdySlmxZA<>yM6?o`T57Lp2{4mnZFOE+>wQ6$Xy2 z6~;a;vnZdO(j+R;Ae1pLCkida=*`GN&B>6MeX|+eq5e&T&-5FaBO^2%foj2C5f!Vr z_?4I!1+5UZU{I<%Gi7{EAkk^~NboiZ+u`Qo`ZH|t#^@?r+;sSAcbUq(qPhM(1zjck z%SxXCgY{0a=_mhw<<~hQ-r~fR#DfzL8YIl3WMI39vXoRtlIC1fX}%HyfIQg>?j0mANzp)nb>U zm)|D(m2U}-c&y1-1a!$*3v<^9p8(UrU_f&n!OfTLdc?-n(xGSSuF; z$F8$`X8ig9r$}CORb(L~=L9UMSVu+6DBc1qfr@cCttWZQ<+0Vo)$fVH<3UmgnJP>d zSgjcLut_yYP?53}L0E99BBbGx14L(wSpR?fj`GBOs=r+qJcOcMT7lO+| zw0}g1qK=cif7t6Wk*T54a1s(tYk1N$q;apVk!QzzJQO8XBRy-=czDEov?3rN*+J`TEBnSBWpWf z4_O4g@7HsWt-NHEbRtWe**9oAa07m$VTK0~`|4GxPp5RidFSyjkMVx!WJ zD#%FAS6FhlAHINE=1d+hPVTLsgd93V{%ZigFW`NdYD&gTTYK2*kMbM9cv}7gqZMt> z>Knhv=EWY@lqYjpnOE`YMK~+|?)fV`qsai@zm>|st@ymPawQM9_Ma=`+A*;#%|i8P zl9+js(^ZZPI$gT~uM9uv`@m4y&fpS3@At>W(f9e&T+c-tFHGzN&&HoM+N(SB+QVUM z&%5gEwrqb?s#!U2lD4Syqn7i zH_nliPR)64@%E@$$nfzl-;_tMLiEL1OKvZ(4^8b1p05fF$Okx5Oqd#U+l>JW6h)r2 z8sUA@#(kyIofkx18x*)6tA@eoFaBHXs27%>^`Dj)*ZjKK_6wpZ>@4V}^B-xg9+L#f zcea8`GY|Or-^XlmMB+z0A^%iGjQbR4KoD2wJ%B*eL21T#25e?~>X1X7<)yg%t%ol( znUU+v7Pi1(uX;fO={k1%z(90>6m>@s?wE8fPB5Y_>WjdFL*bW23~k>dgf$A8P-1om zq4~kjD&m}aUe(%3&l|&aP}La}^5L}!Yfvy~7!&dZxT9uXaCC7En58}zJSS$;|x!mME@u( zO59A1Vws<5lPv0#^404Sj?ygw9*9@#EHn=!;TKdFm(0PWX#qh7eNoDAGxyAish+nj z@%~3qQ{g>Ka6f)dG#MpPBvz_1CA~@&Hbt1}+0aF1+x^1tk&FQJ`H~EeZ<;C16>;nD z1@30%;J@7Q_(2Qjq`^8>PzV|@JSAmjelL1g6k03mU;A(CTG2)Ebc9vDFJ5YiF?*5b zSem!vEX2^nh(yH?WEnzHa+W%8(lSW~QYbL=o@yrx+kaE z#gNBU8|Urp=L{_kahikisa;G>z}GW9^T>GKuteUr+KgUkSrK>5ip^V9qSM8@kN)aJ z&}dG$8(`I@-$+sZiM}upp`EA{nYf5q{>S2 z+}snzoJGI^su{ENZ+*kWnk`gv?T;nB6RH>5rMlM8cT>J9-qp>urCXeUdde2R#U5ki z%dt?Urz3RkESTUY8&GtBalMciXIrwT>+j4!zbC}DG}?{+3t)WDN+8NyTIKJDwJw$MgqzCs8@zK953oq{rIMfq%!O^R zV>`bbxVDo>oxD%O+=O4%4fH^>g+H(q`elunp({9(7$5;vCe)Lg73-*-)MNX^=&c;X65Sdmsr!Z}MeG4p*eR^6xyGLOA=rPX)kqr96V#Cjp`K58GWI->p@*9MTu z+VmvtP;H5@Sf8@vI)Uc`#0`3Jw02MiG&7_B#(4U1xx2^YfA8w|r>yqZ^N=}J@TXWJI_KUP0@7cJ*B zqnk@xqKr!Wao4$ImGi@a!Nru}ks?(kx!m3^!clEyq>M-KI8^e_`WY))dq-Xzv3m8? z$=V8(nffU}>`&o=jx-U6&6E4fq8pPbr83;G0-A%4CMf zZyPMnRYtUE(FcYsC=Vy^^j4ht{XZPy7%W5OF*%|bLj@o5obEUP7cm(K z(bedVvtiSs`eL1DamWW;PWvw*1MV`zXMq`+16in!pIoBKRF)(^8UDe6YQ)q^RjVq* zSs!58Wx(C6wu{tt!C6aZ=1g(1`&Fm}xKbBS$6)jPS-!WF7t=vMh-uccAZjDhXd18R zl41LB$huIm4}*>u*GJu7eL|Fv<|XgR3WK7Ka#3x|MG&GBV+nlXWxxt6m4+KY83hB? z4!62YptqJYtHe4WTQId5;gD@Hc#XuGQfhZx9rV*#h(S^%f~Ws_JNfJsC_A~C-UjiB zB=snPQmNJ)6*vFu;1)~=WlN{h_d!X}L zNa_cN`Yv4tH!I5G#pUJ|l+)7Ek~37ZNY^ZmthVKo18RNbmu*|zW}{|NIsCa)rE`g62&gk@rqi+Je2=K16zn_1l+>01P};9A-Hm3Hr+%0XtBibiUWn9C2Do_Kq#a|s)5s- zg>z`AUxV)UO!Y*5d(%jFCtCJ9>0>PhO@l*Z!y)2=!;wNu3G3pPi6KkiOZ}Z47@bLG z#1tcy^@p=ygc)6N{Pv%n)%t*gLlC_$$`8C~UOw#+I&vNg-?h9ea5x~8Q{F)cB77t{ z6tmRbWrX@0fx3#|Pt1=Cis0)qiqw;t8UNlVQX8$G5AajD2?@+S=X2cc`O=G3C0G{5 zKz39s4Ab^5pUnZ)&(|*JFu2@lJS)77bYJ6)3rZ;Ea;?BAC4GsMm|u;4Kq4<9{o>Md zA;EmT(Q{G2*7YBGeP4&azHGd(zXGiM5ab#wJKm4mnm%uI&+>!yNTslM3CW~zJFY453QP_$_zSmd z13<3EUc7n-p8GjxS)Oqq@CO1%ZoGDO``)l0HXR7JaUim<`|X~VUmV0+Owadd*E(`H zz#c~;t?Xp1-i)7uD*EbkSa)F{Vu2tXIvBSvXprV811#THBlVwQTG)=Zl1*_!j~ne9 z*5>{nZGsBb#q|7--JSs{5D?RUS6KcpIcZ{IZ}(N71} zODe&z)#Zo;0E5lzt^;>t>GFx1D92>42kc^fIvP2lDCX!#s2Sh<(a+muM<0*;Vo- zDI;ZW1mH*a;Un&TS)d+IdT;~(mJ1Y~ z8^dd%iqVcWKt`$-L+;YX3a3aR2le1G8>gYg^Cir`dqfS}cE>|VQu|>g)qGBoWWdIo zIJuINU=Cn1LKB&g#vsZUrIQF!mLV#BxJhzn(+4*^I6P>U4j5&#_hnM&=*r!u6h&W#y9p4QO97NW}QecqEk&KmZOR&oVG2uzgof&fZrmr96vi7v7`#hYkWzv;pE5Bqy6M&K$-8&! z@z?r*$}`3&0tL%3NjOdg*eKyIYb=To3L*W#!yM(8{jJLr{GExMTGml^a;9!N+q$dE zLqgbq2(~`XJgS2*P&kwrq6F?pfhh4`N-4(H+Ols~>_(9BHE^OvXO{<)!o4w3^8_p# zmb+T=6Vsx#yTz`?4l{}jcrqj^LBnwmR2XSyTBc!3OrvpTtXy4>4Y9^|s$3Xr^-7A8 zPK%z1@fUoxHS`Ofmr9~ngpWL8e}dP~jf9nS|L;B2r}rNHAwT7Wq{*H(`cA=2Cz}xf z-FvS>#NGnHf}?t!KbM0n4t}O~J?B>^J&F}}r-@Z<_rrv(!`7RpIo-{W2h($e#^IFk z8A9iQhYf6e{7z&&&M^un_7@7~Rk(m&xPKB&CyFFal^r4=xY^WqgmYH=J~`D;nMnor zCq(@6v`UY}Id|3hhV(sROBeMy+2gvPEp3oMzT~qh*PFdV@M4;)ByZWdH}u_t?;rjZ z#pCAtIKMClM>ro@uBZQ!#Qrp&vOwa=w%H?+r$f=K%4tT$0Kaf6S66Q1$>gRtLF_x! zt#3~9u6gI;b1%~sRsZ$)R&TJ`&HwA<7eHA)^YY_{V)m16@!tj>{b%vCwRihJTe<(K z?fRdqr@Bmh79(QMO|4+hVV(3O$syTqgj6ppZ%djd^#2fb#m2@+HGKE5llC1qk6-Hp z@DmsxrY1)w&Et#8eEW0g)Xc zg@UnPgnFwoVsd~OKLZ5thUGfbC*p<9_poE#> zuGhowa|jssC=u?LJ7PFR%zxia`OP2c+120Qw8v{@QJ!w$>4yF>1mHhOzWOw8PeD7B zEm-W?MXG>Oi+16oQW8?qf+_LLaFJ>p&LeYxZ7;l{EN^c;7Wh(K1^r=8?g?K;xP2L4 z&WJ<6FwU@4^7XFK*J9WG09k&`WInzkBYw(Z)6`n-`{x_;xLp|xCd+7i1p`|D^y`G} zYh6!S9^mG8{*5W80QyQ(!924_b}MtOy!>Srp4UjK@dPa%(a>toQbaH*ppJH?M&W9t zvEO$y&V9biJ)tYw%$=~8+_KoeVE7ZPo=a?U*q&i*Y{1~Zil^lL_Tf5zw%+fEfq>Zm zyZPk*UVQ%l%`#n_j4kc{N4v}`?Vp?rcGUm0%WM)fitMOtR6{*g2es$~zN+iSLiIF3 zNtTjJ$u=4IdY2r-;Cdpvx}4N`M;s)b2|eW6X`jXKY0E*gfKT05j-kKpYZoLeH_??9 z#}M%UxTEe71ds;72I(Gea*32)wq}V8@sv)YP5a!mpm}DsocGa+^QP!rUsCPz$(N~4Gh6))>{V9}C z1@w-laV)UEamXN zO8u|s8C^dPEWXvv^bmHH`+x?@S zahpz|*f;cKZIq9km5a^XuL383SI_m7Ii;)N-qXPwTD(*-l{a$$Z}hkOXGF_3bmCRL zqeqKb2(_d0H(XX^2gWF9fKN4pUV&4 z1&boQm`aE|%rni=E4+N}eHta^TDu-@gs1uJwj((n47DtUJ<8 z5^+2d=^u!F*geh9-pj@W-i!SwL=2#zZ`wRZHzq~4{S;R(zUqpuv0@of>M>UEB-8_gA1txi8u?vM?k33q4`&lY8SW!LIflq^VOmOCbF!imf}q; zY$qx&EwcsPJNCr3+l~95cqD;s>EVaY&w>NT?c*8zm1{=>MG|w66)z8J-4iBqPD!WC zLoIx6tprcc;(@kLD*-P)-J!Hp z{|FRIrmKTv;Z|6}iR$Q7wr&drL$ht%iaAs&fb@DLWP^}*5|sfruJycRM!*Jv@B7<0 zD)%!x=?>qqXXnm2?h;207wa!-w7s%IHq{z!!lE;it`)!`@~%C=>x+Ht5p0P7)1~P` zFKC+Usxjws@KfzYMJQ!d_q5ZuC7DG^Mq0%hO6y27<@c&87uIZtZuJ5#Di=*rQVtN3 z78N_z@vfXxTO$&W>&Lt&13KVEh$npu z3P}S?LFLO%q^b%V>hr~38weZ+KJ;$NlRS!*YNZci)ZX@agqlpEAPpZxYxMg1`5$)+ zYsni66_NhIsNFwYGNAY8#2C9WqBRDsKDeB+Mc02M_vH6x!QI~6gte;@N{S0FHQe31 zA^3idR5(n@4Xr&8EG7tL06nJU=Hm8gT)gIn!AK)A0_i|oeQbGxm0F6=oD3*{*opekn#YhDvOm>SPe6N7})oY zu;;VF0+GO;e-486l}66IOEwyBm#%shpN`t1QRCXns{@%_2fB<17Ucg&**i9g67<`i zz1y~J+qP}nw(Z_+Tf1%Bwr$(Crq4{=n7Ah<&U5EQ))%OX%vCG@YyHyin6qr6n61f? z7)uO@hcwULqKY5yiC~P#qYyw4BJ%f>^ND8@OSr+(1>b^?{}v<8kxSYZ^2>jnFKoc! zQQo2dgPR4_buAZ0T*dzv4ZMU_PNq5b6{c4$pG3TZ0hyo-qYe{bOG=Q7A3sX{9Y39L zd4kOV0iP$rog0h+ojX4SP+V4~JN3R@MiV)ZMq+peFpvRRfXJ)QDA^dAXiOP~yvQXJ zijSZ%9yaLq4+?iG1Y384xf|l3&zSWhj~v+i0$~TV4kTx~1k%P+9On7&wlWj^3v*4o zGF;(&Dbpqv?UGIFV#`uNav*PI!yogH0ZFVRb^Drm&f>kbMSJRSn6Yp%kSFWm$=+rl zE%inTi@aEBI^RG?0SEPOlwpvM#Tz<<^4wvw^OY=7xHC7@ZwfIF85KAAYc89(LQ?Q^ zR}tQL=R(UDD1Y~T4xo1XV zuc20}0T|#09jp1>K{}Hp%|Ny|(}$KM8xy;>jq?81sE%zSLM(elRUu|Y3EiPT(WsIy{B^cH4JPItwqszYOpg^XoWj(M z*+q;i3ZiTNf}Nh#8MVsuR`ML)5x$>BIfr}?9vS28VtBqZ-=Zsy4nT8|kU&XOW|HUx z#RgEPgg_=y__!SLK=vpR=RX?Ff&WgHiApYl(n`n^4=9{}NFf5D#R1P$5Bn$I;g`k# zW<)%jG}Sjy=74JF-W14*werZw>GlBK>qTACDd$E@PSc- zc}N=85MDa;w^(}R>>Q5^t%zvB`dTkRF`zdaGDLZ0%#d&fOoALcKFC*}WVv`sWcXY^ z9pMn+gTN4&g`S#ztcf7I^QK&gTL4D^KI`8nQ=G#U|67cSY3h`6L9g%+U#8e3NKki2 zC)s9sI3yLg1~pKHm| zKw(#g8AkC~EO%tmN3j(`-YF;W-lovN&GkNMUT`B)H*cg%v0Rq_e)o>V@B#Zzu?ayW z>M7|Y895O5pS@Hj{_hKW)L8lOphr@R z;R$zL`L!b$?qD|d=2KP=M~soobGS)h(`_I)$FE~yuFPieAmxup1Ene19%&*HJLSQO z_U|aE0Wb$BqPOv_HJ{sw(r&d;{1*(X(4-G(befrLDGm&~0HKUBX#YiFfG+=FCsUm! z+Aik2u{P@%N~W&kHcKOQRNMV|`71-r&DosfmqPYy2Q8zbcs~+EMTm0lds;vDpx0+_ z_7I*~w0W>(ie|+h+)O}OorN|cNTn>|xX&TIWig58S9XkTVDl1lc) zXOuMg7Lk}B5+o`JsXdiGse6vqeJ(+55wvlPmhGM5)Mh}*ESV}j=1_4ahE3G+E|es`)!R+PI*#h;!GxgA!a3+L3u8BDgO>I0 zP~il)tIPxl*w!v+Qzg*xx2BNe#O9x&6Mz4da9b?GAHeOGP*0XmTYA0O+28>#!`l|b z8b7u$n0VRn6f6ozXT^TeP(le7^>2y=VbhW-tY+4z49%KUOW4k=r9aeZsP|+_Ig<}- z+D1YEc6;>3Xc-U2^D1oPz*mXG&3K~m7PDS<(VWGO^7ob0r4qvw;`-QmV1?d?+1&VsZhL>q_S|0lm9K7a zI-K!!Az3J2B2(AZV_;bU`Ou{II>K%|uew0$&7@mo&2NktV~*^hVGyy9ElG#5d=cKL zCtJu}3iAN=bUf@TP=&3Ku z8ovHkOyoK!T(3OSS7l=eSI;>fkg%GKb$v!Z2>@< z*gtKtelukwH*i=$-Q8*r5a1qhY295N2)&RBmC(nv2NL8WuhXKu=S>1gbp=aWB|3BG zPm*tR3q*{SnzUi#aRXcf4A4`@zv$Q^ZxR5UHMc z!49X-yh0m8GZt6xi8A;m>+^R0hRK5pxnk9Xry;(y%kmB3b#mSG`Waev&Dx zrrDl1W8x~uNRi2Cyx}v;g;Aeeqhc~9 zt0$T<+2YFAQu~_~X>1+~>$z4<*@5C%bpA7g{hF$9j4{tZvqHq`kUr(zLgBiqJQ1tb z6Z`$WG4jjY{EpGIr2ePyS|Uv+S9|t#m`zDrgKLW>D?PoK@8OUonclE}tgGx@ZRp~2 zqfhTnV0gHi3shqkTPhT6I2&eR%?^1zpoi!{oXiOoW}5Q z-PG=)aK7s;5Fq;@S=kJVy-D6gss3t?sdVFwt*QgE4qh$CgLWtQD4>x}Qi*v*9(>*a z6$1Wu&m&Dqig5>N@Q%Ti2lLk7v)gWuRP824aMNw>F$VMQ6enY*3UQ|;<+>Ef_kuO> zb*mIO1WmHm0s={Xs-^6PNid)+aHAioyCF=nbZBLye6V1}Q%gQv5Ux=&N#F~Y^3EsT zbL7iH^n!8^Q|?Xs0Z3yZY;`AzrfMGQsHL+ab1`kwuCU$hU#|wngRqV6}!ex85 zvV;r`ZixB5Q$|X%BlFR*Up!YL z;Zx(_f&u3&hVGXGme`|u6Wu+_5 zRiD^p5l5Bv4%?CijN`^4txF+;(L%pGl?~LLD+N+eOS1dqpN*C{m}W*|BNMlQr%C`O&N$=CzNNg5}rLyX*VsW~Oaa7H%CUvFkIoYhDPYR7&{UqJ zv?4n61XE5Tf3gI3ve)w#ieL|+DB);8j8{G}>jCVp{b<-pZc<}kf%C+x-_lG&A%BCG z>s$<#hS+X4?{mLtK+k`!5$ZhP#SShX5h6_|sTRwu1FGQnk7w8_~+k@YscZNa}_!S&&I^jnbFKmgnBu_{Sc+ zQ_8-xrpdmw!N_+f88Nt4f#JfkuEgGxgXWeO$_EAT}1w;x|iPx{}JX)g_sU9BEOx3K#h6(y%`_;)&niv)vH9Uw^h5Il`O z^MdOT6AVZ_!zl@m8ea@j`gtaajv5bkIsld6Zb|mGh$jS304lkZ*C?Gwv8V1XA@~xW zr83EVrxyddb7h1CL>b=v``A}{b9&0S;hRp(PfEB0r^%kA>ENxR@KFw73;%HSj} zYdX8$!#4ykZ$+&h$;i^EZ)t>(GRmN>no6yOO5bxKr8d_s|3|gHG*}dn*&j-Xes2Vb zotRafvWOh^)2TY;&Ek!B>nSNLV#T1m?YR0S~%|=D1S~k z$t0~9c{{oTFxx=C(ikpPoJyUgJ(&tvJGpYA-R_Fg#qsCAL7c+il-!Hhe;&B6#SZm9 z@8bV8>_H@z_QQ=f!gz^8nS~U=Au)x&!)_Q>E9_;ZKSHvbI z;I3c@oc2ubt;7pfYK&bLW7`35ZaxJR(8XGJjkF{73Xh|%H8cyg`681)<|ZJ5`i7#?qV zj^7)+{_+;Rud==i1x+v0{Qro0K94nqK`^AxV{Kha^%%+)&Qd_vGGk%FJ{ zXR#LS`J73jM1ZwztgeVGbZA-Ld0`Pa9q+EG>8SHA@a(p21XiHmN9EOE+I5RP|7tqn zM??})MdxEjgo&C@yXZsl;VZ84vo9n#jQ^Fw&leQ&WD@jyfA~TTp0=YkiI|P4I#)CI zjKu zOhDcp<2)L=yl#ov(mUa`0b>4Te)^+&CqMnAgivK?!?DeiDe~&?Xc~WBo2ZN-7%xQ9 z1kOfFS@HZ@zu}5RAnA)OTVxj~NdPM5CIGS~q8|IMg8&a9usyB_6ySx2hxgB2E}h>G zw(sXnH1K@~q(-FiL$6WHJr9BRqQj<3Ay|TEgoT!5?0ROx)xgNi-bSn*Z>O*FqEQZF z6z4ypTWkBfz>O%-W*e?aSn*nlC*7X6M-K0|qu;Tz2+tf>9SV{K{!4I;88vee6e4Ab z)Z4bMv!X?>w|$q_x0#(CY~DLu1!t^U*3@jkE^SpGRpWW;M|E6>`WkIaSu?L#O2-YJv zFzc~0m}b0F{8`CWSz#QwV5&cBegh!c9KN}ZD0TybN&u3kwwq&aN2eOM#nF#aPmpBtj90W+!zBF6I*FhFU3F`LL(tepz$RbMCe$Oe2yl<==651sGAcp zS{~NcHmVba$;mU1d=58a;)b~g;v(@p6zk~|H|Y-2$%OOGO*9yHKI%=v zD;brn9^PG((=E-@b_5+or!`4yAo6U98o*tW$_IG)@7W?vOV$cAM+^DmFT~}n@?xut zdy@3CF*xk9`V;tZ3G4}%MRFiU0C&%2r+4hG zl$KG6KPy_-_@S%k)$I)9DD5*HIs-gfCN(k2dV~x}gqT*Cpd56pM5@B8A|xk&$Bd9+ zYo=th&N=&(eHnDG#kPU>$kkZw#TdfGQ{@RH4!Qj2v=8TncryG66RgwNEe?@ADz)pH zef#(>jOo@$c^sqR(k`9RcbBQ&sf0!|tlcuTI26!||3==Ohfv}(6&WboI)|l|!!2ao zlaBzu7V~zOqsH9po><>g)9%)b)^)N5GhCV7q@QJV-z`i=ujxXW*NW@lYB<+EJ8b@hWo;*ViORtT45xc#NLlx zkjabQu)8N7w^+@m@tDl7X7H?OWiszPgEtZFDE0o?9DOP>6m*X%xZByHmfD^!wDHk& zHOh454>R&ZZvtYnH05<$9w2PPIFgE`Y+^|=Fj={-il<2SGVpo5zt*uR^Fz=6!obOQ%&==pRZ^ zrAzjz-|;IQ{Ax%awk&jKq^G9Q;&N84d_NjnN2x#w84OtHqFJ5gv!h^ny9MYma_ION z=tH|rEkNoX@h~47|8QUL9DJu!jHLZaWfq+ap9}#{khM8*wP4O5JME+kMf9j64{P)i zaVz49h{+)kSvsx1qcqyPS0~Mi1Y9%Ke~bc~($kJCvP@>fr~-PXw!Lyhhc4-JakS{} z?=3jimb_`wF_S_Z94^u+=zzjK=PSnAfX-Fc?aaYj|iXoH48rreF zL?{WE@A5aEORs%_r;$CylA6S>NBvb>=2+i_-U1bg?p^^MG60Fc8g{#)zGW+yQT{er zd9Ud&$CMl;o34sS!BHEamgG#{dE&@GE_2sJiL4V}Hp-Z?fmXmTc!2m+|0JrNd(xeZQYRCnRM^!3Qm zy4cccPXVj+vpb5eVjGp}s^duD6J(pTco`s^%vFW*;xZlJ-;g~#2M0uaOIYs*Ll8zd z>POw5ZvNAv42Ah_<7;RPeC1^azOBo9UL|}gRCh__3O5wQx%n9TaX!!9sk1dy;6`31e35o?xi^dKa!oO(bdr$wh~bB|co!dpib%!#cl^e)SWReBtnIcZ=4-*1(z}F` zw}!gXVGr2%^l;P(=jr2)gHCSVR5+)^2tWPK`+P6CI z&WZcGm+?euxyh&tyg{cD-|7h`*3rCJGm)&8&NV(8-_`EPnfInZpvngJF3q|+h>U7! zJ&Fg$n27m;bU9x2o+Lim3Prr4J*J6PG;wWue5v=UAImT5{EgKrk=5$?+b40?dY%Sh z^{4Lox$_6QP|uF6ehvAN{P4cds7lch>6I6b?k`-T#dLR$@%f4Sw8z7SP>Rq86;SsH zQL9NzE~;-Uq(m%9ENhufooaq{dcl3gOBNl_#VffNHI zAx@*KbCthZtBqG;ZGW5Ga2)aL`E;KS%yczV@`fCD zovTuALp#-FOcl1}(Xu)j@uX?W9_8hOyeljYX@wmgbSCFr$v6H&pCDr0%vWE7qk8PqreT*QaD2qi| ze|q&~(Hx_0PHbU;V}KeT+5ASlRQ*F6x4K&#_t)M_A=+EO(M&HKMMI}Y5)rq#b`CrHEd6`}_L z7}*Z(p+3suHoKSRr>Mu1l9ZYn`onl73reFhlcf_c1xz7)s~XQ|Fu2U1M>0IFoxS#8 zE34v03O@&}a%&S`m-M$7Ve=~Bj;DJ#=rloUwd>S~UP{ZFsse*sOM81+p4U!v)M z%2sA8>)2(|Bm3Oc)PB}B!VU2!1v~SrxMVU@Sdld)K8Hs62vr8!tW76eugrv|6#nw! z;^HQ|PJD54rF`3NG#|mDP$W?ki6h9siIucRi_<>ae_V59;#`y+*C`=}gv-ZFJQAFm z{H*y2@o27wwj%)TvKG*SFI_v#*`kvy#4$%8-oA`nMp$R(oCt&nLD4-vLzLMXfhs71 z`g#l=yHo$O0B=vearFwL?zB_9i@B6v&v+b+G*bmnDWeLXH{#YR>-Iq}{+FBSnG2(xa z9s-h!Y0tLW>z_oJ6cW=@hTp$2N!olzPub^*0ujJ`OduE;x}cpT&?fdT<1 zXOpExArcpW!W9SE=A!P}Wb73D*qnpWm|N1^+V2DrzjtB8kOOeuj)NnPMejiV;8qE$ zxBUBIIcNZ(4j5*LO=OlWiJI2$_Fp3m(nx{bxHf{~h7OaWYd|&!`B-Ska+mXpaun9Vg`XQz!YKK64{G8$%0Q6IypCYiAW@2mqk2z%sS} zS}yKT0Dpi_e>2Gc_d`da!*+lkdh-FLGKE>hhUFBQ2(}(|bQxMlB|HXZG~ZxMA}aq! zcU-+XroZToz@_c zQp`w>Nqk>BlqhEYsu*xHJE4`t9C(=^7J7GT-|$pJ%p@`Y5SW|?Co6-3%4og@>W#@| zZEA0Hns-bFfP-l?=#+feJgP$OY}vOnmXdG$)QTm^daSma)f#v=VCzex)`G|4@2f4FfBlQ9Dd(MP~T5pw+v4OnnZ6lWbsj;Y=Jyob>< z9hDwLMvUZ*U_UZmw?U;#aSZ*R-osU&DLzu9gJ*an2^t5W=JdRI7Wc+dkAn`5EC;b5 zrc{P9DJ3>jeY1N4hpHvUB}|1bFRe-0d_{ohFl|D6Xaa312b zIQ0Y<7s3ku4%g9H?YN0r9tdQKHER+@q%u<7+MjNrvAXOchVx|%&_{vhK_`+ooP=M! zRZTf(woFbt~Q(|XB9*U>ON zeLgFSh5#*(;MJ0^rkW1PYi&Sw8e|1K^^}jD1alh(j9>l$v_z7v z7m~0(Qz!a)44Lh;M2)#aZFYjHqOG<~?!_;Etft1B&1dwz}S_ZRXgi(T3Oq0U6k zWXKvCn0fvfxgvCRj~oE4(3&Es*pt4>3M!f2#fS=$=G0|P)&F=@kaRNCHwpq;v%t&j zI39x?R%b#t&WLXIO*ic{&HxdNz!ho+#)u8Ba}CVU=*)Fl*^@xLz7fO5o#Nb--9F<5K`~0X|NN~pKVINDiAaPc2LZOyI zEDw%2FN_6(AMy(iuxO4|0kex+vY-}yNI_a1!P6Msqk94{ww>dFaOEjz@0h%+OS6YK zL@#!}#b{ngy`D=*?HxDTt*LAb4k8Fc1PV; zF$$)~qOl~oViIl4ctk1_)3P#qi=vJqURhe#eQdA+Ja#A6YxWr12D_-o+cLOWufx& zvg$7PV5c97$`9k0n^P4FNp3^T%4|0>?}~ozGS}Hb!xjs zaI<*zA*21~FBxlMBtzd+jWw&CL(FL=?@RL=HEJhjVIdeqTQ3)vQwnJNcaDHn5U@!6 z9p?i;&o8oa=M%jDeb1yQcKHkPduVy*HmIDRdz~4p}vvrDx3O;tT3-{t_D$_ajbL@#_i+)wMmovQQxw{>rMdJi?OO zVPBuyavr_~a4ipzUxCFe)2Kk0!9c*OoF;TXX~|0Iv@uNA}bt`yndEq#xxohUELsSBO7A1 z+rvE=Q0_+bVQv1$cwZBDDbC|U@eRV{2xrU^iU@*PCxizWkw#fLbkUt9DZ@^WY^*02mRHoQi}ek#6f3l~9g1oSdH>99@0iD5XZUev+r(w-v9j?y0Q>)>H2Mb;kkVJFIcgOjq1fy#Rh7dM#kRc$e#BA!O`7En24;Z ze3IbUSTs+)Ex34bYh1(F)CyhCwkTU+W$7Wa8X_s1;#OyFH-^(wZF&GPyJm_TTu~bO zL%HU5`mtvhZq-&irD?H|M)TXrZw^P_Nqts}post!RN#xNjCT$sNoxFGvY&F4DrQLE zqG6fkm>3)h8>f9J&wsIsS&Qw!F>ajMjfHQ+6;ws0%9H(F5~!7R1QU88jgyI!1+GIb zm7x=qI4aJdNr%H)>|pAtm01{4Tr!}RSFbfv!5!AoZETuJE9&0F^KgiWC#+yeDDW5l zUgyPG5hR*-Yv(Iv>F_cXG8Sv~f$zjlZIw>{4GuhNyqsJ_B;#YR8I-;B#%eUph&8)M ze%{h4(Y2W2Qme9i)CF8n#93f9uBy_~R0$D}mbLp6i5$_j9)TKI%u#&_B^AG#^x>K$ zI6Oe#?Fr`g1M89odL^oB|zy2Y3d{jb{Z&C(7K+2&A7didceNM|*9LWg(T?C-SZ>m_ZO< z_8N?Q*x946fRDep&l1SZ0&|cZsp;XBnYGts$K`Os@w#3yY5=ZV#pB`>G!9sHO1D*FWu{+Y2zBM(U9y{M#;-6?6D}?k!7b#$!40-j1*uwRC#>4*B8O03$&Nr zrihN2sw59x|E)&6L5IWEoUoy7NI`&d?L%WEHWwSM=$d)zff#* zh@R6qzHi}W$#4GD^3OKH4G5yesotz7YfPXUamc8BI6_Ls5ySZNttRuS`?-aPJG^5b zbG_`Byf4Yl68KP5xIbL%x$6dl7*L(;GD^c5<68}x?La5)xuOh%WfvO`)*sxrj9ia6 zwqAdrfW|umoIWl8NK7OdHIH>a>wlStq@u)SNBeaKBLA!SLr7O=2+nHv)Zs@%Yra);aBYGc! z85^D1J9c@Y+E z>)9gWJ+8-nXUFnM6vD&rU7``q7RC%*>lS%KAA=qER6macFvw5ZJ0^gB6DzZe!J5`_ zyLB6O5n&Zk&#$>$iZcio8ynCYR1S!V_2tG3`|1+!83Xsq$xoJVT90r1ePYGlSNA04 z;r*lL=H)yDNp#uv&c1F!(u8?tqrL72dYKn59?YzR?zpHYz8Xsg%?T}_-#=u`+?x)Mu|4i z`#iPofq+jN=(-F~WIC(^BRDd1hyQEiry ztZGfqNYDIt+5K{x{}1${)Nj=q{C^c90mEG=Nh{cyke~^y<3km~HER503m_C@MF>Y0 z_qe(&(HoDz6G=2ZAT0rh$G87x!k+%bx4(D0XcqYKuT!ir6fD^W4%pWeXQ3nY+tJP` zMl$v)2DWMpG|YcI+`1%2^Ymc@3(0D!H?yLG>OB3pj*45dWYGapvN;}n;g;<7>--)@ zo2^$W3+2bAcYJ3m;;Tco9$}Aqwwlu5GY)98a>kZqtCYCsCUuM*{t zE+oI*JT*~EA)!ONUNTWiLEn;WVzKFj{Y={Mc6D&MduyK@gNrPvPW3qehN_}LA9QK3 zMk&W)ZQ;PM!OB+B+!b0EB0TWDmZI$C5`eM+IVSrsvPgBF?!=eq^dAlfCof?T_C+63 z*(EVZ9?H2fTEy5wYt>SXK6F2or}GF%-nO}5#|jpnCZlUg42>znLs za{D|tvJ0xq+-G_P-K6GyA_IlN!ZsvXFmWu8T?l0G9trME-xbewh5{ z&O;p=y94%wZ{8lkTfhaTXlH3fVv%WL4da@Q8_?5i9_bh8YhDa*8D}dgTnSlDx7MHT z!b7yNa2t;JgFzeS**fBSbxIW;72?RlHNBpg(7x!gdv=-WonEC|Qu=U7mn`krd$g?!)8oVMFokfROwc$iqkce&( zvkza^`uz0_*FuJ0p z1Th7eR(XM}! zhDJ?eDyXqvVh--~sOhcZ^W)5c4Za>azqBj8l4s>i!9k${HJ+Oq8vEs3DR(i&Fv4JI zp*})Op(-L`q9G!GX#TV?#Kwg7pq|4&Wi^P!;(%~z+rFDy|Lj+dU}bBSl|-{eokA;7 z)I}5gM=b*dE!j;Qll7C)VOtQaci*T(&aH4ujjK>K{o~@nHOZ?Ws*(V$!tv3#^9`uR zOpkFhnvg0h*vDX1Eb^9-0kAcRh4_i%0vT&+3q^5S#H!P9(9?Cj{ct77eG^Z%dBTQm+b*S375Hfe1OI%0}?(ME)Y+f{-eU zQ3;^g_mV{BML``X{RgEXpmnlU18KfKQS+BXWs3ux;wV|h>VcA|X9GF?LUA@l<9Fd5 z0!7S|ibSOZUAU4_^jhL7q8mEFNymuY?kfi`3TDv=)>XLuDDEu#tbN-juQ^Ru8rs9> z*yGE(wG>>o2KE}=FxACAJ6C#S#fs^)=@_Iw>QFt3-;Br^6 z{UNLDDJO%0SNUU5TxsniuN7!*C-Yy@_^MJH!|D3i)eErB8;M|hzk;eKY?W3fKvph6XsP}khSRj++BpF(zU3_?lwFqqz@FF z@y~Ih&#$5zpv)l?g(mY8BV$8^3Xigj8n6tWfM>r@W?dv3c?w?C&+ypmK84}UyjS-Q z?QvmBdZfk-4>7rj2^H9P6`auxpQ*xad6e8#tHy3mL0Uh`BOr;=BWcKzAA>`J2x1XR zVBZ=AX?c?GY!2nJ#Y{1O_^N+mC(T)+9Kc5GepKwAH27+iR%15{d%s*2Z-TNw!T@&r z?yj=24fUejb=RnR2`Cz?dFUEDIJ!72*XU5U&65_9(6x_mQZ5HBf;NeB^LabxlvXrpB4Hy&p#X$R!6rME!v4e!K@u8^FFIXK*%lQyy_zN%ZE!&kx-zmok{hSDf*niGaqKeA|{l*<)4 zDl(N8vLGC$S6MfV(4k`u{#*CA!*8oSun1xo1vZZX>Jzy}o`~5AMk)wc?h*DP47=G0 zK1NwSp40nN$$br5_e>wg1zkU)Nf}{!-LJ^#7}1MHix0QA@v3C z`)c9H4i8lC`-6`UnwAgl`}?;f^XK_&z%=MJ55fJbb(&YTxz3nW_e3ljp&{a{x_`pU z@szIV)n>@4kYT~si3;|FZAw&=&+;Iz5q81^7s+Jq3EBk#Xy)*iIq}!`J z1byU!+CYJ_l`#bG3HwuHvxz=9lX=_?3mm5KFDx6Y+JkZ8z-m|6=SM zf<%j&EnRlqvTfVCW!tuG+qP}nwr$(CZn>*FdeSdC-v1)bU=PmdM4ZgnxmK>vKY{oA z$Gi1afF{)CybE5x=%j065(lF&Bqa!2lwbv)SwjOf2oyqMl@9S!r(;bfO1|X0E|_H{ zVtRVs&>h5@;%(t_1!ZXr`5%4GnTDK zdsIieEVF$Jb|XqCD=9E^y{ti;!22bxEKoKSLa`@_LW(u&(ZGW5xO->#KwT?sK56t} zLvkclx97Ci_Ho2jk}buOC+YbEek%vW{>Gm5kwXcPooAbVoorNCrv#han2;ExkpM)4 zDkL}{MPv%mCanN&>e;>7b-CL$L;A5huoTd)I+`glbxl_*Q;!_#P>6IZbZI)4DfoJ$ zl1;aAf z?QO_AlUtX$iJZ6a<$*_c>9{S#(YfjT?W~3yj|iC%nF4{(ibftAB8m{5F&csSl~F?{ z;WTr)e|F?x!?Q2JJ~uQS)K=Tr7VaZa^i(~dFbhKd2;pz!SY~KgCOBk4jyNb(drYz z#MXu>CqbTfE84L`Zk_nY{>oIIkP0cg#@dq0o;NIBcPhNv157g6de{+@oyBp4yd%h? zX$D-j+>2L(_apon?WRnc;g^Dkwd?YT@Gh6+>WlIX+8h#k*q2#Ur0V3xW^n--fZ;5F zQ9$iogtn$J4uzpx{H{I+xC+=^(XrZZsMga(%{AF>bld-gr&FQ+6io(2Cu|vLgZOi0 zJ_3z+gPWHL&^M4r353N1sEKesRmu!oh8IxXwyyU74$61Y&AKw~dn;^jd*gSF&)HGk zqBdg<{lQsK)nrRuyyM!nY=v{Y=tj)H9TB1wuBn&LR(Z@s ztgsQyUV9aMj&YFS#V6a2^~?4fVN}r>%o33_sZ@YMcJHj|H<@PATJ-7>AaCZqWsujT zbuwd5CtBwtt+y-CKl{HPWBC68g6(gP`1rnU9)C=}-uh;S`bt(bxX}Jf261VVToMTi z6wy1Sbq(4GaEvTg0trUcD}ZEzAushprZkB2?k~%hM#`izzJzH?9mfSTk!{^^I>~*r znl>J@xtekZxh68J1->qCzy`8PjM2wp)%P^LoNWs<)vHq)a40Shjz9oON(xbguhCxY zvR!-e(7i#S$~r{8S(bSZV7*anGBcbHfdOCYtTx6KuXcHarpufnSCwzx KLb@DOck1kGm^Qqdd(Dbg^QBE(d$fG~z(@LRe5X}ul4 zKYuwgcWFKDkW)3UKUcqVL2lJR-Y~lWjgqJ99^P15aNCvXsAu>L7u5h4BJ-D~0vI0^ zNp@86nZ9v?YcJINOWpeUl&)1fQfrIa>J;~FRama`5qmWV^K(5JeX?nNusQAz_xhyg zGZeTAaN5b#D>7uCwLQ|#)XfNUPZyWASpw_H^YuL|uJOqoQQ2F&<^K^}rp^>ew)(YGn> z^1l$W6P?=x0f!}I!(5vL0p<~ILqS3kLK4s>c4ciSHHKC7I<0IEJ>U_Ni@0L(nf*>@ z#f{pNxOrPM@i&gZjSfo__uP148MNlKvC|Ksq;IVkbAr}>cevQMXo*7{+TF!!Z2>DpHi79K#@Oe~`n25M~>AquA0>S9GaRk7Ap@5e1^upaCkw zT7dy}2Ra~HGnO|*!#>>M;O`}-&|Erzk^)3lntQk6-w?4JVA>uR^Ao}VAd&@uh$0D? zR=6ieLSpH6*BK;-n{$uM^;?FWuOz^S!B)GaTC;~$hE{LT&~K}CJ<7knMV6u8D2fW} z6_$?OVZsJPBtm+(lqJBR%8%)RlJrWH#q^8@#+=>U#hN-?{VpUKRjfm}7*;s9f$3LX zkFoCubS8;A3WpbL&{*WdGEh4k%?-e!cd6zL;De!n{KKM^jN;=*g6`Z)!gCLFrlqkQ zpAWfXZt^&lS4$bEu3m}B50A~O)@GjHSvS)iHXX6qrU9zc_R4q8_i^s9-1c+0&D}Z$ z#XO913s!#Cb)yc*CNW3du?Le8KL=zSjV5X&a6VmFhfAW5)-(kDU2O0yJ1;HdKt(L-bpK*Iw<;2&<8Suh_hK><%K zl4iVGgGMgqzvzL)a)Mu)0ZlI!oiU~P=@HnY`e{R0$rW*ws3_<|pvjM6tkN;B8lc9< z2sxY%F0c*`PLgH(b~q(P6&sY#m5d8kkR309Z9#&&g?+2c+2OSAtG~eFbb}xDMm?8$ z4w>oqPF4-&>nXMhCP8j3o$PH!ocrGurh{gwo+a(*Xl#Nz+0((6R#9~kW)TR36VI=L z{qcHYK~aWJ3XKyccTAhAMwJX5=6JhdiTG%2CH4>I?x&xR$9Q;Px4v)x>QTUG)7)MF zyK!d6cn}6T?C%R0l?d|$sEG5W#Qb27)byeHfYE(!1FdkU@04*pvL!sRvm@V5sh5X# z*BABrfw{POX8ZaT>wGxc&z*r;0=I!!%z>U~SX-k&?D*x*zAY^ComxOqj&D?h&`HjU zanD!>$Icy=Mh~3c1Z#G&zjYLMAuw$T>0Vn_KY{*PP(jDGwc>4EIw|u8H-&q0am`3F zouJVkkpJQsg(WmA%#aDtiV+ENO_hI;NQ+E1wRyn)P4E;qya$_u`bDzpLIbJTc6^X( z%y?WQLSAC|eT+%POX8Omby?SHWU$(w^(-AKb@&R6P@}eqQropGYbbU^N+(4>y zP5qp0F95QtfvDG3lmU;6Ua=A;8hwdT?3${!nS^s0#I2#haN(f#rO>Q0uBX z!t;Ul|Biu)vv3Xvf8BEC|0&z~|Bj%XZR~!POJi$0D}5*9|LE4eR*{a~#E0+x&34vW zsOcbm83}J}=x)u~(8-YK7DgX|)PO-<%RJgeGdNWgSF%>UFs4h{pU%XAzr@GvP1Trzh_1_5B!6k(&q4_xd%4w*Mn5_ML;WlE!fQ$2M(t*x4tg+H8XY zw~}(`?Z^(3poba6-YZDHJ#0rFzlWiNzPel@~tYCKZY-TvO`Qf)T&S`3-qM(^~*_kQiY)_l&TDhzO*cMbhR@#6dH3h zRv$z5jhs?2C2ACjQz~Rpd-8>0ZQ_5+&0$GvV0Cd~%hjn@@Vg$W}fb<}3p=OWBDR-Gw#Ru9#=tg3Tm zg6{oCa)R$0f@-Q5$~ z;#`1R7MnD^hE~Fc*8*SXQsMw|>;*zhX+s^6tl&NYuzipN2lj&TP_FE@#4p<$Bi^m^ z!R(zg8T)@XH^`x6H^`%{+u_(dDqn?H4Rfokdkz0Ki5%V5uBm4WOvJH<$}VbYJc=7Y zH}Qf$PkeB6;x=`(wO(arZodKk-`@qallQyoS2d~pRZVFByNRu#mA<2+v5Wq%T4Lzr z_CFE(QzsJ+Sdl|szmO?y#Vi8o=Fl^NOc;`@NE^i!#%D+*NAoPy9mE-7-QUGn_3SWR z(+Dcn{hesPoj-BJb$qr`v9I~eNrx?v-}WK=@e>&9)uX@veWltn3T)`{Go%8k5D|#t zt;$MLzJ|g=7!?NVT(M}nYZ_Jv!*})cyw@t~R!GxB4wC=7T%GNWr%7^~3X&A+6I}5n zI6~73sG;{?`Bq1?%7Tg=Ye;C$`z^P()H9KvL$Y6En!E%}+;4K^@P(`)hSmC^?kNb3 zvy6xNXrwY=aQ4)!EIWM@A5Z&%p_C-k6O|WPObf(aViZ+1&WRJojFWp4bMK7&Zqocj zpuwKp(`@Pu)s!FjoiIHQK~F0#3$2cmdq>+~n|826G|S<($|@1pb^Z*jBK2EbTq$xV zd7QmCDln&8D|gevrU)`*vj|7b;SNyD)M5c-`^<#mxPt7UQnQw}xo7RImgwflg$=B< zo>V_&{T1|~P%Bbv3d}#8fhZ#&j*|8!ODNX$b62)wVm&alULAZ@tQwoe5z`(RS}>fk zC2Oa|+UHn?HR#AKc|!FSsT}Ba^7Lu;d2EEC(^5wXxHdt5Fy0WXl;~AA^r$2u>W;<7 z{p71qAP<~e0*Kia48;vv_PfN{-CyA2yJ)rH+{Sp$W~69`dQ6OE&bL6=b4KFPvh<;6 znw%AJ%7O1#t)Bo~PKk4b%`wRcXiWBk-IyH;C&Ftq#D@jGXHKdp(gIZZ#|0w0;0@NZeEfD;aAVW=(G3H-c^| zcOUVtNA&a=jKd_o81Qo}j!@e3+E{E`FlrjtinknVURe1$|KU+xuxL*0i)Qf?Xd$qe zy9M8H<=2tZPX`O4P|joVAfh~@u;$s(f8Mmu{EON;&SEnT*;+#0(3jOZcfKRy)uG9I zP;xiI+OZ_0vYM&Jwa7If%VQtf;TaoTE4RAq(JOGgtqb)!+11mlYiY%Q+xv54S-EaY zeR`dG@_znbymv?(+%<;3Rv=Gy00650t|mC!nA_SI>RVYkoBt0A!gZ#k?IG*0i26YR zq78J#imHn^-1mZudRBg2teMy9C!49J&iEw8`t?-&8HVC&NJr9+_JO!|WcGyi#$-CA zUy|Ef+ps5|noRSsj+5or$#>cy$ErJZ^ZovQ>Ft50#4?LA8=*CO^KfwU_&KPAva5^A z3AeTE{(RTn&}gT=dKpUY;p3CTeJCzZO9%oG_5J%Ui^H(D9Z}c6DTNbcCdVV1qq;Xj)2==j=1(Q=mgbJ>EE1~x z<P)QOLUezXu|Q*h0~*gv5Llyn}0`2c-LY#2Lq>;nJ=91YW9x{Dz=DDBHx%V zyg1aXO7ooU(4VD4$*WmyV2sUIMRf|1yd_q*Of=6wKKNwYMA|HsIu>xsb=`qA~F84OpBSlT=dayku#KprC%EiT` z{0gNF3z$){888N&`aDAC(^23GX+5jo@Lx2*S7&q>IX%Zg9l43hKd9;lrGcvH>5)ki zB!Ec7jJ8s{PqY$Nv5noQ`>1Q%jol$}vbjC@AJ_xniy%ngEs2XFn&HcVh*LI*D|p>a z1#KL$9W(P8THA-U(uxWfQ*~ng{%K-WDk>L%7f)e~K#(wwA{m8@@a)}WH4XfI**P;` zzO`LgJFRa1+%UB;7T?c($_blnh@p%{h1(be(wJPY?KNkIw8V$Iwe_LCv%0lr(*MH) z3r;l#57h8*)uRj9CrZ+vomm7JWTP5|1|}7t4L>Roy`twI4~k0mNNw}k!7DR*Zz-0~ zA5?ZupF4+t?enL6<*fUPH!Okr)Fgrf)jqWEn4q4w%}OtLUv3_)#+C~Nc8iQK{n?S0 zrgl#oVBLDwf`jbELixXed`**|=%ciL2`Toe$miVFaQGth#+>aAp`C7Az!`}`pPl=dhUUC!24&x@E-jF5Yi0d=0K2s z=7?DRO~RPzLqMPd ziX7%Q%wi6SmHl2o?Xy{+GjoaTZI^}ZH(Aq0Vlpkk9i9vzyY=dWB*I`75*WtVm()fg z`q@d#Jsrg<&V0G~>9l_1GK)`mdyyuF93k!jf=e>udMhXk}CSo0YVN*R#*D1 z_E(f$=mr$%Ts*9+aLTQqYhS!fsnlz9W~MCfv{%D*uSY6&BG{SSh*Ahd^}wb&x%v|r zf4pUyv{XPJjyQ2eF3%4%@J?|5W4=$cc0LmJ+4Zq-L!Bv2re4`5THd@JE%kc!`>JBx z%?N947z8?`ns#j*Q_aA@pMI<=s%73(OucpIwAHRfan&{MDXs2G?p87MPJYX2eNjny zK|aa+^8v**Bhl>(lEY=*^S*Os=Kh1ot;l9;zr8fBnswU}+uKGUc&bVMpt4G_cCQj<}$yzPr5Is&Pwobe5x;D2^{ z1LpjSK@l&%F09Z%J4F3kur^@U*a8VxcMG%1Fdn0BQrG%yCI; z*{M#5kM9rm50kUp`3#QKf^BRWb{ko8!A#9tIn6?GX5*dzq0Fj=S+&tUp*QJLN-@Y{ zI&QmBi=`?>H{D2wrC<(jc-DVt@50W&Qn)N{OOsGXrCb`BodqqV3po*4)u`DCvW~vY zE;n}2+I#EhX4b0gHfU%k;g*5|qW~3$O0iux+hG*RAEE}{z^JCd_<_@ z0r>K+pym+CrR7LR35b*^Feb+?gex$ju;jCgT5hIqKYeY>XSDp|J;XrbvuOAR5kZN9Rz%PLkEe^Wq{$sjaoIV3UarU-(I=QJ8u1W;&kT9v39na?HD<}CU%NSQ}Pdk znkx0H>eo~I6?Ap+g2lM1l%k`+JVsO&d}uuOZJ3c1s%SE-hBn;{PAVz)B|M5n2OMz246OOUOaf!)}t04DF?JV8MCZB0p z87$?f9+vd#_I_M4i?P-87NEBqkcPN8*}+F&x495niX^96=d5kTA8xqO z6i5|NkS^s@4LWm9p%I22vnvnT0}@;ID}tpDN0l0<5wZ^nQkg!I=r3QW5eW55GSi`; zO#(G>&91U}UMgc#pTMG`&M+%xv&SW3w%hc^Vg)(|0Ialwl{YlSb(Q*pRt3orq zC8iio530?=<8bNy9J_oVvh^M3kfIrtu?DI~0e&7BYP`aStQxvEH`+YnOLUB!8qZ01 zg1vylS7%Fmmln%jap6_j)())2uH@{YlU1G!#?va6ex<<$k2UYrmHN<@`uG@h0*Wby z85ok(mMUPb1G<_g_Xk)?7u}7<+cUP&Z2LIX)bxM@7=#;@l5E3FRf#IGraDz!l{Sl^ zLpdB!^53hf>55FBD+~_Ny8Mhp1l1mb$Zd)Nz4l5)X)c0jU5_%||fN0&N_iaY5 z&{h@k)RS6$D-~O8ADIWn=C1!}8w_k9U6(^HXzPuAZQZ!&KWhpZGgR9gMT~63H8}m@ z58EzV-Gr*PIcyPNc~#*yysO5Kww6Ol+{m#+640_c9D4K~&PxcS&Y|k*C?)hr{01g3 zfg>Jrbu6k3YCHTyG8)QRq$@k5Ej#@3=d1%PAaB8Mx`HqOT9e+MBlHoYaRzyTLtbjg z7{smHlJ^9F_hC1{yz{N%@mN6f+Fd(mb9s->zR?)_)X_Bs&snB0s%UQ&5JB9v-==@+ zUg`%3zckEhYq|CiKVpC1EDoeLg7w_edn{lD(I=l=h^3~XoXY-9Ah4Q%w^Edy_BZ#!-zi=ZCAz&Gd!*Madr2^!{sy8mdy8KKeP95**{Md z8Z!Y>u;0A7%#c-4=<3E>S(%&L+uYq=589*8qvJ@lQnKEz&UdvZp7;IC@gl$CEK=s> z#3bUUXL)K|6~_!*=_g7i-rXc#-gG=sGF~r3{ckrfZVoi@k#oU$ap{=YN#FXlf(Kn{ zzTkObeeh~d%JT3HQ z_KYm0!JM{3&La3~lFXBik~YK+qQF_>$q(*y1$XB0Wb+g-gv5{)MBngj{a#s8>j18! z0|LO0O40@Z7OSGi!bDuTY3AF2^At6*2nzY85gWnvXTTE^G|fadgcRn zDPE_i!>8Q@dn}O&VTbvfG&ILF`<;jj3x~tgrkeG{M?3ElQ?LOL*WXc z{MJ7ejEFd44^dNaLGHA|Ingm1iieQlB^dF<`%>o~LDjwx%{+&RP4{y$a%)Xdf*(qZ zoJ=x=3Dk=+Zgv?0;W6c;BNd1C^$kw?TBW3yxhUQmAgr~}PAENVH;A7&P zvpC@Tezhyo!cas|AB3M}aMAMVE^=PPXS{Rh?L;_HT9rmGo6VY5j{_kv`T07E*A7C^tO zd`^OHLy5+drX5RElZjp6VX9#}lm@I$9pf#C_DbgPq!P6Yq^vx4@bhobC@)Ts*U8=E zot@oZTV8X#s zQ~9}S#^l1Ur82WJ{NvN@%dB;mAkD>-WeyrPDQ@_SqOUDdK=(M(dNd;Q>*#y)hzC63<( z5ja%$Qtdvu)G|M zvb&jQkVy_aHjCF_jZj)+5RqhgueUL6ot+B`LEjdK9KOS%q?QosL`OFE#w^<2GEr9F zI!(43PpTnR2&|?_pTV0QxvK`nd`-@aYBNz;Cm~-;EOYS_GO2c1FmN$7Mm2`P^7kt+ ziJt?2ctbA(P!9-Mn+gKs6OXf`dl=Srw)`w172ZZkbW^@qxfZklG6T4xL18HZk8}CH zTpfL0I5ZF^sL%OeR#Tx-=mG-AG3R9cj4Pr>T)>NT$W0B(wH3`gG^NFSG5>YXW+{FJ zaC9=qv8FR6#uZ2@3Rn9xpG7PcTz2j8Ug?}G>EZR){p~ZDg@J~!pA2#t8sYV^qr4eb zukCLlebAAEO59kGGRYAxsB(7}!t9E4r)amgFHgIdV>7txm#3eQg$o#D@(^H73AAuL z1PsfOzs?wluJsiM4)Z*0h!O*nbtnjJL&id}p5Pg|U79l4_?w=>1YytEvX4-{<#$Zc zKmQSxD0e%^zRRPTaX8<)yDzF%=Ly;ZxRX=%^oDBsY|b?6<|C&nWz9x>(>h%hX&%XF z{|^IADp@7_k4u!o8j0SK3aW)ZPLv_Cg)Vvtw(LHAv-am?JRt#r!qnvtnx1|rAN2|Cmsp( z{46PoUBwT^4JTN}CFDZT!+NSX=fV97MIIf_t0KX`CHnaVHyNsXkGg;4r?SMy(TpXK zqP&{Wz3Qz*r%{xN=$UJM(ldL5^3V1?^wlCG=v(hvUmx{6iiTT#(+aG*c8|N9RO)Ql zHUb(7iu)A{B~y->GA~%vM#qgR&K=#gH^#Qp9`hFD~Og_OCP+4c(i- z*$hqjeF+IY$?cxsTM#GK(|hi3aQlQC8wF>`^W?uN0lbE-bnC78_|04&-LUB;_PknY z9}ca#9+frQ&<{II?GT>vd+rG?0l`Um2Pgvd!BoshQZ2zn=G+M?8zjN=m*ldVobKzC z*8%bVu>vQ;~(y^7C1s|vTSp{ll}pgi>+y&pIg zt+L?R7Cr(vO_c{_m=gLf;*V78r08v3mr((_<#i!sU_?-sc5Z*`>Ow--5rSFR*LWK0 z1_D>Y1~9QXM-WobL=!WL4-%e2(q&mavdQjehquqGHH6^q+cm*h(`;;OY9dm0v?2Np z!B24FUENp8c%4*b60P`=6#>eU10c0Rc&xwS@a5i`wg~+7m?-ss?|k7YV|9@G*nXO4 zL7k-{oGY8zc97x-b>LRa<)#p6+xXsm0EYR3{ERXR*<{(t7dzYiOHy2_ho-`VKGV}d zQVusJiM+`)AN<*bX@H&QY7+1%wr3$?{4pgmWKXJ>O#rrwA5+$N@KzaMnpRB~Um+?^2MZt&9q z7V;@exwO?Z`daxHITEuJInbz5cKJxVHcdI$);vrb29~XYgHJTE)6&*ewp)Mf1Lz}r z$TCnM2T^(&csoXTB~Oq@&maMfu)a1?{%d(TPvk{h;pRTRZ}c&|!GDJ2zDmF`OrWO$ z0KvZyW+c zJECQPrCDNefI7A$qC#z>2z$V?Jzs(oTKGI!XtOFwf4;9kBMbl+;RJJ-zV1nXVR(Q? zXyAs=Ut|8-2)JHdCa7Hg``F2t)|(Ij<0DKf?}vK{@u@qvFC08LDx`nUlah4cP00D~ol$ttao0g1;9c$JHI zna9l3!PrM403{LhDCzYyrA=9YUR|m215L(9(EzSyJLYNC=VjUc<;3JGFv>hN0usOn zBi+iR=jT#^iW}!KRsv$w4}bjn1Yi?@niR74sNNA`;It;5(BJ4(W?q zqXM-%5jpl76<5mu#?sT;LK2!EDP_!~HRZ(uJLAYEz$BU@g>V3p#g~Z(NC1|3y?Xx4(d16CX zm(mx_mGyN?f`-;1rOic4jKgT$i|q>0Ci7KO2}8~jnRW4KUq(@h_XX8|5#6I!NA zy}3i9cWa5OtpxG|2q^V7wArTBn;P@_*fv**pDiVPhWI?$={Z9%@@!VQ5$`_w6zOX1 zrB&4glT9|QhengzXuIN5cVidVN5|}yNE7Ui>_pJKfDmHlXbwILRbG-H*9*4%tE#TK zuVUPAW8)T4QQFwj$y1Z}9_y!{JthU?$3f84N5tw@6B7XZWvj_8$eXr+3uzxy^v0(t z`ll&cCkl-{gBuN91WMzE%$D0=rER|=k#IHDmxj5zx5{p=Plv$L_kt|7)3!d8@nFZ~ z^|R+NOc&E)E6n$EUlU;!W9d|qV1eDW-GG_-A66~V6p$6%*@SrVUt3j zVeR% z0r{hlrRtd$Te`?XW+Rxow59yXNu6dT6WoE6h1IE*CrACTgD+=`?aHnG5?S+_#LDaa zdQ`!t1Lu=ekNID-yMOVx?@15eWR+a~?|+Z~Ot(*8P)_9(x4Bl`iEM#?+}&hlGvlq( zyAxtu4`T>6I)I*j@Sou3=^0joppag*tlDi27lKK%Lvx9Z?dH>Wljj8K%u+oqK5n+W zGB5YbOkg%gm)dz`(NgR(&`p^BG=&YnB6Xrjkd==9DDwQEWqsLNFv#|o=vcRWMAM)w zE$MhPU`>6mUl(}#-Kcl9cVECZ=iLDR?vVCZ^1P5A+lkrr_fN0Qng~CETwP5-^O^Jw z7of^hSz1Bc@QN*xy^dmx0T_ zs|^XnslhjC@Um{ahTEyD9yPBkYN>BVA_eG_KYF#G)q6sH!za6gSnv0+yu3HGAUoLY zL+bA&?0s~<+4nHN#`1y1fZNMw^-S#*24gK%l9|YHao13(g5FT7XRVe`3MIpaafa>J z*H*AZ31-WZ3b)XZP{cLQkyGztV5P`OYsqC-(FE(+p6t0_jq_tV5rM!m_`opxE5m_G zMjh>jYl~Lh>UmPZE?n2D`$hQ3Ael^RegvF&tT;mmTwjOLKqvR+#vC2Ezt&Jh2Qs0t zZchv&n}E!wJF;k{&5A2o`i$j^t3|?`-f_IRKh;G=A2T5YHw7V;mUgK6t&=e~hq%M>>lGrP;*zg0{EW3* za=1ITUAL$G;q2M9ff#pt=I3bsJX$mt&X(8hsUzpxV%kzXcGf%0^JOY=@x40v%c$mQ zV6yy^xCqkaFAs0;v%(nNt(S*)gco)?Pc>@i3S)qbST8Gf*{=fwF=`3{Lt?KWJd9E@`NJ&)+i z$~hLlUO!3zKXTb=P5^xY%fYuqwUW*qh02bP1!ZSAQnI)&QYAm1382u{@ucnUWCa&E z($P(|iW~ou1-}Q<8P8}c>*MK3;Ls$huGIr@3fh0mydyAiw(DN~*XF5@ghbf&2g(09 zZkKcno~V-K3Uk9KNs2^YxjH(~7Dl-%a&kOsJ!ac@s?HO6H;3ZVi&i7Y&GcuP**s(8 z!B}DwVvCK1y@^T7XNqNYq#U~M7OdCjO}`q76y8v;J||}Cb=}EObhdB8r8;k>oici5 z>GHiuv%5#gCYfw-sagt-E?Clyoh!Y1!J2tmZcEdn{;dT?Eu}G?IGVHHJ{DlZNvBOQ zQmle}bNRg7o}R>>`r;hW^mx0E zBNXiBT_rY%nuv!aWqYZ*BY*ku4Pw-HNNS9nyxe>}{dIa&zD!N3K^dEWc*1Ua3)Q^3 z8k8?ugUHMlPzGW|DjFsXg>D`cZ+-cwuM7Wt`@FtrqWxWu{o2bFy{|hOFnaV)sL3b@ zhU7ju{rN8-VNBf}Wv}17AY`lmz?}a-UM9!?O_?{b)z!6guyrvvGIsc%SQxa|9X3Z0 zy=PUW?)N>yseuBwtR_ZVRW^;Yt`%LJ`Y7c(- zA$mbZ@2lo7IaB#+@L(rjvKU0g2?j$AZ-km9pHCfoh0S(EAI#j*GS~CNsu0=L3wH42 zCycTJ5d~xFSrJx@>PPFwQv|tE6A16frIjE=NpVgc#ER^A-EZ%g2nmprkMjoNiD}Wl zu-kMb(%db+{LAv?Bnf*u6_zS0^4XDS5K`H!2BYzOJ`M;HoYF;cc1>!duZHCDH}Yu_ zw1@q2FBevwV%Buy7ofBY$YoZdE`&Q2ib?L0Lgx;SDT<>^qg3KpNNFQDSC{c2Que4S zzcyTbmm!cBYc7DW0dFqGmBwb9RzZ{P)zccVL* zaMYq&bGd}SjqB0nYE(|NB?a5M_+Of%)Ob#Jd)At5D}}Qj)i*Xge%?}d7y%cW$-`iZ zCo6u3f?~dOzGG_IxEpdO))m_o_0iVHl?}xNcEFu)m#r^O8S@ca7o%&BbR`IWLX~aa z9Aigxq;hw6))ZO#`W+hUnRiQb}_VZ?T#C?3lowG`@8C@G>M%8f?iWZ?<{otg+m zIKJ{7Dos7?9Na~Y>IC)%mo1nsv3X3e4i*m2+iXuXS zMT4{)72zkPWu|cgD%|vwbq9uYsfcj{LQxbCJLh`8t}cFN zTH|HL$zeq~x6?YPTWsNdo=gqg!5YJTcsqFZy?H;weSpFN*TD};?#Uy7sI{o0Gvm|~ z+l6i`VZVho$&B-OB4XU=YO#584;&{?l$p>_e`=lg-$X;#X@0NcbA<(9#i?a$z^XDQ5vITXNg0es#(Zdl@b3XFDt{YHiUGDF?03|@fMWc*z3#ga5?Q$2Hyn*DYOS`l&yHCWdpX&1>lrZ zWxP+Bhho+iNiZ!x`gE8fyRVcof*UB+T)>DPjDaTHG$15XN~n_7BC8`s(*{` zq%vg(&^%%nao{?>69YJ}jp?5;10Hju=V?WX-Ob_O5#13@>J#qi=zR?Av&MWD0$5Ps z#MVK}bMjLamB`6dK=$J1T>zJfg!2G1;sN}EJ$|KNaXnDhSU-=T13%83rhJU(u!g;I z**ab5>C9S=j(>NTaEZJ--!J>ybhl)@zOVbfq%gkk$a6<{sM0=2(r_nC95JUFYef2D z)@X{84q>JoIfE1++{oTuCENNK&enBJYA16XoZ|eD>OQ{=(Q? zHbKX(j=+&}@Jb+~e%d@5t9ahe;URzEQ5~cvo=VRd61T$AeFCt|Q|*y3yNSmUNhKfk-wB1 zD>8{VZ<3rqcRz_8Kv7Fjqq-3Ufq41{0`K2Lc;do^8$;F(|E3m(N0hhLV!2gm9pm~} zk_V6N4;A;I*pJo+$AthQDP@ERtOa2QRyF~>l9m5{E*HWq*%J(1Qt!_VP6}!<4laH^ zQj!88SO=r#HOcR-e$i(MI7v~73V!N*%q3-ii_1JHv5$&&4;;$jQ^Gt~1^x&3#R&@2 zA|a4;x{wm}@PsJ4z0xy79(AoeX2q6S-AQ)vnyCV8h*e`{cSG$CKo9O*;8=0U@oBaR zlZ-^Hrm+Zz_~N{qRC5fniXh^4b+nBGJ{}nX2Wl@60m67R&@2PVparUC>Z=geVghF% zip=S3y0|!Y;x9;ZX19Ah*-3I7?Bo3ss;%QbEP+lW zXn_TM4YJTm)RCY?*LprhaZRxZKzC(%xQ;+2CL!3P$#+BYLO`SUM3F(+mU?iY<7%?5 zPr^r}&oX=xnk?A^MW;{?J#AJg?``{U+V|z*(`r_9A!>jQB2Y#dBM9Qr1GdVE}LY)y58&`zEfXqqZPdiHd1dc zB2kc*Gd_mvf_*_+;Pdg`f+WbWKk{d{$KZI}`vU;H5JW`77hB20H=bGKtI3OcloBMZ z5wzR}2K{aQCDX7ZR#StShvd(obA#qkL*_Xd!X?uxe~yc$9w;#vbMrT#&I_p30dkiZ zpZBS)y@Aa;zR4=w6l*Qgy)ybph{;SE1UTI@S=3zbv?Ikr>$m;6ap8PKalTNZ95*A2 z9Nw1T(rCyB30klx3-pgX;^{V!tKR&hY@As{ef#;Z+buUr^U-ptUh>(BZP?Xc;xsgf zJ=GrU^aF5m@ns1Z0%fx<(t=q3IHrvR2c={8s+3VaiM3Em&81LsF^38}IBGZ}jva+h zHE9^tN-`AhVENmV*Nj;m_IwKO(2Y0u-0t43=_vo||rxQ}oRU(vMm zkLmTZWpY)@x`516al@o^N1R-mrKf14eZEU+Pnz6UKs*;3Z?=opWn07j7GWB-5zuG_SAI{>z+IOXtBw-rr|FOU=z4(ltf+O#?47`0sYmYVeZa>v#YxW?rNC+T`{9vZnE_umUW8_E;WcIF7A@r>$rI6MU`zHmJpK zg(ZsmETq?r0WR{5=i~chviPwm#93hRHvtP`Ya2pw;`#X*Z}&@u%0DpyUD&He38@B> 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 literal 0 HcmV?d00001 diff --git a/patch_directors_cc.py b/patch_directors_cc.py deleted file mode 100644 index 0bc3fb3..0000000 --- a/patch_directors_cc.py +++ /dev/null @@ -1,80 +0,0 @@ - -import os, shutil, sys, optparse - -if __name__ == "__main__": - - p = optparse.OptionParser(description='Patch calling conventions for some functions, so it builds on windows') - p.add_option('-v', "--verbose", dest="verbose", action="store_true") - p.add_option('-f', "--file", dest="path", type="string", help="File name, without extension.") - opts, _ = p.parse_args(sys.argv[1:]) - - if not opts.path: - p.print_help() - sys.exit(1) - - patches = [ - # text_sink_t - "virtual int idaapi print", - - # user_lvar_modifier_t - "virtual bool idaapi modify_lvars", - - # ctree_visitor_t - "virtual int idaapi visit_insn", - "virtual int idaapi visit_expr", - "virtual int idaapi leave_insn", - "virtual int idaapi leave_expr", - - # ctree_parentee_t - "virtual int idaapi visit_insn", - "virtual int idaapi visit_expr", - "virtual int idaapi leave_insn", - "virtual int idaapi leave_expr", - - # cfunc_parentee_t - "virtual int idaapi visit_insn", - "virtual int idaapi visit_expr", - "virtual int idaapi leave_insn", - "virtual int idaapi leave_expr", - - # enum_member_visitor_t - "virtual int idaapi visit_enum_member", - - # struct_field_visitor_t - "virtual int idaapi visit_field", - - # tinfo_visitor_t - "virtual int idaapi visit_type", - - # aloc_visitor_t - "virtual int idaapi visit_location", - - # const_aloc_visitor_t - "virtual int idaapi visit_location", - - # area_visitor2_t - "virtual int idaapi visit_area", - - # highlighter_cbs_t - "virtual void idaapi set_style", - "virtual int32 idaapi prev_block_state", - "virtual int32 idaapi cur_block_state", - "virtual void idaapi set_block_state", - ] - - path = opts.path - outlines = [] - outpath = "%s.cc" % path - with open(path, "r") as f: - lines = f.readlines() - for line in lines: - for patch in patches: - from_text = patch.replace("idaapi ", "") - if line.find(from_text) > -1: - line = line.replace(from_text, patch) - patches.remove(patch) - break - outlines.append(line) - with open(outpath, "w") as f: - f.writelines(outlines) - shutil.move(outpath, path) diff --git a/pydoc_injections.txt b/pydoc_injections.txt new file mode 100644 index 0000000..224b348 --- /dev/null +++ b/pydoc_injections.txt @@ -0,0 +1,214 @@ +### Processing allins.i +### Processing area.i +### Processing auto.i +### Processing bytes.i +fix_fun: found info for visit_patched_bytes +fix_fun: found info for nextthat +fix_fun: found info for get_many_bytes +fix_fun: found info for get_ascii_contents2 +fix_fun: found info for get_ascii_contents +fix_fun: found info for register_custom_data_type +fix_fun: found info for unregister_custom_data_type +fix_fun: found info for register_custom_data_format +fix_fun: found info for unregister_custom_data_format +fix_fun: found info for get_custom_data_format +fix_fun: found info for get_custom_data_type +### Processing dbg.i +fix_fun: found info for get_manual_regions +fix_fun: found info for dbg_is_loaded +fix_fun: found info for refresh_debugger_memory +fix_fun: found info for move_bpt_to_grp +fix_fun: found info for internal_get_sreg_base +### Processing diskio.i +fix_fun: found info for enumerate_files +### Processing entry.i +### Processing enum.i +### Processing expr.i +### Processing fixup.i +### Processing fpro.i +fix_class: found info for qfile_t +fix_method: found info for __init__ +fix_method: found info for opened +fix_method: found info for open +fix_method: found info for tmpfile +fix_method: found info for seek +fix_method: found info for tell +fix_method: found info for readbytes +fix_method: found info for read +fix_method: found info for gets +fix_method: found info for writebytes +fix_method: found info for write +fix_method: found info for flush +fix_method: found info for put_char +### Processing frame.i +fix_fun: found info for add_stkvar3 +fix_fun: found info for get_stkvar +### Processing funcs.i +fix_fun: found info for get_fchunk_referer +fix_fun: found info for get_idasgn_desc +fix_fun: found info for get_idasgn_desc_with_matches +fix_fun: found info for get_func_cmt +### Processing gdl.i +### Processing graph.i +### Processing hexrays.i +### Processing ida.i +### Processing idaapi.i +fix_fun: found info for get_inf_structure +fix_fun: found info for set_script_timeout +fix_fun: found info for disable_script_timeout +fix_fun: found info for enable_extlang_python +fix_fun: found info for notify_when +fix_class: found info for loader_input_t +fix_method: found info for __init__ +fix_method: found info for open +fix_method: found info for set_linput +fix_method: found info for from_fp +fix_method: found info for open_memory +fix_method: found info for seek +fix_method: found info for tell +fix_method: found info for getz +fix_method: found info for gets +fix_method: found info for read +fix_method: found info for opened +fix_method: found info for readbytes +fix_method: found info for file2base +fix_method: found info for get_char +### Processing idd.i +fix_fun: found info for dbg_get_registers +fix_fun: found info for dbg_get_thread_sreg_base +fix_fun: found info for dbg_read_memory +fix_fun: found info for dbg_write_memory +fix_fun: found info for dbg_get_name +fix_fun: found info for dbg_get_memory_info +fix_fun: found info for dbg_can_query +### Processing idp.i +fix_fun: found info for AssembleLine +fix_fun: found info for assemble +fix_fun: found info for ph_get_id +fix_fun: found info for ph_get_version +fix_fun: found info for ph_get_flag +fix_fun: found info for ph_get_cnbits +fix_fun: found info for ph_get_dnbits +fix_fun: found info for ph_get_regFirstSreg +fix_fun: found info for ph_get_regLastSreg +fix_fun: found info for ph_get_segreg_size +fix_fun: found info for ph_get_regCodeSreg +fix_fun: found info for ph_get_regDataSreg +fix_fun: found info for ph_get_high_fixup_bits +fix_fun: found info for ph_get_icode_return +fix_fun: found info for ph_get_instruc_start +fix_fun: found info for ph_get_instruc_end +fix_fun: found info for ph_get_tbyte_size +fix_fun: found info for ph_get_instruc +fix_fun: found info for ph_get_regnames +fix_fun: found info for ph_get_operand_info +fix_class: found info for IDP_Hooks +fix_method: found info for hook +fix_method: found info for unhook +fix_method: found info for assemble +fix_method: found info for rename +fix_method: found info for closebase +fix_method: found info for load_idasgn +fix_method: found info for may_be_func +fix_method: found info for is_sane_insn +fix_method: found info for custom_ana +fix_method: found info for custom_out +fix_method: found info for custom_emu +fix_method: found info for custom_outop +fix_method: found info for custom_mnem +fix_method: found info for undefine +fix_method: found info for make_code +fix_method: found info for make_data +fix_method: found info for is_call_insn +fix_method: found info for is_ret_insn +fix_method: found info for savebase +fix_method: found info for renamed +fix_method: found info for add_func +fix_method: found info for del_func +### Processing ints.i +### Processing kernwin.i +fix_fun: found info for register_timer +fix_fun: found info for unregister_timer +fix_fun: found info for choose_idasgn +fix_fun: found info for get_highlighted_identifier +fix_fun: found info for umsg +fix_fun: found info for msg +fix_fun: found info for asktext +fix_fun: found info for str2ea +fix_fun: found info for str2user +fix_fun: found info for process_ui_action +fix_fun: found info for del_menu_item +fix_fun: found info for del_hotkey +fix_fun: found info for add_hotkey +fix_fun: found info for add_menu_item +fix_fun: found info for execute_sync +fix_fun: found info for execute_ui_requests +fix_class: found info for UI_Hooks +fix_method: found info for hook +fix_method: found info for unhook +fix_method: found info for saving +fix_method: found info for saved +fix_method: found info for term +fix_method: found info for get_ea_hint +fix_method: found info for preprocess +fix_method: found info for postprocess +fix_method: found info for updating_actions +fix_method: found info for updated_actions +fix_method: found info for populating_tform_popup +fix_method: found info for finish_populating_tform_popup +fix_fun: found info for set_nav_colorizer +fix_fun: found info for call_nav_colorizer +fix_fun: found info for set_dock_pos +fix_fun: found info for free_custom_icon +fix_fun: found info for is_idaq +fix_fun: found info for readsel2 +### Processing lines.i +fix_fun: found info for set_user_defined_prefix +fix_fun: found info for tag_remove +fix_fun: found info for generate_disassembly +### Processing loader.i +fix_fun: found info for mem2base +fix_fun: found info for load_plugin +fix_fun: found info for run_plugin +### Processing moves.i +### Processing nalt.i +fix_fun: found info for get_import_module_name +fix_fun: found info for get_switch_info_ex +fix_fun: found info for set_switch_info_ex +fix_fun: found info for del_switch_info_ex +fix_fun: found info for enum_import_names +### Processing name.i +### Processing netnode.i +### Processing offset.i +### Processing pro.i +### Processing queue.i +### Processing registry.i +### Processing search.i +### Processing segment.i +### Processing srarea.i +### Processing strlist.i +### Processing struct.i +### Processing typeinf.i +fix_fun: found info for calc_type_size +fix_fun: found info for apply_type +fix_fun: found info for print_type +fix_fun: found info for unpack_object_from_bv +fix_fun: found info for pack_object_to_idb +fix_fun: found info for pack_object_to_bv +fix_fun: found info for get_named_type +fix_fun: found info for apply_type_to_stkarg +### Processing ua.i +fix_fun: found info for init_output_buffer +fix_fun: found info for term_output_buffer +fix_fun: found info for decode_preceding_insn +fix_fun: found info for OutValue +fix_fun: found info for OutImmChar +fix_fun: found info for ua_stkvar2 +fix_fun: found info for ua_add_off_drefs +fix_fun: found info for ua_add_off_drefs2 +fix_fun: found info for out_name_expr +fix_fun: found info for construct_macro +### Processing xref.i +fix_fun: found info for create_switch_xrefs +fix_fun: found info for calc_switch_cases +fix_fun: found info for create_switch_table diff --git a/python.cfg b/python.cfg index 1ecd270..b2a1857 100644 --- a/python.cfg +++ b/python.cfg @@ -8,3 +8,7 @@ REMOVE_CWD_SYS_PATH = YES // Script timeout (in seconds) // (A value of 0 disables the timeout) SCRIPT_TIMEOUT = 3 + +// Should the plugin automatically load a backward-compatibility-providing +// 'idaapi' wrapper module? +AUTOIMPORT_COMPAT_IDAAPI = YES diff --git a/python.cpp b/python.cpp index 879f278..712c862 100644 --- a/python.cpp +++ b/python.cpp @@ -32,13 +32,8 @@ #include #include -#ifdef WITH_HEXRAYS -//-V::730 Not all members of a class are initialized inside the constructor -#include -hexdsp_t *hexdsp = NULL; -#endif - #include "pywraps.hpp" +#include "pywraps.cpp" //------------------------------------------------------------------------- // Defines and constants @@ -54,7 +49,6 @@ hexdsp_t *hexdsp = NULL; #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"; @@ -80,9 +74,9 @@ 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); +// // 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); @@ -117,40 +111,186 @@ int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) //------------------------------------------------------------------------- // 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 bool g_autoimport_compat_idaapi = true; -static void end_execution(void); -static void begin_execution(void); +// Allowing the user to interrupt a script is not entirely trivial. +// Imagine the following script, that is run in an IDB that uses +// an IDAPython processor module (important!) : +// --- +// while True: +// gen_disasm_text(ea, ea + 4, dtext, False) +// --- +// This script will call the processor module's out/outop functions in +// order to generate the text. If the processor module behaves +// correctly (i.e., doesn't take forever to generate said text), if the +// user presses 'Cancel' once the wait dialog box shows, what we want +// to cancel is _this_ script above: we don't want to interrupt the +// processor module while it's doing its thing! +// In order to do that, we will have to remember the time-of-entry of +// various entry points: +// - IDAPython_extlang_compile_file +// - IDAPython_RunStatement +// - ... and more importantly in this case: +// - IDAPython_extlang_call_method (called by the IDA kernel to generate text) +// +// Of course, in case the processor module's out/outop misbehaves, we still +// want the ability to cancel that operation. The following code allows for +// that, too. + +//------------------------------------------------------------------------- +struct exec_entry_t +{ + time_t etime; + exec_entry_t() { etime = time(NULL); } +}; +DECLARE_TYPE_AS_MOVABLE(exec_entry_t); +typedef qvector exec_entries_t; + +//------------------------------------------------------------------------- +struct execution_t +{ + exec_entries_t entries; + int timeout; + uint32 steps_before_action; + bool waitdialog_shown; + + execution_t() + : timeout(2), + steps_before_action(0), + waitdialog_shown(false) + { + reset_steps(); + } + void reset_steps(); + void push(); + void pop(); + bool can_interrupt_current(time_t now) const; + void stop_tracking(); + void sync_to_present_time(); + void maybe_hide_waitdialog(); + static int on_trace(PyObject *obj, _frame *frame, int what, PyObject *arg); +}; +static execution_t execution; + +//#define LOG_EXEC 1 +#ifdef LOG_EXEC +#define LEXEC(Format, ...) msg("IDAPython exec: " Format, __VA_ARGS__) +#else +#define LEXEC(Format, ...) +#endif + +//------------------------------------------------------------------------- +void execution_t::reset_steps() +{ + // we want to trace/check the time about every 10 steps. But we don't + // want it to be exactly 10 steps, or we might never make important + // checks because the tracing happens always at the wrong point. + // E.g., imagine the following loop: + // --- + // while True: + // gen_disasm_text(ea, ea + 4, dtext, False) + // --- + // If we never hit the 'trace' callback while in the 'while True' loop + // but always when performing the call to the processor module's 'out/outop' + // then the loop will never stop. That was happening on windows (optimized.) + steps_before_action = 1 + rand() % 20; +} + +//------------------------------------------------------------------------- +void execution_t::push() +{ + if ( entries.empty() ) + PyEval_SetTrace(execution_t::on_trace, NULL); + entries.push_back(); + LEXEC("push() (now: %d entries)\n", int(entries.size())); +} + +//------------------------------------------------------------------------- +void execution_t::pop() +{ + entries.pop_back(); + if ( entries.empty() ) + stop_tracking(); + LEXEC("pop() (now: %d entries)\n", int(entries.size())); +} + +//------------------------------------------------------------------------- +void execution_t::stop_tracking() +{ + PyEval_SetTrace(NULL, NULL); + maybe_hide_waitdialog(); +} + +//------------------------------------------------------------------------- +void execution_t::sync_to_present_time() +{ + time_t now = time(NULL); + for ( size_t i = 0, n = entries.size(); i < n; ++i ) + entries[i].etime = now; + maybe_hide_waitdialog(); +} + +//------------------------------------------------------------------------- +void execution_t::maybe_hide_waitdialog() +{ + if ( waitdialog_shown ) + { + hide_wait_box(); + waitdialog_shown = false; + } +} + +//------------------------------------------------------------------------- +bool execution_t::can_interrupt_current(time_t now) const +{ + LEXEC("can_interrupt_current(): nentries: %d\n", int(entries.size())); + if ( entries.empty() || timeout <= 0 ) + return false; + const exec_entry_t &last = entries.back(); + bool can = (now - last.etime) > timeout; + LEXEC("can_interrupt_current(): last: %d, now: %d (-> %d)\n", + int(last.etime), int(now), can); + return can; +} //------------------------------------------------------------------------ -// This callback is called on various interpreter events -static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) +int execution_t::on_trace(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; + LEXEC("on_trace() (steps=%d, nentries=%d)\n", + int(execution.steps_before_action), int(execution.entries.size())); + // we don't want to query for time at every trace event + if ( execution.steps_before_action-- > 0 ) + return 0; - // Timeout disabled or elapsed? - if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) + execution.reset_steps(); + time_t now = time(NULL); + LEXEC("on_trace()::now: %d\n", int(now)); + bool can_interrupt = execution.can_interrupt_current(now); + if ( can_interrupt ) + { + LEXEC("on_trace()::can_interrupt. Waitdialog shown? %d\n", + int(execution.waitdialog_shown)); + if ( execution.waitdialog_shown ) { - box_displayed = true; + if ( wasBreak() ) + { + LEXEC("on_trace()::INTERRUPTING\n"); + PyErr_SetString(PyExc_KeyboardInterrupt, "User interrupted"); + return -1; + } + } + else + { + LEXEC("on_trace()::showing wait dialog\n"); show_wait_box("Running Python script"); + execution.waitdialog_shown = true; } } + #ifdef ENABLE_PYTHON_PROFILING return tracefunc(obj, frame, what, arg); #else @@ -162,84 +302,50 @@ static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) #endif } -//------------------------------------------------------------------------ -static void reset_execution_time() +//------------------------------------------------------------------------- +//lint -esym(1788, new_execution_t) is referenced only by its constructor or destructor +struct new_execution_t { - 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 ) + bool created; + new_execution_t() { - hide_wait_box(); - box_displayed = false; + created = g_ui_ready && execution.timeout > 0; + if ( created ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + execution.push(); + } } -} - -//------------------------------------------------------------------------ -// 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(); -} + ~new_execution_t() + { + if ( created ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + execution.pop(); + } + } +}; //------------------------------------------------------------------------- //lint -esym(714,disable_script_timeout) Symbol not referenced -void disable_script_timeout() +idaman void ida_export disable_script_timeout() { // Clear timeout - script_timeout = 0; + execution.timeout = 0; // Uninstall the trace function and hide the waitbox (if it was shown) - end_execution(); + execution.stop_tracking(); } //------------------------------------------------------------------------- //lint -esym(714,set_script_timeout) Symbol not referenced -int set_script_timeout(int timeout) +idaman int ida_export set_script_timeout(int timeout) { // Update the timeout - qswap(timeout, script_timeout); + qswap(timeout, execution.timeout); // Reset the execution time and hide the waitbox (so it is shown again after timeout elapses) - reset_execution_time(); - hide_script_waitbox(); + execution.sync_to_present_time(); return timeout; } @@ -347,16 +453,17 @@ static bool idaapi IDAPython_extlang_run_statements( { 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); + { + new_execution_t exec; + newref_t result(PyRun_String( + str, + Py_file_input, + globals, + globals)); + ok = result != NULL && !PyErr_Occurred(); + if ( !ok ) + handle_python_error(errbuf, errbufsize); + } } if ( !ok && errbuf[0] == '\0' ) qstrncpy(errbuf, "internal error", errbufsize); @@ -384,9 +491,10 @@ static error_t idaapi idc_runpythonstatement( //-------------------------------------------------------------------------- static const cfgopt_t opts[] = { - cfgopt_t("SCRIPT_TIMEOUT", &script_timeout, 0, INT_MAX), + cfgopt_t("SCRIPT_TIMEOUT", &execution.timeout, 0, INT_MAX), cfgopt_t("ALERT_AUTO_SCRIPTS", &g_alert_auto_scripts, true), cfgopt_t("REMOVE_CWD_SYS_PATH", &g_remove_cwd_sys_path, true), + cfgopt_t("AUTOIMPORT_COMPAT_IDAAPI", &g_autoimport_compat_idaapi, true), }; //------------------------------------------------------------------------- @@ -397,11 +505,11 @@ static bool check_python_dir() { S_IDC_MODNAME ".py", S_INIT_PY, - "idaapi.py", + "ida_idaapi.py", "idautils.py" }; char filepath[QMAXPATH]; - for ( size_t i=0; iis_zero() ) { errbuf[0] = '\0'; ok = false; } - end_execution(); return ok; } @@ -842,10 +949,8 @@ static bool idaapi IDAPython_extlang_unloadprocmod( size_t errbufsize) { PYW_GIL_GET; - begin_execution(); - bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_UNLOADPROCMOD); - end_execution(); - return ok; + new_execution_t exec; + return IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_UNLOADPROCMOD); } //------------------------------------------------------------------------- @@ -1076,9 +1181,10 @@ bool idaapi IDAPython_extlang_calcexpr( ref_t result; if ( ok ) { - begin_execution(); - result = newref_t(PyRun_String(expr, Py_eval_input, globals, globals)); - end_execution(); + { + new_execution_t exec; + result = newref_t(PyRun_String(expr, Py_eval_input, globals, globals)); + } ok = return_python_result(rv, result, errbuf, errbufsize); } return ok; @@ -1105,7 +1211,8 @@ bool idaapi IDAPython_extlang_call_method( // Behave like run() else if ( idc_obj == NULL && method_name != NULL ) { - return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); + new_execution_t exec; + return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); } // Holds conversion status of input object @@ -1135,8 +1242,11 @@ bool idaapi IDAPython_extlang_call_method( 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); + { + new_execution_t exec; + 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; @@ -1196,7 +1306,7 @@ const extlang_t extlang_python = }; //------------------------------------------------------------------------- -void enable_extlang_python(bool enable) +idaman void ida_export enable_extlang_python(bool enable) { if ( enable ) select_extlang(&extlang_python); @@ -1247,9 +1357,10 @@ bool idaapi IDAPython_cli_execute_line(const char *line) line = s.c_str(); } while (false); - begin_execution(); - PythonEvalOrExec(line); - end_execution(); + { + new_execution_t exec; + PythonEvalOrExec(line); + } return true; } @@ -1294,7 +1405,7 @@ static const cli_t cli_python = //------------------------------------------------------------------------- // Control the Python CLI status -void enable_python_cli(bool enable) +idaman void ida_export enable_python_cli(bool enable) { if ( enable ) install_command_interpreter(&cli_python); @@ -1302,14 +1413,6 @@ void enable_python_cli(bool enable) 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() @@ -1362,37 +1465,25 @@ void convert_idc_args() 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) +static int idaapi on_ui_notification(void *, int code, va_list) { -#ifdef WITH_HEXRAYS - qnotused(va); -#endif switch ( code ) { - case ui_ready_to_run: + case ui_term: { PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. - g_ui_ready = true; - py_print_banner(); + // Let's make sure there are no non-Free()d forms. + free_compiled_form_instances(); + } + break; + case ui_ready_to_run: + { + PYW_GIL_GET; // See above + g_ui_ready = true; + PyRun_SimpleString("print_banner()"); if ( g_run_when == run_on_ui_ready ) RunScript(g_run_script); } @@ -1400,50 +1491,13 @@ static int idaapi on_ui_notification(void *, int code, va_list va) case ui_database_inited: { - PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; // See above 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; } @@ -1452,7 +1506,6 @@ static int idaapi on_ui_notification(void *, int code, va_list va) //------------------------------------------------------------------------- //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 ) @@ -1462,6 +1515,8 @@ static int idaapi on_idp_notification(void *, int code, va_list) // 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(); + // Same thing for live python timers + clear_python_timer_instances(); break; } return 0; @@ -1498,8 +1553,9 @@ static int idaapi ui_debug_handler_cb(void *, int code, va_list) #endif //------------------------------------------------------------------------- -// remove current directory (empty entry) from the sys.path -static void sanitize_path() +// - remove current directory (empty entry) from the sys.path +// - add idadir("python") +static void prepare_sys_path() { char buf[QMAXPATH]; qstrncpy(buf, Py_GetPath(), sizeof(buf)); @@ -1517,6 +1573,9 @@ static void sanitize_path() newpath.append(DELIMITER); newpath.append(d0); } + if ( !newpath.empty() ) + newpath.append(DELIMITER); + newpath.append(idadir("python")); PySys_SetPath(newpath.begin()); } @@ -1540,6 +1599,26 @@ static bool initsite(void) return true; } +//------------------------------------------------------------------------- +static void init_ida_modules() +{ + // char buf[QMAXPATH]; + // // IDA_MODULES must be passed as a define + // qstrncpy(buf, IDA_MODULES, sizeof(buf)); + // char *ctx; + // for ( char *module = qstrtok(buf, ",", &ctx); + // module != NULL; + // module = qstrtok(NULL, DELIMITER, &ctx) ) + // { + // deb(IDA_DEBUG_PLUGIN, "Initializing \"ida_%s\"\n", module); + // } + + // Load the 'ida_idaapi' module, that contains some important bits of code + // ref_t ida_idaapi(PyW_TryImportModule(S_PY_IDA_IDAAPI_MODNAME)); + + // ref_t sys(PyW_TryImportModule("sys")); +} + //------------------------------------------------------------------------- // Initialize the Python environment bool IDAPython_Init(void) @@ -1629,7 +1708,7 @@ bool IDAPython_Init(void) } // remove current directory - sanitize_path(); + prepare_sys_path(); // import "site" if ( !g_use_local_python && !initsite() ) @@ -1642,8 +1721,7 @@ bool IDAPython_Init(void) if ( !PyEval_ThreadsInitialized() ) PyEval_InitThreads(); - // Init the SWIG wrapper - init_idaapi(); + init_ida_modules(); #ifdef Py_DEBUG msg("HexraysPython: Python compiled with DEBUG enabled.\n"); @@ -1654,13 +1732,23 @@ bool IDAPython_Init(void) tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" - "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", + "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n" + "IDAPYTHON_DYNLOAD_BASE = r\"%s\"\n" + "IDAPYTHON_DYNLOAD_RELPATH = \"ida_%d\"\n" + "IDAPYTHON_COMPAT_AUTOIMPORT_MODULES = %s\n", VER_MAJOR, VER_MINOR, VER_PATCH, VER_STATUS, VER_SERIAL, - g_remove_cwd_sys_path ? "True" : "False"); + g_remove_cwd_sys_path ? "True" : "False", + idadir(NULL), +#ifdef __EA64__ + 64, +#else + 32, +#endif + g_autoimport_compat_idaapi ? "True" : "False"); PyRun_SimpleString(tmp); // Install extlang. Needs to be done before running init.py diff --git a/python/idaapi.py b/python/idaapi.py new file mode 100644 index 0000000..8e39a09 --- /dev/null +++ b/python/idaapi.py @@ -0,0 +1,42 @@ + +import sys + +${IMPORTS} + +class idaapi_Cvar(object): + def __init__(self): + # prevent endless recursion + object.__setattr__(self, "modules", "${MODULES}".split(",")) + object.__setattr__(self, "cvars_entries", dict()) + + def _get_module_cvar(self, modname): + mod = sys.modules["ida_%s" % modname] + cv, entries = None, None + if hasattr(mod, "cvar"): + cv = getattr(mod, "cvar") + entries = [] + if cv: + if modname in self.cvars_entries.keys(): + entries = self.cvars_entries[modname] + else: + # Parse 'str' version of cvar. Although this is braindeader than + # braindead, I'm not sure there's another way to do it. + entries_s = str(cv) + entries = entries_s[1:len(entries_s)-1].split(", ") + self.cvars_entries[modname] = entries + return cv, entries + + def __getattr__(self, attr): + for mod in self.modules: + cv, entries = self._get_module_cvar(mod) + if cv and attr in entries: + return getattr(cv, attr) + + def __setattr__(self, attr, value): + for mod in self.modules: + cv, entries = self._get_module_cvar(mod) + if cv and attr in entries: + setattr(cv, attr, value) + + +cvar = idaapi_Cvar() diff --git a/python/idautils.py b/python/idautils.py old mode 100644 new mode 100755 index 65baa5f..91bd75f --- a/python/idautils.py +++ b/python/idautils.py @@ -11,7 +11,22 @@ """ idautils.py - High level utility functions for IDA """ -import idaapi +import ida_bytes +import ida_dbg +import ida_entry +import ida_funcs +import ida_ida +import ida_idaapi +import ida_idd +import ida_idp +import ida_kernwin +import ida_loader +import ida_nalt +import ida_name +import ida_segment +import ida_strlist +import ida_ua +import ida_xref import idc import types import os @@ -22,7 +37,7 @@ def refs(ea, funcfirst, funcnext): Generic reference collector - INTERNAL USE ONLY. """ ref = funcfirst(ea) - while ref != idaapi.BADADDR: + while ref != ida_idaapi.BADADDR: yield ref ref = funcnext(ea, ref) @@ -43,9 +58,9 @@ def CodeRefsTo(ea, flow): print ref """ if flow == 1: - return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to) + return refs(ea, ida_xref.get_first_cref_to, ida_xref.get_next_cref_to) else: - return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to) + return refs(ea, ida_xref.get_first_fcref_to, ida_xref.get_next_fcref_to) def CodeRefsFrom(ea, flow): @@ -64,9 +79,9 @@ def CodeRefsFrom(ea, flow): print ref """ if flow == 1: - return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from) + return refs(ea, ida_xref.get_first_cref_from, ida_xref.get_next_cref_from) else: - return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from) + return refs(ea, ida_xref.get_first_fcref_from, ida_xref.get_next_fcref_from) def DataRefsTo(ea): @@ -82,7 +97,7 @@ def DataRefsTo(ea): for ref in DataRefsTo(ScreenEA()): print ref """ - return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to) + return refs(ea, ida_xref.get_first_dref_to, ida_xref.get_next_dref_to) def DataRefsFrom(ea): @@ -98,7 +113,7 @@ def DataRefsFrom(ea): for ref in DataRefsFrom(ScreenEA()): print ref """ - return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from) + return refs(ea, ida_xref.get_first_dref_from, ida_xref.get_next_dref_from) def XrefTypeName(typecode): @@ -141,14 +156,14 @@ def XrefsFrom(ea, flags=0): Return all references from address 'ea' @param ea: Reference address - @param flags: any of idaapi.XREF_* flags + @param flags: any of ida_xref.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() + xref = ida_xref.xrefblk_t() if xref.first_from(ea, flags): yield _copy_xref(xref) while xref.next_from(): @@ -160,14 +175,14 @@ def XrefsTo(ea, flags=0): Return all references to address 'ea' @param ea: Reference address - @param flags: any of idaapi.XREF_* flags + @param flags: any of ida_xref.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() + xref = ida_xref.xrefblk_t() if xref.first_to(ea, flags): yield _copy_xref(xref) while xref.next_to(): @@ -189,15 +204,15 @@ def Heads(start=None, end=None): @return: list of heads between start and end """ - if not start: start = idaapi.cvar.inf.minEA - if not end: end = idaapi.cvar.inf.maxEA + if not start: start = ida_ida.cvar.inf.minEA + if not end: end = ida_ida.cvar.inf.maxEA ea = start if not idc.isHead(idc.GetFlags(ea)): - ea = idaapi.next_head(ea, end) - while ea != idaapi.BADADDR: + ea = ida_bytes.next_head(ea, end) + while ea != ida_idaapi.BADADDR: yield ea - ea = idaapi.next_head(ea, end) + ea = ida_bytes.next_head(ea, end) def Functions(start=None, end=None): @@ -214,21 +229,21 @@ def Functions(start=None, end=None): 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 + if not start: start = ida_ida.cvar.inf.minEA + if not end: end = ida_ida.cvar.inf.maxEA # find first function head chunk in the range - chunk = idaapi.get_fchunk(start) + chunk = ida_funcs.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) + chunk = ida_funcs.get_next_fchunk(start) + while chunk and chunk.startEA < end and (chunk.flags & ida_funcs.FUNC_TAIL) != 0: + chunk = ida_funcs.get_next_fchunk(chunk.startEA) func = chunk while func and func.startEA < end: startea = func.startEA yield startea - func = idaapi.get_next_func(startea) + func = ida_funcs.get_next_func(startea) def Chunks(start): @@ -240,7 +255,7 @@ def Chunks(start): @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 ) ) + func_iter = ida_funcs.func_tail_iterator_t( ida_funcs.get_func( start ) ) status = func_iter.main() while status: chunk = func_iter.chunk() @@ -252,11 +267,11 @@ 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) + mod = ida_idd.module_info_t() + result = ida_dbg.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) + yield ida_idaapi.object_t(name=mod.name, size=mod.size, base=mod.base, rebase_to=mod.rebase_to) + result = ida_dbg.get_next_module(mod) def Names(): @@ -265,9 +280,9 @@ def 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) + for i in xrange(ida_name.get_nlist_size()): + ea = ida_name.get_nlist_ea(i) + name = ida_name.get_nlist_name(i) yield (ea, name) @@ -277,8 +292,8 @@ def Segments(): @return: List of segment start addresses. """ - for n in xrange(idaapi.get_segm_qty()): - seg = idaapi.getnseg(n) + for n in xrange(ida_segment.get_segm_qty()): + seg = ida_segment.getnseg(n) if seg: yield seg.startEA @@ -289,11 +304,11 @@ def Entries(): @return: List of tuples (index, ordinal, ea, name) """ - n = idaapi.get_entry_qty() + n = ida_entry.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) + ordinal = ida_entry.get_entry_ordinal(i) + ea = ida_entry.get_entry(ordinal) + name = ida_entry.get_entry_name(ordinal) yield (i, ordinal, ea, name) @@ -305,10 +320,10 @@ def FuncItems(start): @return: ea of each item in the function """ - func = idaapi.get_func(start) + func = ida_funcs.get_func(start) if not func: return - fii = idaapi.func_item_iterator_t() + fii = ida_funcs.func_item_iterator_t() ok = fii.set(func) while ok: yield fii.current() @@ -322,7 +337,7 @@ def Structs(): @return: List of tuples (idx, sid, name) """ idx = idc.GetFirstStrucIdx() - while idx != idaapi.BADADDR: + while idx != ida_idaapi.BADADDR: sid = idc.GetStrucId(idx) yield (idx, sid, idc.GetStrucName(sid)) idx = idc.GetNextStrucIdx(idx) @@ -344,7 +359,7 @@ def StructMembers(sid): m = idc.GetFirstMember(sid) if m == -1: raise Exception("No structure with ID: 0x%x" % sid) - while (m != idaapi.BADADDR): + while (m != ida_idaapi.BADADDR): name = idc.GetMemberName(sid, m) if name: yield (m, name, idc.GetMemberSize(sid, m)) @@ -359,11 +374,11 @@ def DecodePrecedingInstruction(ea): @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: + prev_addr, farref = ida_ua.decode_preceding_insn(ea) + if prev_addr == ida_idaapi.BADADDR: return (None, False) else: - return (idaapi.cmd.copy(), farref) + return (ida_ua.cmd.copy(), farref) @@ -374,11 +389,11 @@ def DecodePreviousInstruction(ea): @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: + prev_addr = ida_ua.decode_prev_insn(ea) + if prev_addr == ida_idaapi.BADADDR: return None - return idaapi.cmd.copy() + return ida_ua.cmd.copy() def DecodeInstruction(ea): @@ -388,11 +403,11 @@ def DecodeInstruction(ea): @param ea: address to decode @return: None or a new insn_t instance """ - inslen = idaapi.decode_insn(ea) + inslen = ida_ua.decode_insn(ea) if inslen == 0: return None - return idaapi.cmd.copy() + return ida_ua.cmd.copy() def GetDataList(ea, count, itemsize=1): @@ -400,13 +415,13 @@ def GetDataList(ea, count, itemsize=1): Get data list - INTERNAL USE ONLY """ if itemsize == 1: - getdata = idaapi.get_byte + getdata = ida_bytes.get_byte elif itemsize == 2: - getdata = idaapi.get_word + getdata = ida_bytes.get_word elif itemsize == 4: - getdata = idaapi.get_long + getdata = ida_bytes.get_long elif itemsize == 8: - getdata = idaapi.get_qword + getdata = ida_bytes.get_qword else: raise ValueError, "Invalid data size! Must be 1, 2, 4 or 8" @@ -424,11 +439,11 @@ def PutDataList(ea, datalist, itemsize=1): putdata = None if itemsize == 1: - putdata = idaapi.patch_byte + putdata = ida_bytes.patch_byte if itemsize == 2: - putdata = idaapi.patch_word + putdata = ida_bytes.patch_word if itemsize == 4: - putdata = idaapi.patch_long + putdata = ida_bytes.patch_long assert putdata, "Invalid data size! Must be 1, 2 or 4" @@ -488,22 +503,22 @@ class Strings(object): 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] + return (self.type & 7) in [ida_nalt.ASCSTR_UTF16, ida_nalt.ASCSTR_ULEN2, ida_nalt.ASCSTR_ULEN4] def is_4_bytes_encoding(self): - return (self.type & 7) == idaapi.ASCSTR_UTF32 + return (self.type & 7) == ida_nalt.ASCSTR_UTF32 def _toseq(self, as_unicode): if self.is_2_bytes_encoding(): - conv = idaapi.ACFOPT_UTF16 + conv = ida_bytes.ACFOPT_UTF16 pyenc = "utf-16" elif self.is_4_bytes_encoding(): - conv = idaapi.ACFOPT_UTF8 + conv = ida_bytes.ACFOPT_UTF8 pyenc = "utf-8" else: - conv = idaapi.ACFOPT_ASCII + conv = ida_bytes.ACFOPT_ASCII pyenc = 'ascii' - strbytes = idaapi.get_ascii_contents2(self.ea, self.length, self.type, conv) + strbytes = ida_bytes.get_ascii_contents2(self.ea, self.length, self.type, conv) return unicode(strbytes, pyenc, 'replace') if as_unicode else strbytes def __str__(self): @@ -544,17 +559,17 @@ class Strings(object): else: self.refresh() - self._si = idaapi.string_info_t() + self._si = ida_strlist.string_info_t() def refresh(self, ea1=None, ea2=None): """Refreshes the strings list""" if ea1 is None: - ea1 = idaapi.cvar.inf.minEA + ea1 = ida_ida.cvar.inf.minEA if ea2 is None: - ea2 = idaapi.cvar.inf.maxEA + ea2 = ida_ida.cvar.inf.maxEA - idaapi.refresh_strlist(ea1, ea2) - self.size = idaapi.get_strlist_qty() + ida_strlist.refresh_strlist(ea1, ea2) + self.size = ida_strlist.get_strlist_qty() def setup(self, @@ -567,26 +582,26 @@ class Strings(object): display_only_existing_strings = False): if ea1 is None: - ea1 = idaapi.cvar.inf.minEA + ea1 = ida_ida.cvar.inf.minEA if ea2 is None: - ea2 = idaapi.cvar.inf.maxEA + ea2 = ida_ida.cvar.inf.maxEA - t = idaapi.strwinsetup_t() + t = ida_strlist.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) + ida_strlist.set_strlist_options(t) # Automatically refreshes self.refresh() def _get_item(self, index): - if not idaapi.get_strlist_item(index, self._si): + if not ida_strlist.get_strlist_item(index, self._si): return None else: return Strings.StringItem(self._si) @@ -610,17 +625,17 @@ def GetIdbDir(): This function returns directory path of the current IDB database """ - return os.path.dirname(idaapi.cvar.database_idb) + os.sep + return os.path.dirname(ida_loader.cvar.database_idb) + os.sep # ----------------------------------------------------------------------- def GetRegisterList(): """Returns the register list""" - return idaapi.ph_get_regnames() + return ida_idp.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]] + return [i[0] for i in ida_idp.ph_get_instruc() if i[0]] # ----------------------------------------------------------------------- def _Assemble(ea, line): @@ -633,11 +648,11 @@ def _Assemble(ea, line): lines = line ret = [] for line in lines: - seg = idaapi.getseg(ea) + seg = ida_segment.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) + ip = ea - (ida_segment.ask_selector(seg.sel) << 4) + buf = ida_idp.AssembleLine(ea, seg.sel, ip, seg.bitness, line) if not buf: return (False, "Assembler failed: " + line) ea += len(buf) @@ -705,10 +720,10 @@ class _reg_dtyp_t(object): 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): + ri = ida_idp.reg_info_t() + if not ida_idp.parse_reg_name(attr, ri): raise AttributeError() - r = _reg_dtyp_t(ri.reg, ord(idaapi.get_dtyp_by_size(ri.size))) + r = _reg_dtyp_t(ri.reg, ord(ida_ua.get_dtyp_by_size(ri.size))) self.__dict__[attr] = r return r @@ -754,7 +769,7 @@ class __process_ui_actions_helper(object): return False # Execute one action - idaapi.process_ui_action( + ida_kernwin.process_ui_action( self.__action_list[self.__idx], self.__flags) @@ -775,7 +790,7 @@ def ProcessUiActions(actions, flags=0): # Instantiate a helper helper = __process_ui_actions_helper(actions, flags) - return False if len(helper) < 1 else idaapi.execute_ui_requests((helper,)) + return False if len(helper) < 1 else ida_kernwin.execute_ui_requests((helper,)) # ----------------------------------------------------------------------- @@ -786,15 +801,15 @@ class peutils_t(object): 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 + PE_ALT_DBG_FPOS = ida_idaapi.BADADDR & -1 # altval() -> translated fpos of debuginfo + PE_ALT_IMAGEBASE = ida_idaapi.BADADDR & -2 # altval() -> loading address (usually pe.imagebase) + PE_ALT_PEHDR_OFF = ida_idaapi.BADADDR & -3 # altval() -> offset of PE header + PE_ALT_NEFLAGS = ida_idaapi.BADADDR & -4 # altval() -> neflags + PE_ALT_TDS_LOADED = ida_idaapi.BADADDR & -5 # altval() -> tds already loaded(1) or invalid(-1) + PE_ALT_PSXDLL = ida_idaapi.BADADDR & -6 # altval() -> if POSIX(x86) imports from PSXDLL netnode def __init__(self): - self.__penode = idaapi.netnode() + self.__penode = ida_netnode.netnode() self.__penode.create(peutils_t.PE_NODE) imagebase = property( diff --git a/python/idc.py b/python/idc.py old mode 100644 new mode 100755 index e2be3d1..c0fe5bf --- a/python/idc.py +++ b/python/idc.py @@ -27,10 +27,40 @@ SetFlags() function is strongly discouraged. This file is subject to change without any notice. Future versions of IDA may use other definitions. """ -try: - import idaapi -except ImportError: - print "Could not import idaapi. Running in 'pydoc mode'." +# FIXME: Perhaps those should be loaded on-demand +import ida_idaapi +import ida_auto +import ida_dbg +import ida_diskio +import ida_entry +import ida_enum +import ida_expr +import ida_fixup +import ida_frame +import ida_funcs +import ida_funcs +import ida_gdl +import ida_ida +import ida_bytes +import ida_idd +import ida_idp +import ida_kernwin +import ida_lines +import ida_loader +import ida_nalt +import ida_name +import ida_netnode +import ida_offset +import ida_pro +import ida_search +import ida_segment +import ida_srarea +import ida_struct +import ida_typeinf +import ida_ua +import ida_xref + +import _ida_idaapi import os import re @@ -38,7 +68,7 @@ import struct import time import types -__EA64__ = idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL +__EA64__ = ida_idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL WORDMASK = 0xFFFFFFFFFFFFFFFF if __EA64__ else 0xFFFFFFFF class DeprecatedIDCError(Exception): """ @@ -74,15 +104,15 @@ def _IDC_SetAttr(obj, attrmap, attroffs, value): raise KeyError, errormsg -BADADDR = idaapi.BADADDR # Not allowed address value -BADSEL = idaapi.BADSEL # Not allowed selector value/number -MAXADDR = idaapi.MAXADDR & WORDMASK -SIZE_MAX = idaapi.SIZE_MAX +BADADDR = ida_idaapi.BADADDR # Not allowed address value +BADSEL = ida_idaapi.BADSEL # Not allowed selector value/number +MAXADDR = ida_ida.MAXADDR & WORDMASK +SIZE_MAX = _ida_idaapi.SIZE_MAX # # Flag bit definitions (for GetFlags()) # -MS_VAL = idaapi.MS_VAL # Mask for byte value -FF_IVL = idaapi.FF_IVL # Byte has value ? +MS_VAL = ida_bytes.MS_VAL # Mask for byte value +FF_IVL = ida_bytes.FF_IVL # Byte has value ? # Do flags contain byte value? (i.e. has the byte a value?) # if not, the byte is uninitialized. @@ -102,11 +132,11 @@ def isLoaded(ea): """Is the byte initialized?""" return hasValue(GetFlags(ea)) # any defined value? -MS_CLS = idaapi.MS_CLS # Mask for typing -FF_CODE = idaapi.FF_CODE # Code ? -FF_DATA = idaapi.FF_DATA # Data ? -FF_TAIL = idaapi.FF_TAIL # Tail ? -FF_UNK = idaapi.FF_UNK # Unknown ? +MS_CLS = ida_bytes.MS_CLS # Mask for typing +FF_CODE = ida_bytes.FF_CODE # Code ? +FF_DATA = ida_bytes.FF_DATA # Data ? +FF_TAIL = ida_bytes.FF_TAIL # Tail ? +FF_UNK = ida_bytes.FF_UNK # Unknown ? def isCode(F): return ((F & MS_CLS) == FF_CODE) # is code byte? def isData(F): return ((F & MS_CLS) == FF_DATA) # is data byte? @@ -117,14 +147,14 @@ def isHead(F): return ((F & FF_DATA) != 0) # is start of code/data? # # Common bits # -MS_COMM = idaapi.MS_COMM # Mask of common bits -FF_COMM = idaapi.FF_COMM # Has comment? -FF_REF = idaapi.FF_REF # has references? -FF_LINE = idaapi.FF_LINE # Has next or prev cmt lines ? -FF_NAME = idaapi.FF_NAME # Has user-defined name ? -FF_LABL = idaapi.FF_LABL # Has dummy name? -FF_FLOW = idaapi.FF_FLOW # Exec flow from prev instruction? -FF_VAR = idaapi.FF_VAR # Is byte variable ? +MS_COMM = ida_bytes.MS_COMM # Mask of common bits +FF_COMM = ida_bytes.FF_COMM # Has comment? +FF_REF = ida_bytes.FF_REF # has references? +FF_LINE = ida_bytes.FF_LINE # Has next or prev cmt lines ? +FF_NAME = ida_bytes.FF_NAME # Has user-defined name ? +FF_LABL = ida_bytes.FF_LABL # Has dummy name? +FF_FLOW = ida_bytes.FF_FLOW # Exec flow from prev instruction? +FF_VAR = ida_bytes.FF_VAR # Is byte variable ? FF_ANYNAME = FF_LABL | FF_NAME def isFlow(F): return ((F & FF_FLOW) != 0) @@ -134,33 +164,33 @@ def isRef(F): return ((F & FF_REF) != 0) def hasName(F): return ((F & FF_NAME) != 0) def hasUserName(F): return ((F & FF_ANYNAME) == FF_NAME) -MS_0TYPE = idaapi.MS_0TYPE # Mask for 1st arg typing -FF_0VOID = idaapi.FF_0VOID # Void (unknown)? -FF_0NUMH = idaapi.FF_0NUMH # Hexadecimal number? -FF_0NUMD = idaapi.FF_0NUMD # Decimal number? -FF_0CHAR = idaapi.FF_0CHAR # Char ('x')? -FF_0SEG = idaapi.FF_0SEG # Segment? -FF_0OFF = idaapi.FF_0OFF # Offset? -FF_0NUMB = idaapi.FF_0NUMB # Binary number? -FF_0NUMO = idaapi.FF_0NUMO # Octal number? -FF_0ENUM = idaapi.FF_0ENUM # Enumeration? -FF_0FOP = idaapi.FF_0FOP # Forced operand? -FF_0STRO = idaapi.FF_0STRO # Struct offset? -FF_0STK = idaapi.FF_0STK # Stack variable? +MS_0TYPE = ida_bytes.MS_0TYPE # Mask for 1st arg typing +FF_0VOID = ida_bytes.FF_0VOID # Void (unknown)? +FF_0NUMH = ida_bytes.FF_0NUMH # Hexadecimal number? +FF_0NUMD = ida_bytes.FF_0NUMD # Decimal number? +FF_0CHAR = ida_bytes.FF_0CHAR # Char ('x')? +FF_0SEG = ida_bytes.FF_0SEG # Segment? +FF_0OFF = ida_bytes.FF_0OFF # Offset? +FF_0NUMB = ida_bytes.FF_0NUMB # Binary number? +FF_0NUMO = ida_bytes.FF_0NUMO # Octal number? +FF_0ENUM = ida_bytes.FF_0ENUM # Enumeration? +FF_0FOP = ida_bytes.FF_0FOP # Forced operand? +FF_0STRO = ida_bytes.FF_0STRO # Struct offset? +FF_0STK = ida_bytes.FF_0STK # Stack variable? -MS_1TYPE = idaapi.MS_1TYPE # Mask for 2nd arg typing -FF_1VOID = idaapi.FF_1VOID # Void (unknown)? -FF_1NUMH = idaapi.FF_1NUMH # Hexadecimal number? -FF_1NUMD = idaapi.FF_1NUMD # Decimal number? -FF_1CHAR = idaapi.FF_1CHAR # Char ('x')? -FF_1SEG = idaapi.FF_1SEG # Segment? -FF_1OFF = idaapi.FF_1OFF # Offset? -FF_1NUMB = idaapi.FF_1NUMB # Binary number? -FF_1NUMO = idaapi.FF_1NUMO # Octal number? -FF_1ENUM = idaapi.FF_1ENUM # Enumeration? -FF_1FOP = idaapi.FF_1FOP # Forced operand? -FF_1STRO = idaapi.FF_1STRO # Struct offset? -FF_1STK = idaapi.FF_1STK # Stack variable? +MS_1TYPE = ida_bytes.MS_1TYPE # Mask for 2nd arg typing +FF_1VOID = ida_bytes.FF_1VOID # Void (unknown)? +FF_1NUMH = ida_bytes.FF_1NUMH # Hexadecimal number? +FF_1NUMD = ida_bytes.FF_1NUMD # Decimal number? +FF_1CHAR = ida_bytes.FF_1CHAR # Char ('x')? +FF_1SEG = ida_bytes.FF_1SEG # Segment? +FF_1OFF = ida_bytes.FF_1OFF # Offset? +FF_1NUMB = ida_bytes.FF_1NUMB # Binary number? +FF_1NUMO = ida_bytes.FF_1NUMO # Octal number? +FF_1ENUM = ida_bytes.FF_1ENUM # Enumeration? +FF_1FOP = ida_bytes.FF_1FOP # Forced operand? +FF_1STRO = ida_bytes.FF_1STRO # Struct offset? +FF_1STK = ida_bytes.FF_1STK # Stack variable? # The following macros answer questions like # 'is the 1st (or 2nd) operand of instruction or data of the given type'? @@ -194,20 +224,20 @@ def isStkvar1(F): return ((F & MS_1TYPE) == FF_1STK) # # Bits for DATA bytes # -DT_TYPE = idaapi.DT_TYPE & 0xFFFFFFFF # Mask for DATA typing +DT_TYPE = ida_bytes.DT_TYPE & 0xFFFFFFFF # Mask for DATA typing -FF_BYTE = idaapi.FF_BYTE & 0xFFFFFFFF # byte -FF_WORD = idaapi.FF_WORD & 0xFFFFFFFF # word -FF_DWRD = idaapi.FF_DWRD & 0xFFFFFFFF # dword -FF_QWRD = idaapi.FF_QWRD & 0xFFFFFFFF # qword -FF_TBYT = idaapi.FF_TBYT & 0xFFFFFFFF # tbyte -FF_ASCI = idaapi.FF_ASCI & 0xFFFFFFFF # ASCII ? -FF_STRU = idaapi.FF_STRU & 0xFFFFFFFF # Struct ? -FF_OWRD = idaapi.FF_OWRD & 0xFFFFFFFF # octaword (16 bytes) -FF_FLOAT = idaapi.FF_FLOAT & 0xFFFFFFFF # float -FF_DOUBLE = idaapi.FF_DOUBLE & 0xFFFFFFFF # double -FF_PACKREAL = idaapi.FF_PACKREAL & 0xFFFFFFFF # packed decimal real -FF_ALIGN = idaapi.FF_ALIGN & 0xFFFFFFFF # alignment directive +FF_BYTE = ida_bytes.FF_BYTE & 0xFFFFFFFF # byte +FF_WORD = ida_bytes.FF_WORD & 0xFFFFFFFF # word +FF_DWRD = ida_bytes.FF_DWRD & 0xFFFFFFFF # dword +FF_QWRD = ida_bytes.FF_QWRD & 0xFFFFFFFF # qword +FF_TBYT = ida_bytes.FF_TBYT & 0xFFFFFFFF # tbyte +FF_ASCI = ida_bytes.FF_ASCI & 0xFFFFFFFF # ASCII ? +FF_STRU = ida_bytes.FF_STRU & 0xFFFFFFFF # Struct ? +FF_OWRD = ida_bytes.FF_OWRD & 0xFFFFFFFF # octaword (16 bytes) +FF_FLOAT = ida_bytes.FF_FLOAT & 0xFFFFFFFF # float +FF_DOUBLE = ida_bytes.FF_DOUBLE & 0xFFFFFFFF # double +FF_PACKREAL = ida_bytes.FF_PACKREAL & 0xFFFFFFFF # packed decimal real +FF_ALIGN = ida_bytes.FF_ALIGN & 0xFFFFFFFF # alignment directive def isByte(F): return (isData(F) and (F & DT_TYPE) == FF_BYTE) def isWord(F): return (isData(F) and (F & DT_TYPE) == FF_WORD) @@ -225,24 +255,24 @@ def isAlign(F): return (isData(F) and (F & DT_TYPE) == FF_ALIGN) # # Bits for CODE bytes # -MS_CODE = idaapi.MS_CODE & 0xFFFFFFFF -FF_FUNC = idaapi.FF_FUNC & 0xFFFFFFFF # function start? -FF_IMMD = idaapi.FF_IMMD & 0xFFFFFFFF # Has Immediate value ? -FF_JUMP = idaapi.FF_JUMP & 0xFFFFFFFF # Has jump table +MS_CODE = ida_bytes.MS_CODE & 0xFFFFFFFF +FF_FUNC = ida_bytes.FF_FUNC & 0xFFFFFFFF # function start? +FF_IMMD = ida_bytes.FF_IMMD & 0xFFFFFFFF # Has Immediate value ? +FF_JUMP = ida_bytes.FF_JUMP & 0xFFFFFFFF # Has jump table # # Loader flags # -NEF_SEGS = idaapi.NEF_SEGS # Create segments -NEF_RSCS = idaapi.NEF_RSCS # Load resources -NEF_NAME = idaapi.NEF_NAME # Rename entries -NEF_MAN = idaapi.NEF_MAN # Manual load -NEF_FILL = idaapi.NEF_FILL # Fill segment gaps -NEF_IMPS = idaapi.NEF_IMPS # Create imports section -NEF_FIRST = idaapi.NEF_FIRST # This is the first file loaded -NEF_CODE = idaapi.NEF_CODE # for load_binary_file: -NEF_RELOAD = idaapi.NEF_RELOAD # reload the file at the same place: -NEF_FLAT = idaapi.NEF_FLAT # Autocreated FLAT group (PE) +NEF_SEGS = ida_loader.NEF_SEGS # Create segments +NEF_RSCS = ida_loader.NEF_RSCS # Load resources +NEF_NAME = ida_loader.NEF_NAME # Rename entries +NEF_MAN = ida_loader.NEF_MAN # Manual load +NEF_FILL = ida_loader.NEF_FILL # Fill segment gaps +NEF_IMPS = ida_loader.NEF_IMPS # Create imports section +NEF_FIRST = ida_loader.NEF_FIRST # This is the first file loaded +NEF_CODE = ida_loader.NEF_CODE # for load_binary_file: +NEF_RELOAD = ida_loader.NEF_RELOAD # reload the file at the same place: +NEF_FLAT = ida_loader.NEF_FLAT # Autocreated FLAT group (PE) # List of built-in functions # -------------------------- @@ -366,7 +396,7 @@ def AddHotkey(hotkey, idcfunc): @return: None """ - return idaapi.add_idc_hotkey(hotkey, idcfunc) + return ida_kernwin.add_idc_hotkey(hotkey, idcfunc) def DelHotkey(hotkey): @@ -375,7 +405,7 @@ def DelHotkey(hotkey): @param hotkey: hotkey code to delete """ - return idaapi.del_idc_hotkey(hotkey) + return ida_kernwin.del_idc_hotkey(hotkey) def Jump(ea): @@ -384,7 +414,7 @@ def Jump(ea): @param ea: linear address """ - return idaapi.jumpto(ea) + return ida_kernwin.jumpto(ea) def Wait(): @@ -395,7 +425,7 @@ def Wait(): @note: This function will suspend execution of the calling script till the autoanalysis queue is empty. """ - return idaapi.autoWait() + return ida_auto.autoWait() def CompileEx(input, isfile): @@ -413,9 +443,9 @@ def CompileEx(input, isfile): @return: 0 - ok, otherwise it returns an error message """ if isfile: - res = idaapi.Compile(input) + res = ida_expr.Compile(input) else: - res = idaapi.CompileLine(input) + res = ida_expr.CompileLine(input) if res: return res @@ -434,9 +464,9 @@ def Eval(expr): @note: Python implementation evaluates IDC only, while IDC can call other registered languages """ - rv = idaapi.idc_value_t() + rv = ida_expr.idc_value_t() - err = idaapi.calc_idc_expr(BADADDR, expr, rv) + err = ida_expr.calc_idc_expr(BADADDR, expr, rv) if err: return "IDC_FAILURE: "+err else: @@ -467,26 +497,26 @@ def SaveBase(idbname, flags=0): @param idbname: name of the idb file. if empty, the current idb file will be used. - @param flags: combination of idaapi.DBFL_... bits or 0 + @param flags: combination of ida_loader.DBFL_... bits or 0 """ if len(idbname) == 0: idbname = GetIdbPath() - saveflags = idaapi.cvar.database_flags - mask = idaapi.DBFL_KILL | idaapi.DBFL_COMP | idaapi.DBFL_BAK - idaapi.cvar.database_flags &= ~mask - idaapi.cvar.database_flags |= flags & mask - res = idaapi.save_database(idbname, 0) - idaapi.cvar.database_flags = saveflags + saveflags = ida_loader.cvar.database_flags + mask = ida_loader.DBFL_KILL | ida_loader.DBFL_COMP | ida_loader.DBFL_BAK + ida_loader.cvar.database_flags &= ~mask + ida_loader.cvar.database_flags |= flags & mask + res = ida_loader.save_database(idbname, 0) + ida_loader.cvar.database_flags = saveflags return res -DBFL_BAK = idaapi.DBFL_BAK # for compatiblity with older versions, eventually delete this +DBFL_BAK = ida_loader.DBFL_BAK # for compatiblity with older versions, eventually delete this def ValidateNames(): """ check consistency of IDB name records @return: number of inconsistent name records """ - return idaapi.validate_idb_names() + return ida_nalt.validate_idb_names() def Exit(code): """ @@ -496,7 +526,7 @@ def Exit(code): @return: - """ - idaapi.qexit(code) + ida_pro.qexit(code) def Exec(command): @@ -535,7 +565,7 @@ def RunPlugin(name, arg): @return: 0 if could not load the plugin, 1 if ok """ - return idaapi.load_and_run_plugin(name, arg) + return ida_loader.load_and_run_plugin(name, arg) def ApplySig(name): @@ -546,7 +576,7 @@ def ApplySig(name): @return: 0 if could not load the signature file, !=0 otherwise """ - return idaapi.plan_to_apply_idasgn(name) + return ida_funcs.plan_to_apply_idasgn(name) #---------------------------------------------------------------------------- @@ -559,25 +589,25 @@ def DeleteAll(): Delete all segments, instructions, comments, i.e. everything except values of bytes. """ - ea = idaapi.cvar.inf.minEA + ea = ida_ida.cvar.inf.minEA # Brute-force nuke all info from all the heads - while ea != BADADDR and ea <= idaapi.cvar.inf.maxEA: - idaapi.del_local_name(ea) - idaapi.del_global_name(ea) - func = idaapi.get_func(ea) + while ea != BADADDR and ea <= ida_ida.cvar.inf.maxEA: + ida_name.del_local_name(ea) + ida_name.del_global_name(ea) + func = ida_funcs.get_func(ea) if func: - idaapi.del_func_cmt(func, False) - idaapi.del_func_cmt(func, True) - idaapi.del_func(ea) - idaapi.del_hidden_area(ea) - seg = idaapi.getseg(ea) + ida_funcs.del_func_cmt(func, False) + ida_funcs.del_func_cmt(func, True) + ida_funcs.del_func(ea) + ida_bytes.del_hidden_area(ea) + seg = ida_segment.getseg(ea) if seg: - idaapi.del_segment_cmt(seg, False) - idaapi.del_segment_cmt(seg, True) - idaapi.del_segm(ea, idaapi.SEGDEL_KEEP | idaapi.SEGDEL_SILENT) + ida_segment.del_segment_cmt(seg, False) + ida_segment.del_segment_cmt(seg, True) + ida_segment.del_segm(ea, ida_segment.SEGDEL_KEEP | ida_segment.SEGDEL_SILENT) - ea = idaapi.next_head(ea, idaapi.cvar.inf.maxEA) + ea = ida_bytes.next_head(ea, ida_ida.cvar.inf.maxEA) def MakeCode(ea): @@ -590,7 +620,7 @@ def MakeCode(ea): would overlap with existing items, etc) otherwise returns length of the instruction in bytes """ - return idaapi.create_insn(ea) + return ida_ua.create_insn(ea) def AnalyzeArea(sEA, eEA): @@ -602,7 +632,7 @@ def AnalyzeArea(sEA, eEA): @return: 1-ok, 0-Ctrl-Break was pressed. """ - return idaapi.analyze_area(sEA, eEA) + return ida_auto.analyze_area(sEA, eEA) def MakeNameEx(ea, name, flags): @@ -615,32 +645,32 @@ def MakeNameEx(ea, name, flags): @return: 1-ok, 0-failure """ - return idaapi.set_name(ea, name, flags) + return ida_name.set_name(ea, name, flags) -SN_CHECK = idaapi.SN_CHECK # Fail if the name contains invalid - # characters - # If this bit is clear, all invalid chars - # (those !is_ident_char()) will be replaced - # by SubstChar (usually '_') - # List of valid characters is defined in - # ida.cfg -SN_NOCHECK = idaapi.SN_NOCHECK # Replace invalid chars with SubstChar -SN_PUBLIC = idaapi.SN_PUBLIC # if set, make name public -SN_NON_PUBLIC = idaapi.SN_NON_PUBLIC # if set, make name non-public -SN_WEAK = idaapi.SN_WEAK # if set, make name weak -SN_NON_WEAK = idaapi.SN_NON_WEAK # if set, make name non-weak -SN_AUTO = idaapi.SN_AUTO # if set, make name autogenerated -SN_NON_AUTO = idaapi.SN_NON_AUTO # if set, make name non-autogenerated -SN_NOLIST = idaapi.SN_NOLIST # if set, exclude name from the list - # if not set, then include the name into - # the list (however, if other bits are set, - # the name might be immediately excluded - # from the list) -SN_NOWARN = idaapi.SN_NOWARN # don't display a warning if failed -SN_LOCAL = idaapi.SN_LOCAL # create local name. a function should exist. - # local names can't be public or weak. - # also they are not included into the list - # of names they can't have dummy prefixes +SN_CHECK = ida_name.SN_CHECK # Fail if the name contains invalid + # characters + # If this bit is clear, all invalid chars + # (those !is_ident_char()) will be replaced + # by SubstChar (usually '_') + # List of valid characters is defined in + # ida.cfg +SN_NOCHECK = ida_name.SN_NOCHECK # Replace invalid chars with SubstChar +SN_PUBLIC = ida_name.SN_PUBLIC # if set, make name public +SN_NON_PUBLIC = ida_name.SN_NON_PUBLIC # if set, make name non-public +SN_WEAK = ida_name.SN_WEAK # if set, make name weak +SN_NON_WEAK = ida_name.SN_NON_WEAK # if set, make name non-weak +SN_AUTO = ida_name.SN_AUTO # if set, make name autogenerated +SN_NON_AUTO = ida_name.SN_NON_AUTO # if set, make name non-autogenerated +SN_NOLIST = ida_name.SN_NOLIST # if set, exclude name from the list + # if not set, then include the name into + # the list (however, if other bits are set, + # the name might be immediately excluded + # from the list) +SN_NOWARN = ida_name.SN_NOWARN # don't display a warning if failed +SN_LOCAL = ida_name.SN_LOCAL # create local name. a function should exist. + # local names can't be public or weak. + # also they are not included into the list + # of names they can't have dummy prefixes def MakeComm(ea, comment): """ @@ -651,7 +681,7 @@ def MakeComm(ea, comment): @return: None """ - return idaapi.set_cmt(ea, comment, 0) + return ida_bytes.set_cmt(ea, comment, 0) def MakeRptCmt(ea, comment): @@ -663,7 +693,7 @@ def MakeRptCmt(ea, comment): @return: None """ - return idaapi.set_cmt(ea, comment, 1) + return ida_bytes.set_cmt(ea, comment, 1) def MakeArray(ea, nitems): @@ -677,24 +707,24 @@ def MakeArray(ea, nitems): the type of the item at 'ea'. If the byte at 'ea' is undefined, then this function will create an array of bytes. """ - flags = idaapi.getFlags(ea) + flags = ida_bytes.getFlags(ea) - if idaapi.isCode(flags) or idaapi.isTail(flags) or idaapi.isAlign(flags): + if ida_bytes.isCode(flags) or ida_bytes.isTail(flags) or ida_bytes.isAlign(flags): return False - if idaapi.isUnknown(flags): - flags = idaapi.FF_BYTE + if ida_bytes.isUnknown(flags): + flags = ida_bytes.FF_BYTE - if idaapi.isStruct(flags): - ti = idaapi.opinfo_t() - assert idaapi.get_opinfo(ea, 0, flags, ti), "get_opinfo() failed" - itemsize = idaapi.get_data_elsize(ea, flags, ti) + if ida_bytes.isStruct(flags): + ti = ida_nalt.opinfo_t() + assert ida_bytes.get_opinfo(ea, 0, flags, ti), "get_opinfo() failed" + itemsize = ida_bytes.get_data_elsize(ea, flags, ti) tid = ti.tid else: - itemsize = idaapi.get_item_size(ea) + itemsize = ida_bytes.get_item_size(ea) tid = BADADDR - return idaapi.do_data_ex(ea, flags, itemsize*nitems, tid) + return ida_bytes.do_data_ex(ea, flags, itemsize*nitems, tid) def MakeStr(ea, endea): @@ -713,7 +743,7 @@ def MakeStr(ea, endea): @note: The type of an existing string is returned by GetStringType() """ - return idaapi.make_ascii_string(ea, 0 if endea == BADADDR else endea - ea, GetLongPrm(INF_STRTYPE)) + return ida_bytes.make_ascii_string(ea, 0 if endea == BADADDR else endea - ea, GetLongPrm(INF_STRTYPE)) def MakeData(ea, flags, size, tid): @@ -727,7 +757,7 @@ def MakeData(ea, flags, size, tid): @return: 1-ok, 0-failure """ - return idaapi.do_data_ex(ea, flags, size, tid) + return ida_bytes.do_data_ex(ea, flags, size, tid) def MakeByte(ea): @@ -738,7 +768,7 @@ def MakeByte(ea): @return: 1-ok, 0-failure """ - return idaapi.doByte(ea, 1) + return ida_bytes.doByte(ea, 1) def MakeWord(ea): @@ -749,7 +779,7 @@ def MakeWord(ea): @return: 1-ok, 0-failure """ - return idaapi.doWord(ea, 2) + return ida_bytes.doWord(ea, 2) def MakeDword(ea): @@ -760,7 +790,7 @@ def MakeDword(ea): @return: 1-ok, 0-failure """ - return idaapi.doDwrd(ea, 4) + return ida_bytes.doDwrd(ea, 4) def MakeQword(ea): @@ -771,7 +801,7 @@ def MakeQword(ea): @return: 1-ok, 0-failure """ - return idaapi.doQwrd(ea, 8) + return ida_bytes.doQwrd(ea, 8) def MakeOword(ea): @@ -782,7 +812,7 @@ def MakeOword(ea): @return: 1-ok, 0-failure """ - return idaapi.doOwrd(ea, 16) + return ida_bytes.doOwrd(ea, 16) def MakeYword(ea): @@ -793,7 +823,7 @@ def MakeYword(ea): @return: 1-ok, 0-failure """ - return idaapi.doYwrd(ea, 32) + return ida_bytes.doYwrd(ea, 32) def MakeFloat(ea): @@ -804,7 +834,7 @@ def MakeFloat(ea): @return: 1-ok, 0-failure """ - return idaapi.doFloat(ea, 4) + return ida_bytes.doFloat(ea, 4) def MakeDouble(ea): @@ -815,7 +845,7 @@ def MakeDouble(ea): @return: 1-ok, 0-failure """ - return idaapi.doDouble(ea, 8) + return ida_bytes.doDouble(ea, 8) def MakePackReal(ea): @@ -826,7 +856,7 @@ def MakePackReal(ea): @return: 1-ok, 0-failure """ - return idaapi.doPackReal(ea, idaapi.ph_get_tbyte_size()) + return ida_bytes.doPackReal(ea, ida_idp.ph_get_tbyte_size()) def MakeTbyte(ea): @@ -837,7 +867,7 @@ def MakeTbyte(ea): @return: 1-ok, 0-failure """ - return idaapi.doTbyt(ea, idaapi.ph_get_tbyte_size()) + return ida_bytes.doTbyt(ea, ida_idp.ph_get_tbyte_size()) def MakeStructEx(ea, size, strname): @@ -851,12 +881,12 @@ def MakeStructEx(ea, size, strname): @return: 1-ok, 0-failure """ - strid = idaapi.get_struc_id(strname) + strid = ida_struct.get_struc_id(strname) if size == -1: - size = idaapi.get_struc_size(strid) + size = ida_struct.get_struc_size(strid) - return idaapi.doStruct(ea, size, strid) + return ida_bytes.doStruct(ea, size, strid) def MakeCustomDataEx(ea, size, dtid, fid): @@ -870,7 +900,7 @@ def MakeCustomDataEx(ea, size, dtid, fid): @return: 1-ok, 0-failure """ - return idaapi.doCustomData(ea, size, dtid, fid) + return ida_bytes.doCustomData(ea, size, dtid, fid) @@ -886,7 +916,7 @@ def MakeAlign(ea, count, align): @return: 1-ok, 0-failure """ - return idaapi.doAlign(ea, count, align) + return ida_bytes.doAlign(ea, count, align) def MakeLocal(start, end, location, name): @@ -906,7 +936,7 @@ def MakeLocal(start, end, location, name): If there is no function at 'start' then this function. will fail. """ - func = idaapi.get_func(start) + func = ida_funcs.get_func(start) if not func: return 0 @@ -917,35 +947,35 @@ def MakeLocal(start, end, location, name): if m: # Location in the form of [bp+xx] - register = idaapi.str2reg(m.group(1)) + register = ida_idp.str2reg(m.group(1)) offset = int(m.group(2), 0) - frame = idaapi.get_frame(func) + frame = ida_frame.get_frame(func) if register == -1 or not frame: return 0 offset += func.frsize - member = idaapi.get_member(frame, offset) + member = ida_struct.get_member(frame, offset) if member: # Member already exists, rename it - if idaapi.set_member_name(frame, offset, name): + if ida_struct.set_member_name(frame, offset, name): return 1 else: return 0 else: # No member at the offset, create a new one - if idaapi.add_struc_member(frame, - name, - offset, - idaapi.byteflag(), - None, 1) == 0: + if ida_struct.add_struc_member(frame, + name, + offset, + ida_bytes.byteflag(), + None, 1) == 0: return 1 else: return 0 else: # Location as simple register name - return idaapi.add_regvar(func, start, end, location, name, None) + return ida_frame.add_regvar(func, start, end, location, name, None) def MakeUnkn(ea, flags): @@ -957,7 +987,7 @@ def MakeUnkn(ea, flags): @return: None """ - return idaapi.do_unknown(ea, flags) + return ida_bytes.do_unknown(ea, flags) def MakeUnknown(ea, size, flags): @@ -970,16 +1000,16 @@ def MakeUnknown(ea, size, flags): @return: None """ - return idaapi.do_unknown_range(ea, size, flags) + return ida_bytes.do_unknown_range(ea, size, flags) -DOUNK_SIMPLE = idaapi.DOUNK_SIMPLE # simply undefine the specified item -DOUNK_EXPAND = idaapi.DOUNK_EXPAND # propogate undefined items, for example - # if removing an instruction removes all - # references to the next instruction, then - # plan to convert to unexplored the next - # instruction too. -DOUNK_DELNAMES = idaapi.DOUNK_DELNAMES # delete any names at the specified address(es) +DOUNK_SIMPLE = ida_bytes.DOUNK_SIMPLE # simply undefine the specified item +DOUNK_EXPAND = ida_bytes.DOUNK_EXPAND # propogate undefined items, for example + # if removing an instruction removes all + # references to the next instruction, then + # plan to convert to unexplored the next + # instruction too. +DOUNK_DELNAMES = ida_bytes.DOUNK_DELNAMES # delete any names at the specified address(es) def SetArrayFormat(ea, flags, litems, align): @@ -1022,7 +1052,7 @@ def OpBinary(ea, n): @note: the data items use only the type of the first operand """ - return idaapi.op_bin(ea, n) + return ida_bytes.op_bin(ea, n) def OpOctal(ea, n): @@ -1035,7 +1065,7 @@ def OpOctal(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_oct(ea, n) + return ida_bytes.op_oct(ea, n) def OpDecimal(ea, n): @@ -1048,7 +1078,7 @@ def OpDecimal(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_dec(ea, n) + return ida_bytes.op_dec(ea, n) def OpHex(ea, n): @@ -1061,7 +1091,7 @@ def OpHex(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_hex(ea, n) + return ida_bytes.op_hex(ea, n) def OpChr(ea, n): @@ -1072,7 +1102,7 @@ def OpChr(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_chr(ea, n) + return ida_bytes.op_chr(ea, n) def OpOff(ea, n, base): @@ -1114,10 +1144,10 @@ def OpOff(ea, n, base): @param base: base of the offset as a linear address If base == BADADDR then the current operand becomes non-offset """ - return idaapi.set_offset(ea, n, base) + return ida_offset.set_offset(ea, n, base) -OPND_OUTER = idaapi.OPND_OUTER # outer offset base +OPND_OUTER = ida_bytes.OPND_OUTER # outer offset base def OpOffEx(ea, n, reftype, target, base, tdelta): @@ -1148,19 +1178,19 @@ def OpOffEx(ea, n, reftype, target, base, tdelta): @return: success (boolean) """ - return idaapi.op_offset(ea, n, reftype, target, base, tdelta) + return ida_offset.op_offset(ea, n, reftype, target, base, tdelta) -REF_OFF8 = idaapi.REF_OFF8 # 8bit full offset -REF_OFF16 = idaapi.REF_OFF16 # 16bit full offset -REF_OFF32 = idaapi.REF_OFF32 # 32bit full offset -REF_LOW8 = idaapi.REF_LOW8 # low 8bits of 16bit offset -REF_LOW16 = idaapi.REF_LOW16 # low 16bits of 32bit offset -REF_HIGH8 = idaapi.REF_HIGH8 # high 8bits of 16bit offset -REF_HIGH16 = idaapi.REF_HIGH16 # high 16bits of 32bit offset -REF_VHIGH = idaapi.REF_VHIGH # high ph.high_fixup_bits of 32bit offset (processor dependent) -REF_VLOW = idaapi.REF_VLOW # low (32-ph.high_fixup_bits) of 32bit offset (processor dependent) -REF_OFF64 = idaapi.REF_OFF64 # 64bit full offset +REF_OFF8 = ida_nalt.REF_OFF8 # 8bit full offset +REF_OFF16 = ida_nalt.REF_OFF16 # 16bit full offset +REF_OFF32 = ida_nalt.REF_OFF32 # 32bit full offset +REF_LOW8 = ida_nalt.REF_LOW8 # low 8bits of 16bit offset +REF_LOW16 = ida_nalt.REF_LOW16 # low 16bits of 32bit offset +REF_HIGH8 = ida_nalt.REF_HIGH8 # high 8bits of 16bit offset +REF_HIGH16 = ida_nalt.REF_HIGH16 # high 16bits of 32bit offset +REF_VHIGH = ida_nalt.REF_VHIGH # high ph.high_fixup_bits of 32bit offset (processor dependent) +REF_VLOW = ida_nalt.REF_VLOW # low (32-ph.high_fixup_bits) of 32bit offset (processor dependent) +REF_OFF64 = ida_nalt.REF_OFF64 # 64bit full offset REFINFO_RVA = 0x10 # based reference (rva) REFINFO_PASTEND = 0x20 # reference past an item it may point to an nonexistitng # do not destroy alignment dirs @@ -1184,7 +1214,7 @@ def OpSeg(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_seg(ea, n) + return ida_bytes.op_seg(ea, n) def OpNumber(ea, n): @@ -1197,7 +1227,7 @@ def OpNumber(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_num(ea, n) + return ida_bytes.op_num(ea, n) def OpFloat(ea, n): @@ -1212,7 +1242,7 @@ def OpFloat(ea, n): @return: 1-ok, 0-failure """ - return idaapi.op_flt(ea, n) + return ida_bytes.op_flt(ea, n) def OpAlt(ea, n, opstr): @@ -1229,7 +1259,7 @@ def OpAlt(ea, n, opstr): @note: IDA will not check the specified operand, it will simply display it instead of the orginal representation of the operand. """ - return idaapi.set_forced_operand(ea, n, opstr) + return ida_bytes.set_forced_operand(ea, n, opstr) def OpSign(ea, n): @@ -1242,7 +1272,7 @@ def OpSign(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.toggle_sign(ea, n) + return ida_bytes.toggle_sign(ea, n) def OpNot(ea, n): @@ -1255,7 +1285,7 @@ def OpNot(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - idaapi.toggle_bnot(ea, n) + ida_bytes.toggle_bnot(ea, n) return True @@ -1277,7 +1307,7 @@ def OpEnumEx(ea, n, enumid, serial): There could be 256 symbolic constants with the same value in the enumeration. """ - return idaapi.op_enum(ea, n, enumid, serial) + return ida_bytes.op_enum(ea, n, enumid, serial) def OpStroffEx(ea, n, strid, delta): @@ -1294,9 +1324,9 @@ def OpStroffEx(ea, n, strid, delta): between the structure base and the pointer into the structure. """ - path = idaapi.tid_array(1) + path = ida_pro.tid_array(1) path[0] = strid - return idaapi.op_stroff(ea, n, path.cast(), 1, delta) + return ida_bytes.op_stroff(ea, n, path.cast(), 1, delta) def OpStkvar(ea, n): @@ -1309,7 +1339,7 @@ def OpStkvar(ea, n): - 1 - the second, third and all other operands - -1 - all operands """ - return idaapi.op_stkvar(ea, n) + return ida_bytes.op_stkvar(ea, n) def OpHigh(ea, n, target): @@ -1326,7 +1356,7 @@ def OpHigh(ea, n, target): - -1 - all operands @param target: the full value (all 32bits) of the offset """ - return idaapi.op_offset(ea, n, idaapi.REF_HIGH16, target) + return ida_offset.op_offset(ea, n, ida_nalt.REF_HIGH16, target) def MakeVar(ea): @@ -1341,7 +1371,7 @@ def MakeVar(ea): Nothing else, no additional analysis is performed. This function may disappear in the future. """ - idaapi.doVar(ea, 1) + ida_bytes.doVar(ea, 1) def ExtLinA(ea, n, line): @@ -1359,8 +1389,8 @@ def ExtLinA(ea, n, line): additional line #149, your line will not be displayed. MAX_ITEM_LINES is defined in IDA.CFG """ - idaapi.update_extra_cmt(ea, idaapi.E_PREV + n, line) - idaapi.doExtra(ea) + ida_lines.update_extra_cmt(ea, ida_lines.E_PREV + n, line) + ida_bytes.doExtra(ea) def ExtLinB(ea, n, line): @@ -1378,8 +1408,8 @@ def ExtLinB(ea, n, line): and there is no additional line #149, your line will not be displayed. MAX_ITEM_LINES is defined in IDA.CFG """ - idaapi.update_extra_cmt(ea, idaapi.E_NEXT + n, line) - idaapi.doExtra(ea) + ida_lines.update_extra_cmt(ea, ida_lines.E_NEXT + n, line) + ida_bytes.doExtra(ea) def DelExtLnA(ea, n): @@ -1391,7 +1421,7 @@ def DelExtLnA(ea, n): @return: None """ - idaapi.del_extra_cmt(ea, idaapi.E_PREV + n) + ida_lines.del_extra_cmt(ea, ida_lines.E_PREV + n) def DelExtLnB(ea, n): @@ -1403,7 +1433,7 @@ def DelExtLnB(ea, n): @return: None """ - idaapi.del_extra_cmt(ea, idaapi.E_NEXT + n) + ida_lines.del_extra_cmt(ea, ida_lines.E_NEXT + n) def SetManualInsn(ea, insn): @@ -1416,7 +1446,7 @@ def SetManualInsn(ea, insn): @note: IDA will not check the specified instruction, it will simply display it instead of the orginal representation. """ - return idaapi.set_manual_insn(ea, insn) + return ida_bytes.set_manual_insn(ea, insn) def GetManualInsn(ea): @@ -1427,7 +1457,7 @@ def GetManualInsn(ea): @note: This function returns value set by SetManualInsn earlier. """ - return idaapi.get_manual_insn(ea) + return ida_bytes.get_manual_insn(ea) def PatchDbgByte(ea,value): @@ -1439,7 +1469,7 @@ def PatchDbgByte(ea,value): @return: 1 if successful, 0 if not """ - return idaapi.put_dbg_byte(ea, value) + return ida_dbg.put_dbg_byte(ea, value) def PatchByte(ea, value): @@ -1456,7 +1486,7 @@ def PatchByte(ea, value): or the debugger is not running, and the IDB has value 'value' at address 'ea already. """ - return idaapi.patch_byte(ea, value) + return ida_bytes.patch_byte(ea, value) def PatchWord(ea, value): @@ -1472,7 +1502,7 @@ def PatchWord(ea, value): or the debugger is not running, and the IDB has value 'value' at address 'ea already. """ - return idaapi.patch_word(ea, value) + return ida_bytes.patch_word(ea, value) def PatchDword(ea, value): @@ -1488,7 +1518,7 @@ def PatchDword(ea, value): or the debugger is not running, and the IDB has value 'value' at address 'ea already. """ - return idaapi.patch_long(ea, value) + return ida_bytes.patch_long(ea, value) def PatchQword(ea, value): @@ -1504,7 +1534,7 @@ def PatchQword(ea, value): or the debugger is not running, and the IDB has value 'value' at address 'ea already. """ - return idaapi.patch_qword(ea, value) + return ida_bytes.patch_qword(ea, value) def SetFlags(ea, flags): @@ -1517,7 +1547,7 @@ def SetFlags(ea, flags): @param ea: adress @param flags: new flags value """ - return idaapi.setFlags(ea, flags) + return ida_bytes.setFlags(ea, flags) def SetRegEx(ea, reg, value, tag): """ @@ -1534,9 +1564,9 @@ def SetRegEx(ea, reg, value, tag): See also SetReg() compatibility macro. """ - reg = idaapi.str2reg(reg); + reg = ida_idp.str2reg(reg); if reg >= 0: - return idaapi.splitSRarea1(ea, reg, value, tag) + return ida_srarea.splitSRarea1(ea, reg, value, tag) else: return False @@ -1554,14 +1584,14 @@ def AutoMark2(start, end, queuetype): it. There are several autoanalysis queue types. IDA will process all queries from the first queue and then switch to the second queue, etc. """ - return idaapi.auto_mark_range(start, end, queuetype) + return ida_auto.auto_mark_range(start, end, queuetype) def AutoUnmark(start, end, queuetype): """ Remove range of addresses from a queue. """ - return idaapi.autoUnmark(start, end, queuetype) + return ida_auto.autoUnmark(start, end, queuetype) def AutoMark(ea,qtype): @@ -1570,12 +1600,12 @@ def AutoMark(ea,qtype): """ return AutoMark2(ea,ea+1,qtype) -AU_UNK = idaapi.AU_UNK # make unknown -AU_CODE = idaapi.AU_CODE # convert to instruction -AU_PROC = idaapi.AU_PROC # make function -AU_USED = idaapi.AU_USED # reanalyze -AU_LIBF = idaapi.AU_LIBF # apply a flirt signature (the current signature!) -AU_FINAL = idaapi.AU_FINAL # coagulate unexplored items +AU_UNK = ida_auto.AU_UNK # make unknown +AU_CODE = ida_auto.AU_CODE # convert to instruction +AU_PROC = ida_auto.AU_PROC # make function +AU_USED = ida_auto.AU_USED # reanalyze +AU_LIBF = ida_auto.AU_LIBF # apply a flirt signature (the current signature!) +AU_FINAL = ida_auto.AU_FINAL # coagulate unexplored items #---------------------------------------------------------------------------- @@ -1596,33 +1626,33 @@ def GenerateFile(filetype, path, ea1, ea2, flags): -1 if an error occured OFILE_EXE: 0-can't generate exe file, 1-ok """ - f = idaapi.fopenWT(path) + f = ida_diskio.fopenWT(path) if f: - retval = idaapi.gen_file(filetype, f, ea1, ea2, flags) - idaapi.eclose(f) + retval = ida_loader.gen_file(filetype, f, ea1, ea2, flags) + ida_diskio.eclose(f) return retval else: return -1 # output file types: -OFILE_MAP = idaapi.OFILE_MAP -OFILE_EXE = idaapi.OFILE_EXE -OFILE_IDC = idaapi.OFILE_IDC -OFILE_LST = idaapi.OFILE_LST -OFILE_ASM = idaapi.OFILE_ASM -OFILE_DIF = idaapi.OFILE_DIF +OFILE_MAP = ida_loader.OFILE_MAP +OFILE_EXE = ida_loader.OFILE_EXE +OFILE_IDC = ida_loader.OFILE_IDC +OFILE_LST = ida_loader.OFILE_LST +OFILE_ASM = ida_loader.OFILE_ASM +OFILE_DIF = ida_loader.OFILE_DIF # output control flags: -GENFLG_MAPSEG = idaapi.GENFLG_MAPSEG # map: generate map of segments -GENFLG_MAPNAME = idaapi.GENFLG_MAPNAME # map: include dummy names -GENFLG_MAPDMNG = idaapi.GENFLG_MAPDMNG # map: demangle names -GENFLG_MAPLOC = idaapi.GENFLG_MAPLOC # map: include local names -GENFLG_IDCTYPE = idaapi.GENFLG_IDCTYPE # idc: gen only information about types -GENFLG_ASMTYPE = idaapi.GENFLG_ASMTYPE # asm&lst: gen information about types too -GENFLG_GENHTML = idaapi.GENFLG_GENHTML # asm&lst: generate html (gui version only) -GENFLG_ASMINC = idaapi.GENFLG_ASMINC # asm&lst: gen information only about types +GENFLG_MAPSEG = ida_loader.GENFLG_MAPSEG # map: generate map of segments +GENFLG_MAPNAME = ida_loader.GENFLG_MAPNAME # map: include dummy names +GENFLG_MAPDMNG = ida_loader.GENFLG_MAPDMNG # map: demangle names +GENFLG_MAPLOC = ida_loader.GENFLG_MAPLOC # map: include local names +GENFLG_IDCTYPE = ida_loader.GENFLG_IDCTYPE # idc: gen only information about types +GENFLG_ASMTYPE = ida_loader.GENFLG_ASMTYPE # asm&lst: gen information about types too +GENFLG_GENHTML = ida_loader.GENFLG_GENHTML # asm&lst: generate html (gui version only) +GENFLG_ASMINC = ida_loader.GENFLG_ASMINC # asm&lst: gen information only about types def GenFuncGdl(outfile, title, ea1, ea2, flags): """ @@ -1637,7 +1667,7 @@ def GenFuncGdl(outfile, title, ea1, ea2, flags): @note: If ea2 == BADADDR then ea1 is treated as an address within a function. That function will be flow charted. """ - return idaapi.gen_flow_graph(outfile, title, None, ea1, ea2, flags) + return ida_gdl.gen_flow_graph(outfile, title, None, ea1, ea2, flags) CHART_PRINT_NAMES = 0x1000 # print labels for each block? @@ -1654,7 +1684,7 @@ def GenCallGdl(outfile, title, flags): @param title: graph title @param flags: combination of CHART_GEN_GDL, CHART_WINGRAPH, CHART_NOLIBFUNCS """ - return idaapi.gen_simple_call_chart(outfile, "Generating chart", title, flags) + return ida_gdl.gen_simple_call_chart(outfile, "Generating chart", title, flags) #---------------------------------------------------------------------------- @@ -1666,7 +1696,7 @@ def GetIdaDirectory(): This function returns the directory where IDA.EXE resides """ - return idaapi.idadir("") + return ida_diskio.idadir("") def GetInputFile(): @@ -1675,7 +1705,7 @@ def GetInputFile(): This function returns name of the file being disassembled """ - return idaapi.get_root_filename() + return ida_nalt.get_root_filename() def GetInputFilePath(): @@ -1684,7 +1714,7 @@ def GetInputFilePath(): This function returns the full path of the file being disassembled """ - return idaapi.get_input_file_path() + return ida_nalt.get_input_file_path() def SetInputFilePath(path): @@ -1697,7 +1727,7 @@ def SetInputFilePath(path): @param path: new input file path """ - return idaapi.set_root_filename(path) + return ida_nalt.set_root_filename(path) def GetIdbPath(): @@ -1706,7 +1736,7 @@ def GetIdbPath(): This function returns full path of the current IDB database """ - return idaapi.as_cstr(idaapi.cvar.database_idb) + return ida_idaapi.as_cstr(ida_loader.cvar.database_idb) def GetInputMD5(): @@ -1715,7 +1745,7 @@ def GetInputMD5(): @return: MD5 string or None on error """ - return idaapi.retrieve_input_file_md5() + return ida_nalt.retrieve_input_file_md5() def GetFlags(ea): @@ -1727,7 +1757,7 @@ def GetFlags(ea): @return: 32-bit value of internal flags. See start of IDC.IDC file for explanations. """ - return idaapi.getFlags(ea) + return ida_bytes.getFlags(ea) def IdbByte(ea): @@ -1741,7 +1771,7 @@ def IdbByte(ea): @note: If the current byte size is different from 8 bits, then the returned value may have more 1's. To check if a byte has a value, use this expr: hasValue(GetFlags(ea)) """ - return idaapi.get_db_byte(ea) + return ida_bytes.get_db_byte(ea) def GetManyBytes(ea, size, use_dbg = False): @@ -1758,9 +1788,9 @@ def GetManyBytes(ea, size, use_dbg = False): otherwise a string containing the read bytes """ if use_dbg: - return idaapi.dbg_read_memory(ea, size) + return ida_idd.dbg_read_memory(ea, size) else: - return idaapi.get_many_bytes(ea, size) + return ida_bytes.get_many_bytes(ea, size) def Byte(ea): @@ -1774,14 +1804,14 @@ def Byte(ea): might have more 1's. To check if a byte has a value, use functions hasValue(GetFlags(ea)) """ - return idaapi.get_full_byte(ea) + return ida_bytes.get_full_byte(ea) def __DbgValue(ea, len): - if len not in idaapi.__struct_unpack_table: + if len not in ida_idaapi.__struct_unpack_table: return None - r = idaapi.dbg_read_memory(ea, len) - return None if r is None else struct.unpack((">" if idaapi.cvar.inf.mf else "<") + idaapi.__struct_unpack_table[len][1], r)[0] + r = ida_idd.dbg_read_memory(ea, len) + return None if r is None else struct.unpack((">" if ida_ida.cvar.inf.mf else "<") + ida_idaapi.__struct_unpack_table[len][1], r)[0] def DbgByte(ea): @@ -1834,7 +1864,7 @@ def DbgRead(ea, size): Thread-safe function (may be called only from the main thread and debthread) """ - return idaapi.dbg_read_memory(ea, size) + return ida_idd.dbg_read_memory(ea, size) def DbgWrite(ea, data): @@ -1847,10 +1877,10 @@ def DbgWrite(ea, data): Thread-safe function (may be called only from the main thread and debthread) """ - if not idaapi.dbg_can_query(): + if not ida_idd.dbg_can_query(): return -1 elif len(data) > 0: - return idaapi.dbg_write_memory(ea, data) + return ida_idd.dbg_write_memory(ea, data) def GetOriginalByte(ea): @@ -1861,7 +1891,7 @@ def GetOriginalByte(ea): @return: the original value of byte before any patch applied to it """ - return idaapi.get_original_byte(ea) + return ida_bytes.get_original_byte(ea) def Word(ea): @@ -1874,7 +1904,7 @@ def Word(ea): If the current byte size is different from 8 bits, then the returned value might have more 1's. """ - return idaapi.get_full_word(ea) + return ida_bytes.get_full_word(ea) def Dword(ea): @@ -1885,7 +1915,7 @@ def Dword(ea): @return: the value of the double word. If failed returns -1 """ - return idaapi.get_full_long(ea) + return ida_bytes.get_full_long(ea) def Qword(ea): @@ -1896,7 +1926,7 @@ def Qword(ea): @return: the value of the quadro word. If failed, returns -1 """ - return idaapi.get_qword(ea) + return ida_bytes.get_qword(ea) def GetFloat(ea): @@ -1936,7 +1966,7 @@ def LocByName(name): @return: address of the name BADADDR - No such name """ - return idaapi.get_name_ea(BADADDR, name) + return ida_name.get_name_ea(BADADDR, name) def LocByNameEx(fromaddr, name): @@ -1954,7 +1984,7 @@ def LocByNameEx(fromaddr, name): @note: Dummy names (like byte_xxxx where xxxx are hex digits) are parsed by this function to obtain the address. The database is not consulted for them. """ - return idaapi.get_name_ea(fromaddr, name) + return ida_name.get_name_ea(fromaddr, name) def SegByBase(base): @@ -1966,8 +1996,8 @@ def SegByBase(base): @return: linear address of the start of the segment or BADADDR if no such segment """ - sel = idaapi.find_selector(base) - seg = idaapi.get_segm_by_sel(sel) + sel = ida_segment.find_selector(base) + seg = ida_segment.get_segm_by_sel(sel) if seg: return seg.startEA @@ -1979,7 +2009,7 @@ def ScreenEA(): """ Get linear address of cursor """ - return idaapi.get_screen_ea() + return ida_kernwin.get_screen_ea() def GetCurrentLine(): @@ -1988,7 +2018,7 @@ def GetCurrentLine(): @return: string """ - return idaapi.tag_remove(idaapi.get_curline()) + return ida_lines.tag_remove(ida_kernwin.get_curline()) def SelStart(): @@ -1996,7 +2026,7 @@ def SelStart(): Get start address of the selected area returns BADADDR - the user has not selected an area """ - selection, startaddr, endaddr = idaapi.read_selection() + selection, startaddr, endaddr = ida_kernwin.read_selection() if selection == 1: return startaddr @@ -2010,7 +2040,7 @@ def SelEnd(): @return: BADADDR - the user has not selected an area """ - selection, startaddr, endaddr = idaapi.read_selection() + selection, startaddr, endaddr = ida_kernwin.read_selection() if selection == 1: return endaddr @@ -2031,9 +2061,9 @@ def GetReg(ea, reg): so to get paragraph pointed to by the segment register you need to call AskSelector() function. """ - reg = idaapi.str2reg(reg); + reg = ida_idp.str2reg(reg); if reg >= 0: - return idaapi.getSR(ea, reg) + return ida_srarea.getSR(ea, reg) else: return -1 @@ -2045,7 +2075,7 @@ def NextAddr(ea): @return: BADADDR - the specified address in the last used address """ - return idaapi.nextaddr(ea) + return ida_bytes.nextaddr(ea) def PrevAddr(ea): @@ -2056,7 +2086,7 @@ def PrevAddr(ea): @return: BADADDR - the specified address in the first address """ - return idaapi.prevaddr(ea) + return ida_bytes.prevaddr(ea) def NextHead(ea, maxea=BADADDR): @@ -2069,7 +2099,7 @@ def NextHead(ea, maxea=BADADDR): @return: BADADDR - no (more) defined items """ - return idaapi.next_head(ea, maxea) + return ida_bytes.next_head(ea, maxea) def PrevHead(ea, minea=0): @@ -2082,7 +2112,7 @@ def PrevHead(ea, minea=0): @return: BADADDR - no (more) defined items """ - return idaapi.prev_head(ea, minea) + return ida_bytes.prev_head(ea, minea) def NextNotTail(ea): @@ -2095,7 +2125,7 @@ def NextNotTail(ea): @return: BADADDR - no (more) not-tail addresses """ - return idaapi.next_not_tail(ea) + return ida_bytes.next_not_tail(ea) def PrevNotTail(ea): @@ -2108,7 +2138,7 @@ def PrevNotTail(ea): @return: BADADDR - no (more) not-tail addresses """ - return idaapi.prev_not_tail(ea) + return ida_bytes.prev_not_tail(ea) def ItemHead(ea): @@ -2120,7 +2150,7 @@ def ItemHead(ea): @return: the starting address of the item if the current address is unexplored, returns 'ea' """ - return idaapi.get_item_head(ea) + return ida_bytes.get_item_head(ea) def ItemEnd(ea): @@ -2131,7 +2161,7 @@ def ItemEnd(ea): @return: address past end of the item at 'ea' """ - return idaapi.get_item_end(ea) + return ida_bytes.get_item_end(ea) def ItemSize(ea): @@ -2142,7 +2172,7 @@ def ItemSize(ea): @return: 1..n """ - return idaapi.get_item_end(ea) - ea + return ida_bytes.get_item_end(ea) - ea def NameEx(fromaddr, ea): @@ -2162,7 +2192,7 @@ def NameEx(fromaddr, ea): @return: "" - byte has no name """ - name = idaapi.get_name(fromaddr, ea) + name = ida_name.get_name(fromaddr, ea) if not name: return "" @@ -2183,7 +2213,7 @@ def GetTrueNameEx(fromaddr, ea): @return: "" - byte has no name """ - name = idaapi.get_true_name(fromaddr, ea) + name = ida_name.get_true_name(fromaddr, ea) if not name: return "" @@ -2203,7 +2233,7 @@ def Demangle(name, disable_mask): @return: a demangled name If the input name cannot be demangled, returns None """ - return idaapi.demangle_name(name, disable_mask) + return ida_name.demangle_name(name, disable_mask) def GetDisasmEx(ea, flags): @@ -2219,20 +2249,20 @@ def GetDisasmEx(ea, flags): @note: this function may not return exactly the same mnemonics as you see on the screen. """ - text = idaapi.generate_disasm_line(ea, flags) + text = ida_lines.generate_disasm_line(ea, flags) if text: - return idaapi.tag_remove(text) + return ida_lines.tag_remove(text) else: return "" # flags for GetDisasmEx # generate a disassembly line as if # there is an instruction at 'ea' -GENDSM_FORCE_CODE = idaapi.GENDSM_FORCE_CODE +GENDSM_FORCE_CODE = ida_lines.GENDSM_FORCE_CODE # if the instruction consists of several lines, # produce all of them (useful for parallel instructions) -GENDSM_MULTI_LINE = idaapi.GENDSM_MULTI_LINE +GENDSM_MULTI_LINE = ida_lines.GENDSM_MULTI_LINE def GetDisasm(ea): """ @@ -2258,7 +2288,7 @@ def GetMnem(ea): @note: this function may not return exactly the same mnemonics as you see on the screen. """ - res = idaapi.ua_mnem(ea) + res = ida_ua.ua_mnem(ea) if not res: return "" @@ -2278,15 +2308,15 @@ def GetOpnd(ea, n): @return: the current text representation of operand or "" """ - if not isCode(idaapi.get_flags_novalue(ea)): + if not isCode(ida_bytes.get_flags_novalue(ea)): return "" - res = idaapi.ua_outop2(ea, n) + res = ida_ua.ua_outop2(ea, n) if not res: return "" else: - return idaapi.tag_remove(res) + return ida_lines.tag_remove(res) def GetOpType(ea, n): @@ -2300,49 +2330,49 @@ def GetOpType(ea, n): @return: any of o_* constants or -1 on error """ - inslen = idaapi.decode_insn(ea) - return -1 if inslen == 0 else idaapi.cmd.Operands[n].type + inslen = ida_ua.decode_insn(ea) + return -1 if inslen == 0 else ida_ua.cmd.Operands[n].type -o_void = idaapi.o_void # No Operand ---------- -o_reg = idaapi.o_reg # General Register (al,ax,es,ds...) reg -o_mem = idaapi.o_mem # Direct Memory Reference (DATA) addr -o_phrase = idaapi.o_phrase # Memory Ref [Base Reg + Index Reg] phrase -o_displ = idaapi.o_displ # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr -o_imm = idaapi.o_imm # Immediate Value value -o_far = idaapi.o_far # Immediate Far Address (CODE) addr -o_near = idaapi.o_near # Immediate Near Address (CODE) addr -o_idpspec0 = idaapi.o_idpspec0 # Processor specific type -o_idpspec1 = idaapi.o_idpspec1 # Processor specific type -o_idpspec2 = idaapi.o_idpspec2 # Processor specific type -o_idpspec3 = idaapi.o_idpspec3 # Processor specific type -o_idpspec4 = idaapi.o_idpspec4 # Processor specific type -o_idpspec5 = idaapi.o_idpspec5 # Processor specific type +o_void = ida_ua.o_void # No Operand ---------- +o_reg = ida_ua.o_reg # General Register (al,ax,es,ds...) reg +o_mem = ida_ua.o_mem # Direct Memory Reference (DATA) addr +o_phrase = ida_ua.o_phrase # Memory Ref [Base Reg + Index Reg] phrase +o_displ = ida_ua.o_displ # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr +o_imm = ida_ua.o_imm # Immediate Value value +o_far = ida_ua.o_far # Immediate Far Address (CODE) addr +o_near = ida_ua.o_near # Immediate Near Address (CODE) addr +o_idpspec0 = ida_ua.o_idpspec0 # Processor specific type +o_idpspec1 = ida_ua.o_idpspec1 # Processor specific type +o_idpspec2 = ida_ua.o_idpspec2 # Processor specific type +o_idpspec3 = ida_ua.o_idpspec3 # Processor specific type +o_idpspec4 = ida_ua.o_idpspec4 # Processor specific type +o_idpspec5 = ida_ua.o_idpspec5 # Processor specific type # There can be more processor specific types # x86 -o_trreg = idaapi.o_idpspec0 # trace register -o_dbreg = idaapi.o_idpspec1 # debug register -o_crreg = idaapi.o_idpspec2 # control register -o_fpreg = idaapi.o_idpspec3 # floating point register -o_mmxreg = idaapi.o_idpspec4 # mmx register -o_xmmreg = idaapi.o_idpspec5 # xmm register +o_trreg = ida_ua.o_idpspec0 # trace register +o_dbreg = ida_ua.o_idpspec1 # debug register +o_crreg = ida_ua.o_idpspec2 # control register +o_fpreg = ida_ua.o_idpspec3 # floating point register +o_mmxreg = ida_ua.o_idpspec4 # mmx register +o_xmmreg = ida_ua.o_idpspec5 # xmm register # arm -o_reglist = idaapi.o_idpspec1 # Register list (for LDM/STM) -o_creglist = idaapi.o_idpspec2 # Coprocessor register list (for CDP) -o_creg = idaapi.o_idpspec3 # Coprocessor register (for LDC/STC) -o_fpreg_arm = idaapi.o_idpspec4 # Floating point register -o_fpreglist = idaapi.o_idpspec5 # Floating point register list -o_text = (idaapi.o_idpspec5+1) # Arbitrary text stored in the operand +o_reglist = ida_ua.o_idpspec1 # Register list (for LDM/STM) +o_creglist = ida_ua.o_idpspec2 # Coprocessor register list (for CDP) +o_creg = ida_ua.o_idpspec3 # Coprocessor register (for LDC/STC) +o_fpreg_arm = ida_ua.o_idpspec4 # Floating point register +o_fpreglist = ida_ua.o_idpspec5 # Floating point register list +o_text = (ida_ua.o_idpspec5+1) # Arbitrary text stored in the operand # ppc -o_spr = idaapi.o_idpspec0 # Special purpose register -o_twofpr = idaapi.o_idpspec1 # Two FPRs -o_shmbme = idaapi.o_idpspec2 # SH & MB & ME -o_crf = idaapi.o_idpspec3 # crfield x.reg -o_crb = idaapi.o_idpspec4 # crbit x.reg -o_dcr = idaapi.o_idpspec5 # Device control register +o_spr = ida_ua.o_idpspec0 # Special purpose register +o_twofpr = ida_ua.o_idpspec1 # Two FPRs +o_shmbme = ida_ua.o_idpspec2 # SH & MB & ME +o_crf = ida_ua.o_idpspec3 # crfield x.reg +o_crb = ida_ua.o_idpspec4 # crbit x.reg +o_dcr = ida_ua.o_idpspec5 # Device control register def GetOperandValue(ea, n): """ @@ -2361,20 +2391,20 @@ def GetOperandValue(ea, n): operand is a register phrase => phrase number otherwise => -1 """ - inslen = idaapi.decode_insn(ea) + inslen = ida_ua.decode_insn(ea) if inslen == 0: return -1 - op = idaapi.cmd.Operands[n] + op = ida_ua.cmd.Operands[n] if not op: return -1 - if op.type in [ idaapi.o_mem, idaapi.o_far, idaapi.o_near, idaapi.o_displ ]: + if op.type in [ ida_ua.o_mem, ida_ua.o_far, ida_ua.o_near, ida_ua.o_displ ]: value = op.addr - elif op.type == idaapi.o_reg: + elif op.type == ida_ua.o_reg: value = op.reg - elif op.type == idaapi.o_imm: + elif op.type == ida_ua.o_imm: value = op.value - elif op.type == idaapi.o_phrase: + elif op.type == ida_ua.o_phrase: value = op.phrase else: value = -1 @@ -2391,7 +2421,7 @@ def LineA(ea, num): @return: anterior line string """ - return idaapi.get_extra_cmt(ea, idaapi.E_PREV + num) + return ida_lines.get_extra_cmt(ea, ida_lines.E_PREV + num) def LineB(ea, num): @@ -2403,7 +2433,7 @@ def LineB(ea, num): @return: posterior line string """ - return idaapi.get_extra_cmt(ea, idaapi.E_NEXT + num) + return ida_lines.get_extra_cmt(ea, ida_lines.E_NEXT + num) def GetCommentEx(ea, repeatable): @@ -2416,7 +2446,7 @@ def GetCommentEx(ea, repeatable): @return: string or None if it fails """ - return idaapi.get_cmt(ea, repeatable) + return ida_bytes.get_cmt(ea, repeatable) def CommentEx(ea, repeatable): @@ -2443,16 +2473,16 @@ def AltOp(ea, n): @return: string or None if it fails """ - return idaapi.get_forced_operand(ea, n) + return ida_bytes.get_forced_operand(ea, n) -ASCSTR_C = idaapi.ASCSTR_TERMCHR # C-style ASCII string -ASCSTR_PASCAL = idaapi.ASCSTR_PASCAL # Pascal-style ASCII string (length byte) -ASCSTR_LEN2 = idaapi.ASCSTR_LEN2 # Pascal-style, length is 2 bytes -ASCSTR_UNICODE = idaapi.ASCSTR_UNICODE # Unicode string -ASCSTR_LEN4 = idaapi.ASCSTR_LEN4 # Pascal-style, length is 4 bytes -ASCSTR_ULEN2 = idaapi.ASCSTR_ULEN2 # Pascal-style Unicode, length is 2 bytes -ASCSTR_ULEN4 = idaapi.ASCSTR_ULEN4 # Pascal-style Unicode, length is 4 bytes -ASCSTR_LAST = idaapi.ASCSTR_LAST # Last string type +ASCSTR_C = ida_nalt.ASCSTR_TERMCHR # C-style ASCII string +ASCSTR_PASCAL = ida_nalt.ASCSTR_PASCAL # Pascal-style ASCII string (length byte) +ASCSTR_LEN2 = ida_nalt.ASCSTR_LEN2 # Pascal-style, length is 2 bytes +ASCSTR_UNICODE = ida_nalt.ASCSTR_UNICODE # Unicode string +ASCSTR_LEN4 = ida_nalt.ASCSTR_LEN4 # Pascal-style, length is 4 bytes +ASCSTR_ULEN2 = ida_nalt.ASCSTR_ULEN2 # Pascal-style Unicode, length is 2 bytes +ASCSTR_ULEN4 = ida_nalt.ASCSTR_ULEN4 # Pascal-style Unicode, length is 4 bytes +ASCSTR_LAST = ida_nalt.ASCSTR_LAST # Last string type def GetString(ea, length = -1, strtype = ASCSTR_C): """ @@ -2464,9 +2494,9 @@ def GetString(ea, length = -1, strtype = ASCSTR_C): @return: string contents or empty string """ if length == -1: - length = idaapi.get_max_ascii_length(ea, strtype, idaapi.ALOPT_IGNHEADS) + length = ida_bytes.get_max_ascii_length(ea, strtype, ida_bytes.ALOPT_IGNHEADS) - return idaapi.get_ascii_contents2(ea, length, strtype) + return ida_bytes.get_ascii_contents2(ea, length, strtype) def GetStringType(ea): @@ -2477,9 +2507,9 @@ def GetStringType(ea): @return: One of ASCSTR_... constants """ - ti = idaapi.opinfo_t() + ti = ida_nalt.opinfo_t() - if idaapi.get_opinfo(ea, 0, GetFlags(ea), ti): + if ida_bytes.get_opinfo(ea, 0, GetFlags(ea), ti): return ti.strtype else: return None @@ -2489,22 +2519,22 @@ def GetStringType(ea): # flag is combination of the following bits # returns BADADDR - not found -def FindVoid (ea, flag): return idaapi.find_void(ea, flag) -def FindCode (ea, flag): return idaapi.find_code(ea, flag) -def FindData (ea, flag): return idaapi.find_data(ea, flag) -def FindUnexplored (ea, flag): return idaapi.find_unknown(ea, flag) -def FindExplored (ea, flag): return idaapi.find_defined(ea, flag) -def FindImmediate (ea, flag, value): return idaapi.find_imm(ea, flag, value) +def FindVoid (ea, flag): return ida_search.find_void(ea, flag) +def FindCode (ea, flag): return ida_search.find_code(ea, flag) +def FindData (ea, flag): return ida_search.find_data(ea, flag) +def FindUnexplored (ea, flag): return ida_search.find_unknown(ea, flag) +def FindExplored (ea, flag): return ida_search.find_defined(ea, flag) +def FindImmediate (ea, flag, value): return ida_search.find_imm(ea, flag, value) -SEARCH_UP = idaapi.SEARCH_UP # search backward -SEARCH_DOWN = idaapi.SEARCH_DOWN # search forward -SEARCH_NEXT = idaapi.SEARCH_NEXT # start the search at the next/prev item - # useful only for FindText() and FindBinary() -SEARCH_CASE = idaapi.SEARCH_CASE # search case-sensitive - # (only for bin&txt search) -SEARCH_REGEX = idaapi.SEARCH_REGEX # enable regular expressions (only for text) -SEARCH_NOBRK = idaapi.SEARCH_NOBRK # don't test ctrl-break -SEARCH_NOSHOW = idaapi.SEARCH_NOSHOW # don't display the search progress +SEARCH_UP = ida_search.SEARCH_UP # search backward +SEARCH_DOWN = ida_search.SEARCH_DOWN # search forward +SEARCH_NEXT = ida_search.SEARCH_NEXT # start the search at the next/prev item + # useful only for FindText() and FindBinary() +SEARCH_CASE = ida_search.SEARCH_CASE # search case-sensitive + # (only for bin&txt search) +SEARCH_REGEX = ida_search.SEARCH_REGEX # enable regular expressions (only for text) +SEARCH_NOBRK = ida_search.SEARCH_NOBRK # don't test ctrl-break +SEARCH_NOSHOW = ida_search.SEARCH_NOSHOW # don't display the search progress def FindText(ea, flag, y, x, searchstr): """ @@ -2516,7 +2546,7 @@ def FindText(ea, flag, y, x, searchstr): @return: ea of result or BADADDR if not found """ - return idaapi.find_text(ea, y, x, searchstr, flag) + return ida_search.find_text(ea, y, x, searchstr, flag) def FindBinary(ea, flag, searchstr, radix=16): @@ -2530,8 +2560,8 @@ def FindBinary(ea, flag, searchstr, radix=16): @note: Example: "41 42" - find 2 bytes 41h,42h (radix is 16) """ - endea = flag & 1 and idaapi.cvar.inf.maxEA or idaapi.cvar.inf.minEA - return idaapi.find_binary(ea, endea, searchstr, radix, flag) + endea = flag & 1 and ida_ida.cvar.inf.maxEA or ida_ida.cvar.inf.minEA + return ida_search.find_binary(ea, endea, searchstr, radix, flag) #---------------------------------------------------------------------------- @@ -2545,7 +2575,7 @@ def ChangeConfig(directive): @note: If the directives are erroneous, a fatal error will be generated. The settings are permanent: effective for the current session and the next ones """ - return Eval('ChangeConfig("%s")' % idaapi.str2user(directive)) + return Eval('ChangeConfig("%s")' % ida_kernwin.str2user(directive)) # The following functions allow you to set/get common parameters. @@ -2554,10 +2584,10 @@ def ChangeConfig(directive): def GetLongPrm(offset): """ """ - val = _IDC_GetAttr(idaapi.cvar.inf, _INFMAP, offset) + val = _IDC_GetAttr(ida_ida.cvar.inf, _INFMAP, offset) if offset == INF_PROCNAME: # procName is a character array - val = idaapi.as_cstr(val) + val = ida_idaapi.as_cstr(val) return val def GetShortPrm(offset): @@ -2568,20 +2598,24 @@ def GetCharPrm (offset): return GetLongPrm(offset) -def SetLongPrm (offset, value): - """ - """ +def _invoke_idc_setprm(vtype, offset, value): if offset == INF_PROCNAME: - raise NotImplementedError, "Please use idaapi.set_processor_type() to change processor" - return _IDC_SetAttr(idaapi.cvar.inf, _INFMAP, offset, value) + raise NotImplementedError, "Please use ida_idp.set_processor_type() to change processor" + # We really want to go through IDC's equivalent, because it might + # have side-effects (i.e., send a notification, etc...) + return Eval("Set%sPrm(%d, %d)" % (vtype, offset, value)) + + +def SetLongPrm (offset, value): + return _invoke_idc_setprm("Long", offset, value) def SetShortPrm(offset, value): - SetLongPrm(offset, value) + return _invoke_idc_setprm("Short", offset, value) def SetCharPrm (offset, value): - SetLongPrm(offset, value) + return _invoke_idc_setprm("Char", offset, value) INF_VERSION = 3 # short; Version of database @@ -2594,8 +2628,6 @@ LFLG_64BIT = 0x04 # 64-bit program? LFLG_DBG_NOPATH = 0x08 # do not store input full path LFLG_SNAPSHOT = 0x10 # is memory snapshot? # in debugger process options -LFLG_8ALIGN4 = 0x40 # 4 byte alignment for 8byte scalars - # (__int64/double) inside structures INF_DEMNAMES = 14 # char; display demangled names as: DEMNAM_CMNT = 0 # comments DEMNAM_NAME = 1 # regular names @@ -2819,7 +2851,15 @@ INF_SIZEOF_SHORT = 189 INF_SIZEOF_LONG = 190 INF_SIZEOF_LLONG = 191 INF_CHANGE_COUNTER = 192 # database change counter; keeps track of byte and segment modifications -INF_SIZEOF_LDBL = 196 # uchar; sizeof(long double) +INF_SIZEOF_LDBL = 196 # uchar; sizeof(long double) +INF_ABINAME = 201 # char[16]; ABI name +INF_ABIBITS = 217 # uint32; ABI features: +ABI_8ALIGN4 = 0x00000001 # 4 byte alignment for 8byte scalars (__int64/double) inside structures? +ABI_PACK_STKARGS = 0x00000002 # do not align stack arguments to stack slots +ABI_BIGARG_ALIGN = 0x00000004 # use natural type alignment for argument if the alignment exceeds native word size (e.g. __int64 argument should be 8byte aligned on some 32bit platforms) +ABI_STACK_LDBL = 0x00000008 # long double areuments are passed on stack +ABI_STACK_VARARGS = 0x00000010 # varargs are always passed on stack (even when there are free registers) +ABI_HARD_FLOAT = 0x00000020 # use the floating-point register set INF_REFCMTS = 221 # uchar; number of comment lines to generate for refs # to ASCII string or demangled name # 0 - such comments won't be generated at all @@ -2966,7 +3006,7 @@ INF_MARGIN : (False, 'margin'), # ushort; max length of data lines INF_LENXREF : (False, 'lenxref'), # ushort; max length of line with xrefs INF_LPREFIX : (False, 'lprefix'), # char[16];prefix of local names INF_LPREFIXLEN : (False, 'lprefixlen'), # uchar; length of the lprefix -INF_COMPILER : (False, 'cc') # uchar; compiler +INF_COMPILER : (False, 'cc'), # uchar; compiler #INF_MODEL = 184 # uchar; memory model & calling convention #INF_SIZEOF_INT = 185 # uchar; sizeof(int) @@ -2976,6 +3016,7 @@ INF_COMPILER : (False, 'cc') # uchar; compiler #INF_SIZEOF_SHORT = 189 #INF_SIZEOF_LONG = 190 #INF_SIZEOF_LLONG = 191 +INF_ABIBITS : (False, 'abibits') # uint32; ABI features } @@ -2995,7 +3036,7 @@ def SetProcessorType (processor, level): means that if the processor type can't be set, IDA should display an error message and exit. """ - return idaapi.set_processor_type(processor, level) + return ida_idp.set_processor_type(processor, level) def SetTargetAssembler(asmidx): """ @@ -3005,12 +3046,12 @@ def SetTargetAssembler(asmidx): @return: 1-ok, 0-failed """ - return idaapi.set_target_assembler(asmidx) + return ida_idp.set_target_assembler(asmidx) -SETPROC_COMPAT = idaapi.SETPROC_COMPAT -SETPROC_ALL = idaapi.SETPROC_ALL -SETPROC_USER = idaapi.SETPROC_USER -SETPROC_FATAL = idaapi.SETPROC_FATAL +SETPROC_COMPAT = ida_idp.SETPROC_COMPAT +SETPROC_ALL = ida_idp.SETPROC_ALL +SETPROC_USER = ida_idp.SETPROC_USER +SETPROC_FATAL = ida_idp.SETPROC_FATAL def SetPrcsr(processor): return SetProcessorType(processor, SETPROC_COMPAT) @@ -3025,8 +3066,8 @@ def Batch(batch): @return: old balue of batch flag """ - batch_prev = idaapi.cvar.batch - idaapi.cvar.batch = batch + batch_prev = ida_kernwin.cvar.batch + ida_kernwin.cvar.batch = batch return batch_prev @@ -3043,7 +3084,7 @@ def AskStr(defval, prompt): @return: the entered string or None. """ - return idaapi.askstr(0, defval, prompt) + return ida_kernwin.askstr(0, defval, prompt) def AskFile(forsave, mask, prompt): @@ -3056,7 +3097,7 @@ def AskFile(forsave, mask, prompt): @return: the selected file or None. """ - return idaapi.askfile_c(forsave, mask, prompt) + return ida_kernwin.askfile_c(forsave, mask, prompt) def AskAddr(defval, prompt): @@ -3069,7 +3110,7 @@ def AskAddr(defval, prompt): @return: the entered address or BADADDR. """ - return idaapi.askaddr(defval, prompt) + return ida_kernwin.askaddr(defval, prompt) def AskLong(defval, prompt): @@ -3082,7 +3123,7 @@ def AskLong(defval, prompt): @return: the entered number or -1. """ - return idaapi.asklong(defval, prompt) + return ida_kernwin.asklong(defval, prompt) def ProcessUiAction(name, flags=0): @@ -3093,7 +3134,7 @@ def ProcessUiAction(name, flags=0): @param flags: Reserved. Must be zero @return: Boolean """ - return idaapi.process_ui_action(name, flags) + return ida_kernwin.process_ui_action(name, flags) def AskSeg(defval, prompt): @@ -3106,7 +3147,7 @@ def AskSeg(defval, prompt): @return: the entered segment selector or BADSEL. """ - return idaapi.askseg(defval, prompt) + return ida_kernwin.askseg(defval, prompt) def AskIdent(defval, prompt): @@ -3119,7 +3160,7 @@ def AskIdent(defval, prompt): @return: the entered identifier or None. """ - return idaapi.askident(defval, prompt) + return ida_kernwin.askident(defval, prompt) def AskYN(defval, prompt): @@ -3132,7 +3173,7 @@ def AskYN(defval, prompt): @return: -1:cancel,0-no,1-ok """ - return idaapi.askyn_c(defval, prompt) + return ida_kernwin.askyn_c(defval, prompt) def Message(msg): @@ -3143,7 +3184,7 @@ def Message(msg): This function can be used to debug IDC scripts """ - idaapi.msg(msg) + ida_kernwin.msg(msg) def UMessage(msg): @@ -3157,7 +3198,7 @@ def UMessage(msg): This function can be used to debug IDC scripts """ - idaapi.umsg(msg) + ida_kernwin.umsg(msg) def Warning(msg): @@ -3170,7 +3211,7 @@ def Warning(msg): The user will be able to hide messages if they appear twice in a row on the screen """ - idaapi.warning(msg) + ida_kernwin.warning(msg) def Fatal(format): @@ -3179,7 +3220,7 @@ def Fatal(format): @param format: message to print """ - idaapi.error(format) + ida_kernwin.error(format) def SetStatus(status): @@ -3190,7 +3231,7 @@ def SetStatus(status): @return: the previous status. """ - return idaapi.setStat(status) + return ida_auto.setStat(status) IDA_STATUS_READY = 0 # READY IDA is idle @@ -3203,14 +3244,14 @@ def Refresh(): """ Refresh all disassembly views """ - idaapi.refresh_idaview_anyway() + ida_kernwin.refresh_idaview_anyway() def RefreshLists(): """ Refresh all list views (names, functions, etc) """ - idaapi.refresh_lists() + ida_kernwin.refresh_lists() #---------------------------------------------------------------------------- @@ -3227,9 +3268,9 @@ def AskSelector(sel): @note: selector values are always in paragraphs """ - s = idaapi.sel_pointer() - base = idaapi.ea_pointer() - res,tmp = idaapi.getn_selector(sel, s.cast(), base.cast()) + s = ida_pro.sel_pointer() + base = ida_pro.ea_pointer() + res,tmp = ida_segment.getn_selector(sel, s.cast(), base.cast()) if not res: return sel @@ -3248,7 +3289,7 @@ def FindSelector(val): @note: selector values are always in paragraphs """ - return idaapi.find_selector(val) & 0xFFFF + return ida_segment.find_selector(val) & 0xFFFF def SetSelector(sel, value): @@ -3264,7 +3305,7 @@ def SetSelector(sel, value): if 'sel' == 'val' then the selector is destroyed because it has no significance """ - return idaapi.set_selector(sel, value) + return ida_segment.set_selector(sel, value) def DelSelector(sel): @@ -3277,7 +3318,7 @@ def DelSelector(sel): @note: if the selector is found, it will be deleted """ - return idaapi.del_selector(sel) + return ida_segment.del_selector(sel) def FirstSeg(): @@ -3287,7 +3328,7 @@ def FirstSeg(): @return: address of the start of the first segment BADADDR - no segments are defined """ - seg = idaapi.get_first_seg() + seg = ida_segment.get_first_seg() if not seg: return BADADDR else: @@ -3303,7 +3344,7 @@ def NextSeg(ea): @return: start of the next segment BADADDR - no next segment """ - nextseg = idaapi.get_next_seg(ea) + nextseg = ida_segment.get_next_seg(ea) if not nextseg: return BADADDR else: @@ -3321,7 +3362,7 @@ def SegStart(ea): @return: start of segment BADADDR - the specified address doesn't belong to any segment """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return BADADDR @@ -3338,7 +3379,7 @@ def SegEnd(ea): @return: end of segment (an address past end of the segment) BADADDR - the specified address doesn't belong to any segment """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return BADADDR @@ -3354,12 +3395,12 @@ def SegName(ea): @return: "" - no segment at the specified address """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return "" else: - name = idaapi.get_true_segm_name(seg) + name = ida_segment.get_true_segm_name(seg) if not name: return "" @@ -3386,31 +3427,31 @@ def AddSegEx(startea, endea, base, use32, align, comb, flags): @return: 0-failed, 1-ok """ - s = idaapi.segment_t() + s = ida_segment.segment_t() s.startEA = startea s.endEA = endea - s.sel = idaapi.setup_selector(base) + s.sel = ida_segment.setup_selector(base) s.bitness = use32 s.align = align s.comb = comb - return idaapi.add_segm_ex(s, "", "", flags) + return ida_segment.add_segm_ex(s, "", "", flags) -ADDSEG_NOSREG = idaapi.ADDSEG_NOSREG # set all default segment register values - # to BADSELs - # (undefine all default segment registers) -ADDSEG_OR_DIE = idaapi. ADDSEG_OR_DIE # qexit() if can't add a segment -ADDSEG_NOTRUNC = idaapi.ADDSEG_NOTRUNC # don't truncate the new segment at the beginning - # of the next segment if they overlap. - # destroy/truncate old segments instead. -ADDSEG_QUIET = idaapi.ADDSEG_QUIET # silent mode, no "Adding segment..." in the messages window -ADDSEG_FILLGAP = idaapi.ADDSEG_FILLGAP # If there is a gap between the new segment - # and the previous one, and this gap is less - # than 64K, then fill the gap by extending the - # previous segment and adding .align directive - # to it. This way we avoid gaps between segments. - # Too many gaps lead to a virtual array failure. - # It can not hold more than ~1000 gaps. -ADDSEG_SPARSE = idaapi.ADDSEG_SPARSE # Use sparse storage method for the new segment +ADDSEG_NOSREG = ida_segment.ADDSEG_NOSREG # set all default segment register values + # to BADSELs + # (undefine all default segment registers) +ADDSEG_OR_DIE = ida_segment. ADDSEG_OR_DIE # qexit() if can't add a segment +ADDSEG_NOTRUNC = ida_segment.ADDSEG_NOTRUNC # don't truncate the new segment at the beginning + # of the next segment if they overlap. + # destroy/truncate old segments instead. +ADDSEG_QUIET = ida_segment.ADDSEG_QUIET # silent mode, no "Adding segment..." in the messages window +ADDSEG_FILLGAP = ida_segment.ADDSEG_FILLGAP # If there is a gap between the new segment + # and the previous one, and this gap is less + # than 64K, then fill the gap by extending the + # previous segment and adding .align directive + # to it. This way we avoid gaps between segments. + # Too many gaps lead to a virtual array failure. + # It can not hold more than ~1000 gaps. +ADDSEG_SPARSE = ida_segment.ADDSEG_SPARSE # Use sparse storage method for the new segment def AddSeg(startea, endea, base, use32, align, comb): return AddSegEx(startea, endea, base, use32, align, comb, ADDSEG_NOSREG) @@ -3424,12 +3465,12 @@ def DelSeg(ea, flags): @return: boolean success """ - return idaapi.del_segm(ea, flags) + return ida_segment.del_segm(ea, flags) -SEGMOD_KILL = idaapi.SEGMOD_KILL # disable addresses if segment gets +SEGMOD_KILL = ida_segment.SEGMOD_KILL # disable addresses if segment gets # shrinked or deleted -SEGMOD_KEEP = idaapi.SEGMOD_KEEP # keep information (code & data, etc) -SEGMOD_SILENT = idaapi.SEGMOD_SILENT # be silent +SEGMOD_KEEP = ida_segment.SEGMOD_KEEP # keep information (code & data, etc) +SEGMOD_SILENT = ida_segment.SEGMOD_SILENT # be silent def SetSegBounds(ea, startea, endea, flags): @@ -3443,8 +3484,8 @@ def SetSegBounds(ea, startea, endea, flags): @return: boolean success """ - return idaapi.set_segm_start(ea, startea, flags) & \ - idaapi.set_segm_end(ea, endea, flags) + return ida_segment.set_segm_start(ea, startea, flags) & \ + ida_segment.set_segm_end(ea, endea, flags) def RenameSeg(ea, name): @@ -3456,12 +3497,12 @@ def RenameSeg(ea, name): @return: success (boolean) """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return False - return idaapi.set_segm_name(seg, name) + return ida_segment.set_segm_name(seg, name) def SetSegClass(ea, segclass): @@ -3473,12 +3514,12 @@ def SetSegClass(ea, segclass): @return: success (boolean) """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return False - return idaapi.set_segm_class(seg, segclass) + return ida_segment.set_segm_class(seg, segclass) def SegAlign(ea, alignment): @@ -3493,22 +3534,22 @@ def SegAlign(ea, alignment): return SetSegmentAttr(ea, SEGATTR_ALIGN, alignment) -saAbs = idaapi.saAbs # Absolute segment. -saRelByte = idaapi.saRelByte # Relocatable, byte aligned. -saRelWord = idaapi.saRelWord # Relocatable, word (2-byte, 16-bit) aligned. -saRelPara = idaapi.saRelPara # Relocatable, paragraph (16-byte) aligned. -saRelPage = idaapi.saRelPage # Relocatable, aligned on 256-byte boundary - # (a "page" in the original Intel specification). -saRelDble = idaapi.saRelDble # Relocatable, aligned on a double word - # (4-byte) boundary. This value is used by - # the PharLap OMF for the same alignment. -saRel4K = idaapi.saRel4K # This value is used by the PharLap OMF for - # page (4K) alignment. It is not supported - # by LINK. -saGroup = idaapi.saGroup # Segment group -saRel32Bytes = idaapi.saRel32Bytes # 32 bytes -saRel64Bytes = idaapi.saRel64Bytes # 64 bytes -saRelQword = idaapi.saRelQword # 8 bytes +saAbs = ida_segment.saAbs # Absolute segment. +saRelByte = ida_segment.saRelByte # Relocatable, byte aligned. +saRelWord = ida_segment.saRelWord # Relocatable, word (2-byte, 16-bit) aligned. +saRelPara = ida_segment.saRelPara # Relocatable, paragraph (16-byte) aligned. +saRelPage = ida_segment.saRelPage # Relocatable, aligned on 256-byte boundary + # (a "page" in the original Intel specification). +saRelDble = ida_segment.saRelDble # Relocatable, aligned on a double word + # (4-byte) boundary. This value is used by + # the PharLap OMF for the same alignment. +saRel4K = ida_segment.saRel4K # This value is used by the PharLap OMF for + # page (4K) alignment. It is not supported + # by LINK. +saGroup = ida_segment.saGroup # Segment group +saRel32Bytes = ida_segment.saRel32Bytes # 32 bytes +saRel64Bytes = ida_segment.saRel64Bytes # 64 bytes +saRelQword = ida_segment.saRelQword # 8 bytes def SegComb(segea, comb): @@ -3523,15 +3564,15 @@ def SegComb(segea, comb): return SetSegmentAttr(segea, SEGATTR_COMB, comb) -scPriv = idaapi.scPriv # Private. Do not combine with any other program - # segment. -scPub = idaapi.scPub # Public. Combine by appending at an offset that - # meets the alignment requirement. -scPub2 = idaapi.scPub2 # As defined by Microsoft, same as C=2 (public). -scStack = idaapi.scStack # Stack. Combine as for C=2. This combine type - # forces byte alignment. -scCommon = idaapi.scCommon # Common. Combine by overlay using maximum size. -scPub3 = idaapi.scPub3 # As defined by Microsoft, same as C=2 (public). +scPriv = ida_segment.scPriv # Private. Do not combine with any other program + # segment. +scPub = ida_segment.scPub # Public. Combine by appending at an offset that + # meets the alignment requirement. +scPub2 = ida_segment.scPub2 # As defined by Microsoft, same as C=2 (public). +scStack = ida_segment.scStack # Stack. Combine as for C=2. This combine type + # forces byte alignment. +scCommon = ida_segment.scCommon # Common. Combine by overlay using maximum size. +scPub3 = ida_segment.scPub3 # As defined by Microsoft, same as C=2 (public). def SetSegAddressing(ea, bitness): @@ -3543,7 +3584,7 @@ def SetSegAddressing(ea, bitness): @return: success (boolean) """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return False @@ -3561,7 +3602,7 @@ def SegByName(segname): @return: segment selector or BADADDR """ - seg = idaapi.get_segm_by_name(segname) + seg = ida_segment.get_segm_by_name(segname) if not seg: return BADADDR @@ -3579,11 +3620,11 @@ def SetSegDefReg(ea, reg, value): @param reg: name of segment register @param value: default value of the segment register. -1-undefined. """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) - reg = idaapi.str2reg(reg); + reg = ida_idp.str2reg(reg); if seg and reg >= 0: - return idaapi.SetDefaultRegisterValue(seg, reg, value) + return ida_srarea.SetDefaultRegisterValue(seg, reg, value) else: return False @@ -3597,7 +3638,7 @@ def SetSegmentType(segea, segtype): @return: !=0 - ok """ - seg = idaapi.getseg(segea) + seg = ida_segment.getseg(segea) if not seg: return False @@ -3606,22 +3647,22 @@ def SetSegmentType(segea, segtype): return seg.update() -SEG_NORM = idaapi.SEG_NORM -SEG_XTRN = idaapi.SEG_XTRN # * segment with 'extern' definitions - # no instructions are allowed -SEG_CODE = idaapi.SEG_CODE # pure code segment -SEG_DATA = idaapi.SEG_DATA # pure data segment -SEG_IMP = idaapi.SEG_IMP # implementation segment -SEG_GRP = idaapi.SEG_GRP # * group of segments - # no instructions are allowed -SEG_NULL = idaapi.SEG_NULL # zero-length segment -SEG_UNDF = idaapi.SEG_UNDF # undefined segment type -SEG_BSS = idaapi.SEG_BSS # uninitialized segment -SEG_ABSSYM = idaapi.SEG_ABSSYM # * segment with definitions of absolute symbols - # no instructions are allowed -SEG_COMM = idaapi.SEG_COMM # * segment with communal definitions - # no instructions are allowed -SEG_IMEM = idaapi.SEG_IMEM # internal processor memory & sfr (8051) +SEG_NORM = ida_segment.SEG_NORM +SEG_XTRN = ida_segment.SEG_XTRN # * segment with 'extern' definitions + # no instructions are allowed +SEG_CODE = ida_segment.SEG_CODE # pure code segment +SEG_DATA = ida_segment.SEG_DATA # pure data segment +SEG_IMP = ida_segment.SEG_IMP # implementation segment +SEG_GRP = ida_segment.SEG_GRP # * group of segments + # no instructions are allowed +SEG_NULL = ida_segment.SEG_NULL # zero-length segment +SEG_UNDF = ida_segment.SEG_UNDF # undefined segment type +SEG_BSS = ida_segment.SEG_BSS # uninitialized segment +SEG_ABSSYM = ida_segment.SEG_ABSSYM # * segment with definitions of absolute symbols + # no instructions are allowed +SEG_COMM = ida_segment.SEG_COMM # * segment with communal definitions + # no instructions are allowed +SEG_IMEM = ida_segment.SEG_IMEM # internal processor memory & sfr (8051) def GetSegmentAttr(segea, attr): @@ -3631,10 +3672,10 @@ def GetSegmentAttr(segea, attr): @param segea: any address within segment @param attr: one of SEGATTR_... constants """ - seg = idaapi.getseg(segea) + seg = ida_segment.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][1]) + return ida_segment.get_defsr(seg, _SEGATTRMAP[attr][1]) else: return _IDC_GetAttr(seg, _SEGATTRMAP, attr) @@ -3650,10 +3691,10 @@ def SetSegmentAttr(segea, attr, value): Also some of them should be modified using special functions like SetSegAddressing, etc. """ - seg = idaapi.getseg(segea) + seg = ida_segment.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][1], value) + ida_segment.set_defsr(seg, _SEGATTRMAP[attr][1], value) else: _IDC_SetAttr(seg, _SEGATTRMAP, attr, value) return seg.update() @@ -3742,10 +3783,10 @@ def MoveSegm(ea, to, flags): @returns: MOVE_SEGM_... error code """ - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if not seg: return MOVE_SEGM_PARAM - return idaapi.move_segm(seg, to, flags) + return ida_segment.move_segm(seg, to, flags) MSF_SILENT = 0x0001 # don't display a "please wait" box on the screen @@ -3773,7 +3814,7 @@ def rebase_program(delta, flags): @returns: error code MOVE_SEGM_... """ - return idaapi.rebase_program(delta, flags) + return ida_segment.rebase_program(delta, flags) def SetStorageType(startEA, endEA, stt): @@ -3786,7 +3827,7 @@ def SetStorageType(startEA, endEA, stt): @returns: 0 - ok, otherwise internal error code """ - return idaapi.change_storage_type(startEA, endEA, stt) + return ida_bytes.change_storage_type(startEA, endEA, stt) STT_VA = 0 # regular storage: virtual arrays, an explicit flag for each byte @@ -3811,7 +3852,7 @@ XREF_USER = 32 # All user-specified xref types def AddCodeXref(From, To, flowtype): """ """ - return idaapi.add_cref(From, To, flowtype) + return ida_xref.add_cref(From, To, flowtype) def DelCodeXref(From, To, undef): @@ -3822,7 +3863,7 @@ def DelCodeXref(From, To, undef): @returns: 1 - planned to be made undefined """ - return idaapi.del_cref(From, To, undef) + return ida_xref.del_cref(From, To, undef) # The following functions include the ordinary flows: @@ -3831,28 +3872,28 @@ def Rfirst(From): """ Get first code xref from 'From' """ - return idaapi.get_first_cref_from(From) + return ida_xref.get_first_cref_from(From) def Rnext(From, current): """ Get next code xref from """ - return idaapi.get_next_cref_from(From, current) + return ida_xref.get_next_cref_from(From, current) def RfirstB(To): """ Get first code xref to 'To' """ - return idaapi.get_first_cref_to(To) + return ida_xref.get_first_cref_to(To) def RnextB(To, current): """ Get next code xref to 'To' """ - return idaapi.get_next_cref_to(To, current) + return ida_xref.get_next_cref_to(To, current) # The following functions don't take into account the ordinary flows: @@ -3860,78 +3901,78 @@ def Rfirst0(From): """ Get first xref from 'From' """ - return idaapi.get_first_fcref_from(From) + return ida_xref.get_first_fcref_from(From) def Rnext0(From, current): """ Get next xref from """ - return idaapi.get_next_fcref_from(From, current) + return ida_xref.get_next_fcref_from(From, current) def RfirstB0(To): """ Get first xref to 'To' """ - return idaapi.get_first_fcref_to(To) + return ida_xref.get_first_fcref_to(To) def RnextB0(To, current): """ Get next xref to 'To' """ - return idaapi.get_next_fcref_to(To, current) + return ida_xref.get_next_fcref_to(To, current) # Data reference types (combine with XREF_USER!): -dr_O = idaapi.dr_O # Offset -dr_W = idaapi.dr_W # Write -dr_R = idaapi.dr_R # Read -dr_T = idaapi.dr_T # Text (names in manual operands) -dr_I = idaapi.dr_I # Informational +dr_O = ida_xref.dr_O # Offset +dr_W = ida_xref.dr_W # Write +dr_R = ida_xref.dr_R # Read +dr_T = ida_xref.dr_T # Text (names in manual operands) +dr_I = ida_xref.dr_I # Informational def add_dref(From, To, drefType): """ Create Data Ref """ - return idaapi.add_dref(From, To, drefType) + return ida_xref.add_dref(From, To, drefType) def del_dref(From, To): """ Unmark Data Ref """ - return idaapi.del_dref(From, To) + return ida_xref.del_dref(From, To) def Dfirst(From): """ Get first data xref from 'From' """ - return idaapi.get_first_dref_from(From) + return ida_xref.get_first_dref_from(From) def Dnext(From, current): """ Get next data xref from 'From' """ - return idaapi.get_next_dref_from(From, current) + return ida_xref.get_next_dref_from(From, current) def DfirstB(To): """ Get first data xref to 'To' """ - return idaapi.get_first_dref_to(To) + return ida_xref.get_first_dref_to(To) def DnextB(To, current): """ Get next data xref to 'To' """ - return idaapi.get_next_dref_to(To, current) + return ida_xref.get_next_dref_to(To, current) def XrefType(): @@ -3974,11 +4015,11 @@ def LoadFile(filepath, pos, ea, size): @return: 0 - error, 1 - ok """ - li = idaapi.open_linput(filepath, False) + li = ida_diskio.open_linput(filepath, False) if li: - retval = idaapi.file2base(li, pos, ea, ea+size, False) - idaapi.close_linput(li) + retval = ida_loader.file2base(li, pos, ea, ea+size, False) + ida_diskio.close_linput(li) return retval else: return 0 @@ -3998,14 +4039,14 @@ def SaveFile(filepath, pos, ea, size): @return: 0 - error, 1 - ok """ if ( os.path.isfile(filepath) ): - of = idaapi.fopenM(filepath) + of = ida_diskio.fopenM(filepath) else: - of = idaapi.fopenWB(filepath) + of = ida_diskio.fopenWB(filepath) if of: - retval = idaapi.base2file(of, pos, ea, ea+size) - idaapi.eclose(of) + retval = ida_loader.base2file(of, pos, ea, ea+size) + ida_diskio.eclose(of) return retval else: return 0 @@ -4044,7 +4085,7 @@ def writestr(handle, s): # F U N C T I O N S # ---------------------------------------------------------------------------- -def MakeFunction(start, end = idaapi.BADADDR): +def MakeFunction(start, end = ida_idaapi.BADADDR): """ Create a function @@ -4060,7 +4101,7 @@ def MakeFunction(start, end = idaapi.BADADDR): @note: an instruction should be present at the start address """ - return idaapi.add_func(start, end) + return ida_funcs.add_func(start, end) def DelFunction(ea): @@ -4071,7 +4112,7 @@ def DelFunction(ea): @return: !=0 - ok """ - return idaapi.del_func(ea) + return ida_funcs.del_func(ea) def SetFunctionEnd(ea, end): @@ -4083,7 +4124,7 @@ def SetFunctionEnd(ea, end): @return: !=0 - ok """ - return idaapi.func_setend(ea, end) + return ida_funcs.func_setend(ea, end) def NextFunction(ea): @@ -4095,7 +4136,7 @@ def NextFunction(ea): @return: BADADDR - no more functions otherwise returns the next function start address """ - func = idaapi.get_next_func(ea) + func = ida_funcs.get_next_func(ea) if not func: return BADADDR @@ -4112,7 +4153,7 @@ def PrevFunction(ea): @return: BADADDR - no more functions otherwise returns the previous function start address """ - func = idaapi.get_prev_func(ea) + func = ida_funcs.get_prev_func(ea) if not func: return BADADDR @@ -4129,7 +4170,7 @@ def GetFunctionAttr(ea, attr): @return: BADADDR - error otherwise returns the attribute value """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) return _IDC_GetAttr(func, _FUNCATTRMAP, attr) if func else BADADDR @@ -4144,11 +4185,11 @@ def SetFunctionAttr(ea, attr, value): @return: 1-ok, 0-failed """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if func: _IDC_SetAttr(func, _FUNCATTRMAP, attr, value) - return idaapi.update_func(func) + return ida_funcs.update_func(func) return 0 @@ -4202,7 +4243,7 @@ def GetFunctionFlags(ea): @return: -1 - function doesn't exist otherwise returns the flags """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return -1 @@ -4210,33 +4251,33 @@ def GetFunctionFlags(ea): return func.flags -FUNC_NORET = idaapi.FUNC_NORET # function doesn't return -FUNC_FAR = idaapi.FUNC_FAR # far function -FUNC_LIB = idaapi.FUNC_LIB # library function -FUNC_STATIC = idaapi.FUNC_STATICDEF # static function -FUNC_FRAME = idaapi.FUNC_FRAME # function uses frame pointer (BP) -FUNC_USERFAR = idaapi.FUNC_USERFAR # user has specified far-ness - # of the function -FUNC_HIDDEN = idaapi.FUNC_HIDDEN # a hidden function -FUNC_THUNK = idaapi.FUNC_THUNK # thunk (jump) function -FUNC_BOTTOMBP = idaapi.FUNC_BOTTOMBP # BP points to the bottom of the stack frame -FUNC_NORET_PENDING = idaapi.FUNC_NORET_PENDING # Function 'non-return' analysis - # must be performed. This flag is - # verified upon func_does_return() -FUNC_SP_READY = idaapi.FUNC_SP_READY # SP-analysis has been performed - # If this flag is on, the stack - # change points should not be not - # modified anymore. Currently this - # analysis is performed only for PC -FUNC_PURGED_OK = idaapi.FUNC_PURGED_OK # 'argsize' field has been validated. - # If this bit is clear and 'argsize' - # is 0, then we do not known the real - # number of bytes removed from - # the stack. This bit is handled - # by the processor module. -FUNC_TAIL = idaapi.FUNC_TAIL # This is a function tail. - # Other bits must be clear - # (except FUNC_HIDDEN) +FUNC_NORET = ida_funcs.FUNC_NORET # function doesn't return +FUNC_FAR = ida_funcs.FUNC_FAR # far function +FUNC_LIB = ida_funcs.FUNC_LIB # library function +FUNC_STATIC = ida_funcs.FUNC_STATICDEF # static function +FUNC_FRAME = ida_funcs.FUNC_FRAME # function uses frame pointer (BP) +FUNC_USERFAR = ida_funcs.FUNC_USERFAR # user has specified far-ness + # of the function +FUNC_HIDDEN = ida_funcs.FUNC_HIDDEN # a hidden function +FUNC_THUNK = ida_funcs.FUNC_THUNK # thunk (jump) function +FUNC_BOTTOMBP = ida_funcs.FUNC_BOTTOMBP # BP points to the bottom of the stack frame +FUNC_NORET_PENDING = ida_funcs.FUNC_NORET_PENDING # Function 'non-return' analysis + # must be performed. This flag is + # verified upon func_does_return() +FUNC_SP_READY = ida_funcs.FUNC_SP_READY # SP-analysis has been performed + # If this flag is on, the stack + # change points should not be not + # modified anymore. Currently this + # analysis is performed only for PC +FUNC_PURGED_OK = ida_funcs.FUNC_PURGED_OK # 'argsize' field has been validated. + # If this bit is clear and 'argsize' + # is 0, then we do not known the real + # number of bytes removed from + # the stack. This bit is handled + # by the processor module. +FUNC_TAIL = ida_funcs.FUNC_TAIL # This is a function tail. + # Other bits must be clear + # (except FUNC_HIDDEN) def SetFunctionFlags(ea, flags): @@ -4248,13 +4289,13 @@ def SetFunctionFlags(ea, flags): @return: !=0 - ok """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return 0 else: func.flags = flags - idaapi.update_func(func) + ida_funcs.update_func(func) return 1 @@ -4267,7 +4308,7 @@ def GetFunctionName(ea): @return: null string - function doesn't exist otherwise returns function name """ - name = idaapi.get_func_name(ea) + name = ida_funcs.get_func_name(ea) if not name: return "" @@ -4285,12 +4326,12 @@ def GetFunctionCmt(ea, repeatable): @return: function comment string """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return "" else: - comment = idaapi.get_func_cmt(func, repeatable) + comment = ida_funcs.get_func_cmt(func, repeatable) if not comment: return "" @@ -4307,12 +4348,12 @@ def SetFunctionCmt(ea, cmt, repeatable): @param repeatable: 1: get repeatable comment 0: get regular comment """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return None else: - return idaapi.set_func_cmt(func, cmt, repeatable) + return ida_funcs.set_func_cmt(func, cmt, repeatable) def ChooseFunction(title): @@ -4326,7 +4367,7 @@ def ChooseFunction(title): @return: -1 - user refused to select a function otherwise returns the selected function start address """ - f = idaapi.choose_func(title, idaapi.BADADDR) + f = ida_kernwin.choose_func(title, ida_idaapi.BADADDR) return BADADDR if f is None else f.startEA @@ -4340,7 +4381,7 @@ def GetFuncOffset(ea): formed as 'name+offset' where 'name' is a function name 'offset' is offset within the function else return null string """ - return idaapi.a2funcoff(ea) + return ida_funcs.a2funcoff(ea) def FindFuncEnd(ea): @@ -4353,11 +4394,11 @@ def FindFuncEnd(ea): If a function end cannot be determined, the return BADADDR otherwise return the end address of the new function """ - func = idaapi.func_t() + func = ida_funcs.func_t() - res = idaapi.find_func_bounds(ea, func, idaapi.FIND_FUNC_DEFINE) + res = ida_funcs.find_func_bounds(ea, func, ida_funcs.FIND_FUNC_DEFINE) - if res == idaapi.FIND_FUNC_UNDEF: + if res == ida_funcs.FIND_FUNC_UNDEF: return BADADDR else: return func.endEA @@ -4373,7 +4414,7 @@ def GetFrame(ea): you need to use structure member manipulaion functions with the obtained ID. """ - frame = idaapi.get_frame(ea) + frame = ida_frame.get_frame(ea) if frame: return frame.id @@ -4434,12 +4475,12 @@ def GetFrameSize(ea): function return address in the stack. If the function does't exist, return 0 """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return 0 else: - return idaapi.get_frame_size(func) + return ida_frame.get_frame_size(func) def MakeFrame(ea, lvsize, frregs, argsize): @@ -4455,14 +4496,14 @@ def MakeFrame(ea, lvsize, frregs, argsize): If the function did not have a frame, the frame will be created. Otherwise the frame will be modified """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if func is None: return -1 - frameid = idaapi.add_frame(func, lvsize, frregs, argsize) + frameid = ida_frame.add_frame(func, lvsize, frregs, argsize) if not frameid: - if not idaapi.set_frame_size(func, lvsize, frregs, argsize): + if not ida_frame.set_frame_size(func, lvsize, frregs, argsize): return -1 return func.frame @@ -4478,12 +4519,12 @@ def GetSpd(ea): @return: The difference between the original SP upon entering the function and SP for the specified address """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return None - return idaapi.get_spd(func, ea) + return ida_frame.get_spd(func, ea) def GetSpDiff(ea): @@ -4497,12 +4538,12 @@ def GetSpDiff(ea): If the specified location doesn't contain a SP change point, return 0 Otherwise return delta of SP modification """ - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if not func: return None - return idaapi.get_sp_delta(func, ea) + return ida_frame.get_sp_delta(func, ea) def SetSpDiff(ea, delta): @@ -4515,7 +4556,7 @@ def SetSpDiff(ea, delta): @return: 1-ok, 0-failed """ - return idaapi.add_user_stkpnt(ea, delta) + return ida_frame.add_user_stkpnt(ea, delta) # ---------------------------------------------------------------------------- @@ -4532,10 +4573,10 @@ def AddAutoStkPnt2(func_ea, ea, delta): @param delta: difference between old and new values of SP @return: 1-ok, 0-failed """ - pfn = idaapi.get_func(func_ea) + pfn = ida_funcs.get_func(func_ea) if not pfn: return 0 - return idaapi.add_auto_stkpnt2(pfn, ea, delta) + return ida_frame.add_auto_stkpnt2(pfn, ea, delta) def AddUserStkPnt(ea, delta): """ @@ -4546,7 +4587,7 @@ def AddUserStkPnt(ea, delta): @return: 1-ok, 0-failed """ - return idaapi.add_user_stkpnt(ea, delta); + return ida_frame.add_user_stkpnt(ea, delta); def DelStkPnt(func_ea, ea): """ @@ -4556,10 +4597,10 @@ def DelStkPnt(func_ea, ea): @param ea: linear address @return: 1-ok, 0-failed """ - pfn = idaapi.get_func(func_ea) + pfn = ida_funcs.get_func(func_ea) if not pfn: return 0 - return idaapi.del_stkpnt(pfn, ea) + return ida_frame.del_stkpnt(pfn, ea) def GetMinSpd(func_ea): """ @@ -4569,10 +4610,10 @@ def GetMinSpd(func_ea): @param func_ea: function start @return: BADDADDR - no such function """ - pfn = idaapi.get_func(func_ea) + pfn = ida_funcs.get_func(func_ea) if not pfn: return BADADDR - return idaapi.get_min_spd_ea(pfn) + return ida_frame.get_min_spd_ea(pfn) def RecalcSpd(cur_ea): """ @@ -4581,7 +4622,7 @@ def RecalcSpd(cur_ea): @param cur_ea: linear address of the current instruction @return: 1 - new stkpnt is added, 0 - nothing is changed """ - return idaapi.recalc_spd(cur_ea) + return ida_frame.recalc_spd(cur_ea) @@ -4597,7 +4638,7 @@ def GetEntryPointQty(): @returns: number of entry points """ - return idaapi.get_entry_qty() + return ida_entry.get_entry_qty() def AddEntryPoint(ordinal, ea, name, makecode): @@ -4616,7 +4657,7 @@ def AddEntryPoint(ordinal, ea, name, makecode): @return: 0 - entry point with the specifed ordinal already exists 1 - ok """ - return idaapi.add_entry(ordinal, ea, name, makecode) + return ida_entry.add_entry(ordinal, ea, name, makecode) def GetEntryOrdinal(index): @@ -4628,7 +4669,7 @@ def GetEntryOrdinal(index): @return: 0 if entry point doesn't exist otherwise entry point ordinal """ - return idaapi.get_entry_ordinal(index) + return ida_entry.get_entry_ordinal(index) def GetEntryPoint(ordinal): @@ -4643,7 +4684,7 @@ def GetEntryPoint(ordinal): If entry point address is equal to its ordinal number, then the entry point has no ordinal. """ - return idaapi.get_entry(ordinal) + return ida_entry.get_entry(ordinal) def GetEntryName(ordinal): @@ -4654,7 +4695,7 @@ def GetEntryName(ordinal): @return: entry point name or None """ - return idaapi.get_entry_name(ordinal) + return ida_entry.get_entry_name(ordinal) def RenameEntryPoint(ordinal, name): @@ -4666,7 +4707,7 @@ def RenameEntryPoint(ordinal, name): @return: !=0 - ok """ - return idaapi.rename_entry(ordinal, name) + return ida_entry.rename_entry(ordinal, name) # ---------------------------------------------------------------------------- @@ -4681,7 +4722,7 @@ def GetNextFixupEA(ea): @return: BADADDR - no more fixups otherwise returns the next address with fixup information """ - return idaapi.get_next_fixup_ea(ea) + return ida_fixup.get_next_fixup_ea(ea) def GetPrevFixupEA(ea): @@ -4693,7 +4734,7 @@ def GetPrevFixupEA(ea): @return: BADADDR - no more fixups otherwise returns the previous address with fixup information """ - return idaapi.get_prev_fixup_ea(ea) + return ida_fixup.get_prev_fixup_ea(ea) def GetFixupTgtType(ea): @@ -4705,9 +4746,9 @@ def GetFixupTgtType(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target type: """ - fd = idaapi.fixup_data_t() + fd = ida_fixup.fixup_data_t() - if not idaapi.get_fixup(ea, fd): + if not ida_fixup.get_fixup(ea, fd): return -1 return fd.type @@ -4751,9 +4792,9 @@ def GetFixupTgtSel(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target selector """ - fd = idaapi.fixup_data_t() + fd = ida_fixup.fixup_data_t() - if not idaapi.get_fixup(ea, fd): + if not ida_fixup.get_fixup(ea, fd): return -1 return fd.sel @@ -4768,9 +4809,9 @@ def GetFixupTgtOff(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target offset """ - fd = idaapi.fixup_data_t() + fd = ida_fixup.fixup_data_t() - if not idaapi.get_fixup(ea, fd): + if not ida_fixup.get_fixup(ea, fd): return -1 return fd.off @@ -4785,9 +4826,9 @@ def GetFixupTgtDispl(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target displacement """ - fd = idaapi.fixup_data_t() + fd = ida_fixup.fixup_data_t() - if not idaapi.get_fixup(ea, fd): + if not ida_fixup.get_fixup(ea, fd): return -1 return fd.displacement @@ -4806,13 +4847,13 @@ def SetFixup(ea, fixuptype, targetsel, targetoff, displ): @return: none """ - fd = idaapi.fixup_data_t() + fd = ida_fixup.fixup_data_t() fd.type = fixuptype fd.sel = targetsel fd.off = targetoff fd.displacement = displ - idaapi.set_fixup(ea, fd) + ida_fixup.set_fixup(ea, fd) def DelFixup(ea): @@ -4823,7 +4864,7 @@ def DelFixup(ea): @return: None """ - idaapi.del_fixup(ea) + ida_fixup.del_fixup(ea) #---------------------------------------------------------------------------- @@ -4845,7 +4886,7 @@ def MarkPosition(ea, lnnum, x, y, slot, comment): @return: None """ - curloc = idaapi.curloc() + curloc = ida_moves.curloc() curloc.ea = ea curloc.lnnum = lnnum curloc.x = x @@ -4863,8 +4904,8 @@ def GetMarkedPos(slot): @return: BADADDR - the slot doesn't contain a marked address otherwise returns the marked address """ - curloc = idaapi.curloc() - intp = idaapi.int_pointer() + curloc = ida_moves.curloc() + intp = ida_pro.int_pointer() intp.assign(slot) return curloc.markedpos(intp) @@ -4878,7 +4919,7 @@ def GetMarkComment(slot): @return: None if the slot doesn't contain a marked address otherwise returns the marked address comment """ - curloc = idaapi.curloc() + curloc = ida_moves.curloc() return curloc.markdesc(slot) @@ -4892,7 +4933,7 @@ def GetStrucQty(): @return: number of structure types """ - return idaapi.get_struc_qty() + return ida_struct.get_struc_qty() def GetFirstStrucIdx(): @@ -4913,7 +4954,7 @@ def GetFirstStrucIdx(): can't be changed. Even when the structure type gets deleted, its ID won't be resued in the future. """ - return idaapi.get_first_struc_idx() + return ida_struct.get_first_struc_idx() def GetLastStrucIdx(): @@ -4925,7 +4966,7 @@ def GetLastStrucIdx(): See GetFirstStrucIdx() for the explanation of structure indices and IDs. """ - return idaapi.get_last_struc_idx() + return ida_struct.get_last_struc_idx() def GetNextStrucIdx(index): @@ -4939,7 +4980,7 @@ def GetNextStrucIdx(index): See GetFirstStrucIdx() for the explanation of structure indices and IDs. """ - return idaapi.get_next_struc_idx(index) + return ida_struct.get_next_struc_idx(index) def GetPrevStrucIdx(index): @@ -4953,7 +4994,7 @@ def GetPrevStrucIdx(index): See GetFirstStrucIdx() for the explanation of structure indices and IDs. """ - return idaapi.get_prev_struc_idx(index) + return ida_struct.get_prev_struc_idx(index) def GetStrucIdx(sid): @@ -4967,7 +5008,7 @@ def GetStrucIdx(sid): See GetFirstStrucIdx() for the explanation of structure indices and IDs. """ - return idaapi.get_struc_idx(sid) + return ida_struct.get_struc_idx(sid) def GetStrucId(index): @@ -4980,7 +5021,7 @@ def GetStrucId(index): @note: See GetFirstStrucIdx() for the explanation of structure indices and IDs. """ - return idaapi.get_struc_by_idx(index) + return ida_struct.get_struc_by_idx(index) def GetStrucIdByName(name): @@ -4992,7 +5033,7 @@ def GetStrucIdByName(name): @return: BADADDR if bad structure type name is passed otherwise returns structure ID. """ - return idaapi.get_struc_id(name) + return ida_struct.get_struc_id(name) def GetStrucName(sid): @@ -5004,7 +5045,7 @@ def GetStrucName(sid): @return: None if bad structure type ID is passed otherwise returns structure type name. """ - return idaapi.get_struc_name(sid) + return ida_struct.get_struc_name(sid) def GetStrucComment(sid, repeatable): @@ -5018,7 +5059,7 @@ def GetStrucComment(sid, repeatable): @return: None if bad structure type ID is passed otherwise returns comment. """ - return idaapi.get_struc_cmt(sid, repeatable) + return ida_struct.get_struc_cmt(sid, repeatable) def GetStrucSize(sid): @@ -5030,7 +5071,7 @@ def GetStrucSize(sid): @return: 0 if bad structure type ID is passed otherwise returns size of structure in bytes. """ - return idaapi.get_struc_size(sid) + return ida_struct.get_struc_size(sid) def GetMemberQty(sid): @@ -5046,7 +5087,7 @@ def GetMemberQty(sid): at subsequent byte offsets: member 0 -> offset 0x0, member 1 -> offset 0x1, etc... """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) return -1 if not s else s.memqty @@ -5063,11 +5104,11 @@ def GetMemberId(sid, member_offset): no member at the specified offset. otherwise returns the member id. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) if not m: return -1 @@ -5082,7 +5123,7 @@ def GetStrucPrevOff(sid, offset): @param offset: current offset @return: -1 if bad structure type ID is passed, - idaapi.BADADDR if no (more) offsets in the structure, + ida_idaapi.BADADDR if no (more) offsets in the structure, otherwise returns previous offset in a structure. @note: IDA allows 'holes' between members of a @@ -5096,11 +5137,11 @@ def GetStrucPrevOff(sid, offset): at subsequent byte offsets: member 0 -> offset 0x0, member 1 -> offset 0x1, etc... """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - return idaapi.get_struc_prev_offset(s, offset) + return ida_struct.get_struc_prev_offset(s, offset) def GetStrucNextOff(sid, offset): @@ -5111,7 +5152,7 @@ def GetStrucNextOff(sid, offset): @param offset: current offset @return: -1 if bad structure type ID is passed, - idaapi.BADADDR if no (more) offsets in the structure, + ida_idaapi.BADADDR if no (more) offsets in the structure, otherwise returns next offset in a structure. @note: IDA allows 'holes' between members of a @@ -5125,8 +5166,8 @@ def GetStrucNextOff(sid, offset): at subsequent byte offsets: member 0 -> offset 0x0, member 1 -> offset 0x1, etc... """ - s = idaapi.get_struc(sid) - return -1 if not s else idaapi.get_struc_next_offset(s, offset) + s = ida_struct.get_struc(sid) + return -1 if not s else ida_struct.get_struc_next_offset(s, offset) def GetFirstMember(sid): @@ -5136,7 +5177,7 @@ def GetFirstMember(sid): @param sid: structure type ID @return: -1 if bad structure type ID is passed, - idaapi.BADADDR if structure has no members, + ida_idaapi.BADADDR if structure has no members, otherwise returns offset of the first member. @note: IDA allows 'holes' between members of a @@ -5147,11 +5188,11 @@ def GetFirstMember(sid): at subsequent byte offsets: member 0 -> offset 0x0, member 1 -> offset 0x1, etc... """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - return idaapi.get_struc_first_offset(s) + return ida_struct.get_struc_first_offset(s) def GetLastMember(sid): @@ -5161,7 +5202,7 @@ def GetLastMember(sid): @param sid: structure type ID @return: -1 if bad structure type ID is passed, - idaapi.BADADDR if structure has no members, + ida_idaapi.BADADDR if structure has no members, otherwise returns offset of the last member. @note: IDA allows 'holes' between members of a @@ -5172,11 +5213,11 @@ def GetLastMember(sid): at subsequent byte offsets: member 0 -> offset 0x0, member 1 -> offset 0x1, etc... """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - return idaapi.get_struc_last_offset(s) + return ida_struct.get_struc_last_offset(s) def GetMemberOffset(sid, member_name): @@ -5194,11 +5235,11 @@ def GetMemberOffset(sid, member_name): at subsequent byte offsets: member 0 -> offset 0x0, member 1 -> offset 0x1, etc... """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - m = idaapi.get_member_by_name(s, member_name) + m = ida_struct.get_member_by_name(s, member_name) if not m: return -1 @@ -5220,15 +5261,15 @@ def GetMemberName(sid, member_offset): or no such member in the structure otherwise returns name of the specified member. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return None - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) if not m: return None - return idaapi.get_member_name(m.id) + return ida_struct.get_member_name(m.id) def GetMemberComment(sid, member_offset, repeatable): @@ -5248,15 +5289,15 @@ def GetMemberComment(sid, member_offset, repeatable): or no such member in the structure otherwise returns comment of the specified member. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return None - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) if not m: return None - return idaapi.get_member_cmt(m.id, repeatable) + return ida_struct.get_member_cmt(m.id, repeatable) def GetMemberSize(sid, member_offset): @@ -5275,15 +5316,15 @@ def GetMemberSize(sid, member_offset): otherwise returns size of the specified member in bytes. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return None - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) if not m: return None - return idaapi.get_member_size(m) + return ida_struct.get_member_size(m) def GetMemberFlag(sid, member_offset): @@ -5304,11 +5345,11 @@ def GetMemberFlag(sid, member_offset): then function GetMemberStrid() should be used to get the structure type id. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) return -1 if not m else m.flag @@ -5327,15 +5368,15 @@ def GetMemberStrId(sid, member_offset): otherwise returns structure id of the member. If the current member is not a structure, returns -1. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return -1 - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) if not m: return -1 - cs = idaapi.get_sptr(m) + cs = ida_struct.get_sptr(m) if cs: return cs.id else: @@ -5353,7 +5394,7 @@ def IsUnion(sid): @note: Unions are a special kind of structures """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 @@ -5384,7 +5425,7 @@ def AddStrucEx(index, name, is_union): if index == -1: index = BADADDR - return idaapi.add_struc(index, name, is_union) + return ida_struct.add_struc(index, name, is_union) def DelStruc(sid): @@ -5399,11 +5440,11 @@ def DelStruc(sid): deleted structure type will be displayed as array of bytes. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 - return idaapi.del_struc(s) + return ida_struct.del_struc(s) def SetStrucIdx(sid, index): @@ -5418,11 +5459,11 @@ def SetStrucIdx(sid, index): @note: See GetFirstStrucIdx() for the explanation of structure indices and IDs. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 - return idaapi.set_struc_idx(s, index) + return ida_struct.set_struc_idx(s, index) def SetStrucName(sid, name): @@ -5434,7 +5475,7 @@ def SetStrucName(sid, name): @return: != 0 - ok """ - return idaapi.set_struc_name(sid, name) + return ida_struct.set_struc_name(sid, name) def SetStrucComment(sid, comment, repeatable): @@ -5447,7 +5488,7 @@ def SetStrucComment(sid, comment, repeatable): 0: change regular comment @return: != 0 - ok """ - return idaapi.set_struc_cmt(sid, comment, repeatable) + return ida_struct.set_struc_cmt(sid, comment, repeatable) def AddStrucMember(sid, name, offset, flag, typeid, nbytes, target=-1, tdelta=0, reftype=REF_OFF32): @@ -5481,10 +5522,10 @@ def AddStrucMember(sid, name, offset, flag, typeid, nbytes, target=-1, tdelta=0, """ if isOff0(flag): - return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d, %d, %d, %d);' % (sid, idaapi.str2user(name), offset, flag, typeid, nbytes, + return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d, %d, %d, %d);' % (sid, ida_kernwin.str2user(name), offset, flag, typeid, nbytes, target, tdelta, reftype)) else: - return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d);' % (sid, idaapi.str2user(name), offset, flag, typeid, nbytes)) + return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d);' % (sid, ida_kernwin.str2user(name), offset, flag, typeid, nbytes)) STRUC_ERROR_MEMBER_NAME = -1 # already has member with this name (bad name) @@ -5509,11 +5550,11 @@ def DelStrucMember(sid, member_offset): structure. It treats these 'holes' as unnamed arrays of bytes. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 - return idaapi.del_struc_member(s, member_offset) + return ida_struct.del_struc_member(s, member_offset) def SetMemberName(sid, member_offset, name): @@ -5526,11 +5567,11 @@ def SetMemberName(sid, member_offset, name): @return: != 0 - ok. """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 - return idaapi.set_member_name(s, member_offset, name) + return ida_struct.set_member_name(s, member_offset, name) def SetMemberType(sid, member_offset, flag, typeid, nitems, target=-1, tdelta=0, reftype=REF_OFF32): @@ -5579,15 +5620,15 @@ def SetMemberComment(sid, member_offset, comment, repeatable): @return: != 0 - ok """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 - m = idaapi.get_member(s, member_offset) + m = ida_struct.get_member(s, member_offset) if not m: return 0 - return idaapi.set_member_cmt(m, comment, repeatable) + return ida_struct.set_member_cmt(m, comment, repeatable) def ExpandStruc(sid, offset, delta, recalc): @@ -5600,11 +5641,11 @@ def ExpandStruc(sid, offset, delta, recalc): type is used @return: != 0 - ok """ - s = idaapi.get_struc(sid) + s = ida_struct.get_struc(sid) if not s: return 0 - return idaapi.expand_struc(s, offset, delta, recalc) + return ida_struct.expand_struc(s, offset, delta, recalc) def GetFchunkAttr(ea, attr): @@ -5616,7 +5657,7 @@ def GetFchunkAttr(ea, attr): @return: desired attribute or -1 """ - func = idaapi.get_fchunk(ea) + func = ida_funcs.get_fchunk(ea) return _IDC_GetAttr(func, _FUNCATTRMAP, attr) if func else BADADDR @@ -5631,10 +5672,10 @@ def SetFchunkAttr(ea, attr, value): @return: 0 if failed, 1 if success """ if attr in [ FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER ]: - chunk = idaapi.get_fchunk(ea) + chunk = ida_funcs.get_fchunk(ea) if chunk: _IDC_SetAttr(chunk, _FUNCATTRMAP, attr, value) - return idaapi.update_func(chunk) + return ida_funcs.update_func(chunk) return 0 @@ -5647,7 +5688,7 @@ def GetFchunkReferer(ea, idx): @return: referer address or BADADDR """ - return idaapi.get_fchunk_referer(ea, idx) + return ida_funcs.get_fchunk_referer(ea, idx) def NextFchunk(ea): @@ -5660,7 +5701,7 @@ def NextFchunk(ea): @note: This function enumerates all chunks of all functions in the database """ - func = idaapi.get_next_fchunk(ea) + func = ida_funcs.get_next_fchunk(ea) if func: return func.startEA @@ -5678,7 +5719,7 @@ def PrevFchunk(ea): @note: This function enumerates all chunks of all functions in the database """ - func = idaapi.get_prev_fchunk(ea) + func = ida_funcs.get_prev_fchunk(ea) if func: return func.startEA @@ -5698,12 +5739,12 @@ def AppendFchunk(funcea, ea1, ea2): @note: If a chunk exists at the specified addresses, it must have exactly the specified boundaries """ - func = idaapi.get_func(funcea) + func = ida_funcs.get_func(funcea) if not func: return 0 else: - return idaapi.append_func_tail(func, ea1, ea2) + return ida_funcs.append_func_tail(func, ea1, ea2) def RemoveFchunk(funcea, tailea): @@ -5715,12 +5756,12 @@ def RemoveFchunk(funcea, tailea): @return: 0 if failed, 1 if success """ - func = idaapi.get_func(funcea) + func = ida_funcs.get_func(funcea) if not func: return 0 else: - return idaapi.remove_func_tail(func, tailea) + return ida_funcs.remove_func_tail(func, tailea) def SetFchunkOwner(tailea, funcea): @@ -5730,16 +5771,16 @@ def SetFchunkOwner(tailea, funcea): @param tailea: any address in the function chunk @param funcea: the starting address of the new owner - @return: 0 if failed, 1 if success + @return: False if failed, True if success @note: The new owner must already have the chunk appended before the call """ - tail = idaapi.get_func(tailea) + tail = ida_funcs.get_fchunk(tailea) if not tail: - return 0 + return False else: - return idaapi.set_tail_owner(tail, funcea) + return ida_funcs.set_tail_owner(tail, funcea) def FirstFuncFchunk(funcea): @@ -5752,8 +5793,8 @@ def FirstFuncFchunk(funcea): @note: This function returns the first (main) chunk of the specified function """ - func = idaapi.get_func(funcea) - fci = idaapi.func_tail_iterator_t(func, funcea) + func = ida_funcs.get_func(funcea) + fci = ida_funcs.func_tail_iterator_t(func, funcea) if fci.main(): return fci.chunk().startEA else: @@ -5771,8 +5812,8 @@ def NextFuncFchunk(funcea, tailea): @note: This function returns the next chunk of the specified function """ - func = idaapi.get_func(funcea) - fci = idaapi.func_tail_iterator_t(func, funcea) + func = ida_funcs.get_func(funcea) + fci = ida_funcs.func_tail_iterator_t(func, funcea) if not fci.main(): return BADADDR @@ -5802,7 +5843,7 @@ def GetEnumQty(): @return: number of enumerations """ - return idaapi.get_enum_qty() + return ida_enum.get_enum_qty() def GetnEnum(idx): @@ -5813,7 +5854,7 @@ def GetnEnum(idx): @return: ID of enum or -1 if error """ - return idaapi.getn_enum(idx) + return ida_enum.getn_enum(idx) def GetEnumIdx(enum_id): @@ -5824,7 +5865,7 @@ def GetEnumIdx(enum_id): @return: (0..GetEnumQty()-1) or -1 if error """ - return idaapi.get_enum_idx(enum_id) + return ida_enum.get_enum_idx(enum_id) def GetEnum(name): @@ -5836,7 +5877,7 @@ def GetEnum(name): returns: ID of enum or -1 if no such enum exists """ - return idaapi.get_enum(name) + return ida_enum.get_enum(name) def GetEnumName(enum_id): @@ -5847,7 +5888,7 @@ def GetEnumName(enum_id): @return: name of enum or empty string """ - return idaapi.get_enum_name(enum_id) + return ida_enum.get_enum_name(enum_id) def GetEnumCmt(enum_id, repeatable): @@ -5860,7 +5901,7 @@ def GetEnumCmt(enum_id, repeatable): @return: comment of enum """ - return idaapi.get_enum_cmt(enum_id, repeatable) + return ida_enum.get_enum_cmt(enum_id, repeatable) def GetEnumSize(enum_id): @@ -5872,7 +5913,7 @@ def GetEnumSize(enum_id): @return: number of constants in the enum Returns 0 if enum_id is bad. """ - return idaapi.get_enum_size(enum_id) + return ida_enum.get_enum_size(enum_id) def GetEnumWidth(enum_id): @@ -5886,7 +5927,7 @@ def GetEnumWidth(enum_id): 1-1byte,2-2bytes,3-4bytes,4-8bytes,etc Returns 0 if enum_id is bad or the width is unknown. """ - return idaapi.get_enum_width(enum_id) + return ida_enum.get_enum_width(enum_id) def GetEnumFlag(enum_id): @@ -5901,7 +5942,7 @@ def GetEnumFlag(enum_id): more information about flags. Returns 0 if enum_id is bad. """ - return idaapi.get_enum_flag(enum_id) + return ida_enum.get_enum_flag(enum_id) def GetConstByName(name): @@ -5912,7 +5953,7 @@ def GetConstByName(name): @return: ID of constant or -1 """ - return idaapi.get_enum_member_by_name(name) + return ida_enum.get_enum_member_by_name(name) def GetConstValue(const_id): @@ -5923,7 +5964,7 @@ def GetConstValue(const_id): @return: value of constant or 0 """ - return idaapi.get_enum_member_value(const_id) + return ida_enum.get_enum_member_value(const_id) def GetConstBmask(const_id): @@ -5935,7 +5976,7 @@ def GetConstBmask(const_id): @return: bitmask of constant or 0 ordinary enums have bitmask = -1 """ - return idaapi.get_enum_member_bmask(const_id) + return ida_enum.get_enum_member_bmask(const_id) def GetConstEnum(const_id): @@ -5947,7 +5988,7 @@ def GetConstEnum(const_id): @return: id of enum the constant belongs to. -1 if const_id is bad. """ - return idaapi.get_enum_member_enum(const_id) + return ida_enum.get_enum_member_enum(const_id) def GetConstEx(enum_id, value, serial, bmask): @@ -5965,7 +6006,7 @@ def GetConstEx(enum_id, value, serial, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_enum_member(enum_id, value, serial, bmask) + return ida_enum.get_enum_member(enum_id, value, serial, bmask) def GetFirstBmask(enum_id): @@ -5979,7 +6020,7 @@ def GetFirstBmask(enum_id): All bitmasks are sorted by their values as unsigned longs. """ - return idaapi.get_first_bmask(enum_id) + return ida_enum.get_first_bmask(enum_id) def GetLastBmask(enum_id): @@ -5991,7 +6032,7 @@ def GetLastBmask(enum_id): @return: the biggest bitmask or -1 no bitmasks are defined yet All bitmasks are sorted by their values as unsigned longs. """ - return idaapi.get_last_bmask(enum_id) + return ida_enum.get_last_bmask(enum_id) def GetNextBmask(enum_id, value): @@ -6006,7 +6047,7 @@ def GetNextBmask(enum_id, value): All bitmasks are sorted by their values as unsigned longs. """ - return idaapi.get_next_bmask(enum_id, value) + return ida_enum.get_next_bmask(enum_id, value) def GetPrevBmask(enum_id, value): @@ -6020,7 +6061,7 @@ def GetPrevBmask(enum_id, value): value. -1 no such bitmasks exist. All bitmasks are sorted by their values as unsigned longs. """ - return idaapi.get_prev_bmask(enum_id, value) + return ida_enum.get_prev_bmask(enum_id, value) def GetBmaskName(enum_id, bmask): @@ -6034,7 +6075,7 @@ def GetBmaskName(enum_id, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_bmask_name(enum_id, bmask) + return ida_enum.get_bmask_name(enum_id, bmask) def GetBmaskCmt(enum_id, bmask, repeatable): @@ -6049,7 +6090,7 @@ def GetBmaskCmt(enum_id, bmask, repeatable): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_bmask_cmt(enum_id, bmask, repeatable) + return ida_enum.get_bmask_cmt(enum_id, bmask, repeatable) def SetBmaskName(enum_id, bmask, name): @@ -6064,7 +6105,7 @@ def SetBmaskName(enum_id, bmask, name): """ if bmask < 0: bmask &= BADADDR - return idaapi.set_bmask_name(enum_id, bmask, name) + return ida_enum.set_bmask_name(enum_id, bmask, name) def SetBmaskCmt(enum_id, bmask, cmt, repeatable): @@ -6080,7 +6121,7 @@ def SetBmaskCmt(enum_id, bmask, cmt, repeatable): """ if bmask < 0: bmask &= BADADDR - return idaapi.set_bmask_cmt(enum_id, bmask, cmt, repeatable) + return ida_enum.set_bmask_cmt(enum_id, bmask, cmt, repeatable) def GetFirstConst(enum_id, bmask): @@ -6095,7 +6136,7 @@ def GetFirstConst(enum_id, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_first_enum_member(enum_id, bmask) + return ida_enum.get_first_enum_member(enum_id, bmask) def GetLastConst(enum_id, bmask): @@ -6111,7 +6152,7 @@ def GetLastConst(enum_id, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_last_enum_member(enum_id, bmask) + return ida_enum.get_last_enum_member(enum_id, bmask) def GetNextConst(enum_id, value, bmask): @@ -6128,7 +6169,7 @@ def GetNextConst(enum_id, value, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_next_enum_member(enum_id, value, bmask) + return ida_enum.get_next_enum_member(enum_id, value, bmask) def GetPrevConst(enum_id, value, bmask): @@ -6146,7 +6187,7 @@ def GetPrevConst(enum_id, value, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.get_prev_enum_member(enum_id, value, bmask) + return ida_enum.get_prev_enum_member(enum_id, value, bmask) def GetConstName(const_id): @@ -6157,7 +6198,7 @@ def GetConstName(const_id): Returns: name of constant """ - name = idaapi.get_enum_member_name(const_id) + name = ida_enum.get_enum_member_name(const_id) if not name: return "" @@ -6174,7 +6215,7 @@ def GetConstCmt(const_id, repeatable): @return: comment string """ - cmt = idaapi.get_enum_member_cmt(const_id, repeatable) + cmt = ida_enum.get_enum_member_cmt(const_id, repeatable) if not cmt: return "" @@ -6205,7 +6246,7 @@ def AddEnum(idx, name, flag): """ if idx < 0: idx = idx & SIZE_MAX - return idaapi.add_enum(idx, name, flag) + return ida_enum.add_enum(idx, name, flag) def DelEnum(enum_id): @@ -6216,7 +6257,7 @@ def DelEnum(enum_id): @return: None """ - idaapi.del_enum(enum_id) + ida_enum.del_enum(enum_id) def SetEnumIdx(enum_id, idx): @@ -6236,7 +6277,7 @@ def SetEnumIdx(enum_id, idx): @return: comment string """ - return idaapi.set_enum_idx(enum_id, idx) + return ida_enum.set_enum_idx(enum_id, idx) def SetEnumName(enum_id, name): @@ -6248,7 +6289,7 @@ def SetEnumName(enum_id, name): @return: 1-ok,0-failed """ - return idaapi.set_enum_name(enum_id, name) + return ida_enum.set_enum_name(enum_id, name) def SetEnumCmt(enum_id, cmt, repeatable): @@ -6263,7 +6304,7 @@ def SetEnumCmt(enum_id, cmt, repeatable): @return: 1-ok,0-failed """ - return idaapi.set_enum_cmt(enum_id, cmt, repeatable) + return ida_enum.set_enum_cmt(enum_id, cmt, repeatable) def SetEnumFlag(enum_id, flag): @@ -6276,7 +6317,7 @@ def SetEnumFlag(enum_id, flag): @return: 1-ok,0-failed """ - return idaapi.set_enum_flag(enum_id, flag) + return ida_enum.set_enum_flag(enum_id, flag) def SetEnumBf(enum_id, flag): @@ -6290,7 +6331,7 @@ def SetEnumBf(enum_id, flag): @return: 1-ok,0-failed """ - return idaapi.set_enum_bf(enum_id, flag) + return ida_enum.set_enum_bf(enum_id, flag) def SetEnumWidth(enum_id, width): @@ -6304,7 +6345,7 @@ def SetEnumWidth(enum_id, width): @return: 1-ok, 0-failed """ - return idaapi.set_enum_width(enum_id, width) + return ida_enum.set_enum_width(enum_id, width) def IsBitfield(enum_id): @@ -6315,7 +6356,7 @@ def IsBitfield(enum_id): @return: 1-yes, 0-no, ordinary enum """ - return idaapi.is_bf(enum_id) + return ida_enum.is_bf(enum_id) def AddConstEx(enum_id, name, value, bmask): @@ -6333,14 +6374,14 @@ def AddConstEx(enum_id, name, value, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.add_enum_member(enum_id, name, value, bmask) + return ida_enum.add_enum_member(enum_id, name, value, bmask) -ENUM_MEMBER_ERROR_NAME = idaapi.ENUM_MEMBER_ERROR_NAME # already have member with this name (bad name) -ENUM_MEMBER_ERROR_VALUE = idaapi.ENUM_MEMBER_ERROR_VALUE # already have member with this value -ENUM_MEMBER_ERROR_ENUM = idaapi.ENUM_MEMBER_ERROR_ENUM # bad enum id -ENUM_MEMBER_ERROR_MASK = idaapi.ENUM_MEMBER_ERROR_MASK # bad bmask -ENUM_MEMBER_ERROR_ILLV = idaapi.ENUM_MEMBER_ERROR_ILLV # bad bmask and value combination (~bmask & value != 0) +ENUM_MEMBER_ERROR_NAME = ida_enum.ENUM_MEMBER_ERROR_NAME # already have member with this name (bad name) +ENUM_MEMBER_ERROR_VALUE = ida_enum.ENUM_MEMBER_ERROR_VALUE # already have member with this value +ENUM_MEMBER_ERROR_ENUM = ida_enum.ENUM_MEMBER_ERROR_ENUM # bad enum id +ENUM_MEMBER_ERROR_MASK = ida_enum.ENUM_MEMBER_ERROR_MASK # bad bmask +ENUM_MEMBER_ERROR_ILLV = ida_enum.ENUM_MEMBER_ERROR_ILLV # bad bmask and value combination (~bmask & value != 0) def DelConstEx(enum_id, value, serial, bmask): @@ -6358,7 +6399,7 @@ def DelConstEx(enum_id, value, serial, bmask): """ if bmask < 0: bmask &= BADADDR - return idaapi.del_enum_member(enum_id, value, serial, bmask) + return ida_enum.del_enum_member(enum_id, value, serial, bmask) def SetConstName(const_id, name): @@ -6370,7 +6411,7 @@ def SetConstName(const_id, name): @return: 1-ok, 0-failed """ - return idaapi.set_enum_member_name(const_id, name) + return ida_enum.set_enum_member_name(const_id, name) def SetConstCmt(const_id, cmt, repeatable): @@ -6385,7 +6426,7 @@ def SetConstCmt(const_id, cmt, repeatable): @return: 1-ok, 0-failed """ - return idaapi.set_enum_member_cmt(const_id, cmt, repeatable) + return ida_enum.set_enum_member_cmt(const_id, cmt, repeatable) #---------------------------------------------------------------------------- # A R R A Y S I N I D C @@ -6398,17 +6439,17 @@ def __l2m1(v): 'signed long' version of -1, then return -1. Otherwise, return 'v'. """ - if v == idaapi.BADNODE: + if v == ida_netnode.BADNODE: return -1 else: return v -AR_LONG = idaapi.atag +AR_LONG = ida_netnode.atag """Array of longs""" -AR_STR = idaapi.stag +AR_STR = ida_netnode.stag """Array of strings""" @@ -6471,7 +6512,7 @@ def __GetArrayById(array_id): @param array_id: A positive, valid array ID. """ try: - node = idaapi.netnode(array_id) + node = ida_netnode.netnode(array_id) nodename = node.name() if nodename is None or not nodename.startswith(_IDC_ARRAY_PREFIX): return __dummy_netnode.instance @@ -6489,7 +6530,7 @@ def CreateArray(name): @return: -1 in case of failure, a valid array_id otherwise. """ - node = idaapi.netnode() + node = ida_netnode.netnode() res = node.create(_IDC_ARRAY_PREFIX + name) if res == False: return -1 @@ -6506,7 +6547,7 @@ def GetArrayId(name): @return: -1 in case of failure (i.e., no array with that name exists), a valid array_id otherwise. """ - return __l2m1(idaapi.netnode(_IDC_ARRAY_PREFIX + name, 0, False).index()) + return __l2m1(ida_netnode.netnode(_IDC_ARRAY_PREFIX + name, 0, False).index()) def RenameArray(array_id, newname): @@ -6820,7 +6861,7 @@ def AddSourceFile(ea1, ea2, filename): Each source file is represented by a range of addresses. A source file may contains several address ranges. """ - return idaapi.add_sourcefile(ea1, ea2, filename) + return ida_lines.add_sourcefile(ea1, ea2, filename) def GetSourceFile(ea): @@ -6832,7 +6873,7 @@ def GetSourceFile(ea): @return: NULL - source file information is not found otherwise returns pointer to file name """ - return idaapi.get_sourcefile(ea) + return ida_lines.get_sourcefile(ea) def DelSourceFile(ea): @@ -6844,7 +6885,7 @@ def DelSourceFile(ea): @return: NULL - source file information is not found otherwise returns pointer to file name """ - return idaapi.del_sourcefile(ea) + return ida_lines.del_sourcefile(ea) def SetLineNumber(ea, lnnum): @@ -6856,7 +6897,7 @@ def SetLineNumber(ea, lnnum): @return: None """ - idaapi.set_source_linnum(ea, lnnum) + ida_nalt.set_source_linnum(ea, lnnum) def GetLineNumber(ea): @@ -6867,7 +6908,7 @@ def GetLineNumber(ea): @return: number of line in the source file or -1 """ - return idaapi.get_source_linnum(ea) + return ida_nalt.get_source_linnum(ea) def DelLineNumber(ea): @@ -6878,7 +6919,7 @@ def DelLineNumber(ea): @return: None """ - idaapi.del_source_linnum(ea) + ida_nalt.del_source_linnum(ea) #---------------------------------------------------------------------------- @@ -6892,8 +6933,7 @@ def LoadTil(name): @param name: name of type library. @return: 1-ok, 0-failed. """ - til = idaapi.add_til2(name, idaapi.ADDTIL_DEFAULT) - + til = ida_typeinf.add_til2(name, ida_typeinf.ADDTIL_DEFAULT) if til: return 1 else: @@ -6912,7 +6952,7 @@ def Til2Idb(idx, type_name): @return: BADNODE-failed, otherwise the type id (structure id or enum id) """ - return idaapi.import_type(idaapi.cvar.idati, idx, type_name) + return ida_typeinf.import_type(ida_typeinf.cvar.idati, idx, type_name) def GetType(ea): @@ -6923,7 +6963,7 @@ def GetType(ea): @return: type string or None if failed """ - return idaapi.idc_get_type(ea) + return ida_typeinf.idc_get_type(ea) def SizeOf(typestr): """ @@ -6931,7 +6971,7 @@ def SizeOf(typestr): Use name, tp, fld = idc.ParseType() ; SizeOf(tp) to retrieve the size @return: -1 if typestring is not valid otherwise the size of the type """ - return idaapi.calc_type_size(idaapi.cvar.idati, typestr) + return ida_typeinf.calc_type_size(ida_typeinf.cvar.idati, typestr) def GetTinfo(ea): """ @@ -6940,7 +6980,7 @@ def GetTinfo(ea): @param ea: the address of the object @return: None on failure, or (type, fields) tuple. """ - return idaapi.idc_get_type_raw(ea) + return ida_typeinf.idc_get_type_raw(ea) def GetLocalTinfo(ordinal): """ @@ -6949,7 +6989,7 @@ def GetLocalTinfo(ordinal): @param ordinal: slot number (1...NumberOfLocalTypes) @return: None on failure, or (type, fields, name) tuple. """ - return idaapi.idc_get_local_type_raw(ordinal) + return ida_typeinf.idc_get_local_type_raw(ordinal) def GuessType(ea): """ @@ -6959,7 +6999,7 @@ def GuessType(ea): @return: type string or None if failed """ - return idaapi.idc_guess_type(ea) + return ida_typeinf.idc_guess_type(ea) TINFO_GUESSED = 0x0000 # this is a guessed type TINFO_DEFINITE = 0x0001 # this is a definite type @@ -6971,7 +7011,7 @@ def ApplyType(ea, py_type, flags = TINFO_DEFINITE): """ Apply the specified type to the address - @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param ti: Type info. 'ida_typeinf.cvar.idati' can be passed. @param py_type: typeinfo tuple (type, fields) as GetTinfo() returns or tuple (name, type, fields) as ParseType() returns or None @@ -6991,7 +7031,7 @@ def ApplyType(ea, py_type, flags = TINFO_DEFINITE): pt = py_type[1:] # skip name component else: pt = py_type - return idaapi.apply_type(idaapi.cvar.idati, pt[0], pt[1], ea, flags) + return ida_typeinf.apply_type(ida_typeinf.cvar.idati, pt[0], pt[1], ea, flags) def SetType(ea, newtype): """ @@ -7025,7 +7065,7 @@ def ParseType(inputtype, flags): """ if len(inputtype) != 0 and inputtype[-1] != ';': inputtype = inputtype + ';' - return idaapi.idc_parse_decl(idaapi.cvar.idati, inputtype, flags) + return ida_typeinf.idc_parse_decl(ida_typeinf.cvar.idati, inputtype, flags) def ParseTypes(inputtype, flags = 0): """ @@ -7036,7 +7076,7 @@ def ParseTypes(inputtype, flags = 0): @return: number of parsing errors (0 no errors) """ - return idaapi.idc_parse_types(inputtype, flags) + return ida_typeinf.idc_parse_types(inputtype, flags) PT_FILE = 0x0001 # input if a file name (otherwise contains type declarations) @@ -7061,10 +7101,10 @@ def PrintLocalTypes(ordinals, flags): @return: string containing the type definitions """ - class def_sink(idaapi.text_sink_t): + class def_sink(ida_typeinf.text_sink_t): def __init__(self): - idaapi.text_sink_t.__init__(self) + ida_typeinf.text_sink_t.__init__(self) self.text = "" def _print(self, defstr): @@ -7073,7 +7113,7 @@ def PrintLocalTypes(ordinals, flags): sink = def_sink() py_ordinals = map(lambda l : int(l), ordinals.split(",")) - idaapi.print_decls(sink, idaapi.cvar.idati, py_ordinals, flags) + ida_typeinf.print_decls(sink, ida_typeinf.cvar.idati, py_ordinals, flags) return sink.text @@ -7090,7 +7130,7 @@ def GetMaxLocalType(): @return: value >= 1. 1 means that there are no local types. """ - return idaapi.get_ordinal_qty(idaapi.cvar.idati) + return ida_typeinf.get_ordinal_qty(ida_typeinf.cvar.idati) def SetLocalType(ordinal, input, flags): @@ -7105,7 +7145,7 @@ def SetLocalType(ordinal, input, flags): @return: slot number or 0 if error """ - return idaapi.idc_set_local_type(ordinal, input, flags) + return ida_typeinf.idc_set_local_type(ordinal, input, flags) def GetLocalType(ordinal, flags): @@ -7117,7 +7157,7 @@ def GetLocalType(ordinal, flags): (type, fields) = GetLocalTinfo(ordinal) if type: name = GetLocalTypeName(ordinal) - return idaapi.idc_print_type(type, fields, name, flags) + return ida_typeinf.idc_print_type(type, fields, name, flags) return "" PRTYPE_1LINE = 0x0000 # print to one line @@ -7134,7 +7174,7 @@ def GetLocalTypeName(ordinal): returns: local type name or None """ - return idaapi.idc_get_local_type_name(ordinal) + return ida_typeinf.idc_get_local_type_name(ordinal) # ---------------------------------------------------------------------------- @@ -7155,7 +7195,7 @@ def HideArea(start, end, description, header, footer, color): @returns: !=0 - ok """ - return idaapi.add_hidden_area(start, end, description, header, footer, color) + return ida_bytes.add_hidden_area(start, end, description, header, footer, color) def SetHiddenArea(ea, visible): @@ -7167,13 +7207,13 @@ def SetHiddenArea(ea, visible): @return: != 0 - ok """ - ha = idaapi.get_hidden_area(ea) + ha = ida_bytes.get_hidden_area(ea) if not ha: return 0 else: ha.visible = visible - return idaapi.update_hidden_area(ha) + return ida_bytes.update_hidden_area(ha) def DelHiddenArea(ea): @@ -7183,7 +7223,7 @@ def DelHiddenArea(ea): @param ea: any address belonging to the hidden area @returns: != 0 - ok """ - return idaapi.del_hidden_area(ea) + return ida_bytes.del_hidden_area(ea) #-------------------------------------------------------------------------- @@ -7199,7 +7239,7 @@ def LoadDebugger(dbgname, use_remote): @note: This function is needed only when running idc scripts from the command line. In other cases IDA loads the debugger module automatically. """ - return idaapi.load_debugger(dbgname, use_remote) + return ida_dbg.load_debugger(dbgname, use_remote) def StartDebugger(path, args, sdir): @@ -7215,7 +7255,7 @@ def StartDebugger(path, args, sdir): @note: For all args: if empty, the default value from the database will be used See the important note to the StepInto() function """ - return idaapi.start_process(path, args, sdir) + return ida_dbg.start_process(path, args, sdir) def StopDebugger(): @@ -7225,7 +7265,7 @@ def StopDebugger(): @return: success """ - return idaapi.exit_process() + return ida_dbg.exit_process() def PauseProcess(): @@ -7239,14 +7279,14 @@ def PauseProcess(): @note: To resume a suspended process use the GetDebuggerEvent function. See the important note to the StepInto() function """ - return idaapi.suspend_process() + return ida_dbg.suspend_process() def GetProcessQty(): """ Take a snapshot of running processes and return their number. """ - return idaapi.get_process_qty() + return ida_dbg.get_process_qty() def GetProcessPid(idx): @@ -7257,9 +7297,9 @@ def GetProcessPid(idx): @return: 0 if failure """ - pinfo = idaapi.process_info_t() - pid = idaapi.get_process_info(idx, pinfo) - if pid != idaapi.NO_PROCESS: + pinfo = ida_idd.process_info_t() + pid = ida_dbg.get_process_info(idx, pinfo) + if pid != ida_idd.NO_PROCESS: return pinfo.pid else: return 0 @@ -7273,9 +7313,9 @@ def GetProcessName(idx): @return: None if failure """ - pinfo = idaapi.process_info_t() - pid = idaapi.get_process_info(idx, pinfo) - return None if pid == idaapi.NO_PROCESS else pinfo.name + pinfo = ida_idd.process_info_t() + pid = ida_dbg.get_process_info(idx, pinfo) + return None if pid == ida_idd.NO_PROCESS else pinfo.name def AttachProcess(pid, event_id): @@ -7294,7 +7334,7 @@ def AttachProcess(pid, event_id): - 1: the debugger properly attached to the process @note: See the important note to the StepInto() function """ - return idaapi.attach_process(pid, event_id) + return ida_dbg.attach_process(pid, event_id) def DetachProcess(): @@ -7303,7 +7343,7 @@ def DetachProcess(): @return: success """ - return idaapi.detach_process() + return ida_dbg.detach_process() def GetThreadQty(): @@ -7312,7 +7352,7 @@ def GetThreadQty(): @return: number of threads """ - return idaapi.get_thread_qty() + return ida_dbg.get_thread_qty() def GetThreadId(idx): @@ -7323,7 +7363,7 @@ def GetThreadId(idx): @return: -1 if failure """ - return idaapi.getn_thread(idx) + return ida_dbg.getn_thread(idx) def GetCurrentThreadId(): @@ -7332,7 +7372,7 @@ def GetCurrentThreadId(): @return: -1 if failure """ - return idaapi.get_current_thread() + return ida_dbg.get_current_thread() def SelectThread(tid): @@ -7345,7 +7385,7 @@ def SelectThread(tid): @note: The process must be suspended to select a new thread. """ - return idaapi.select_thread(tid) + return ida_dbg.select_thread(tid) def SuspendThread(tid): @@ -7359,7 +7399,7 @@ def SuspendThread(tid): @note: Suspending a thread may deadlock the whole application if the suspended was owning some synchronization objects. """ - return idaapi.suspend_thread(tid) + return ida_dbg.suspend_thread(tid) def ResumeThread(tid): @@ -7370,18 +7410,18 @@ def ResumeThread(tid): @return: -1:network error, 0-failed, 1-ok """ - return idaapi.resume_thread(tid) + return ida_dbg.resume_thread(tid) def _get_modules(): """ INTERNAL: Enumerate process modules """ - module = idaapi.module_info_t() - result = idaapi.get_first_module(module) + module = ida_idd.module_info_t() + result = ida_dbg.get_first_module(module) while result: yield module - result = idaapi.get_next_module(module) + result = ida_dbg.get_next_module(module) def GetFirstModule(): @@ -7458,7 +7498,7 @@ def StepInto(): This remark applies to all execution control functions. The event codes depend on the issued command. """ - return idaapi.step_into() + return ida_dbg.step_into() def StepOver(): @@ -7470,7 +7510,7 @@ def StepOver(): @return: success """ - return idaapi.step_over() + return ida_dbg.step_over() def RunTo(ea): @@ -7481,7 +7521,7 @@ def RunTo(ea): @return: success """ - return idaapi.run_to(ea) + return ida_dbg.run_to(ea) def StepUntilRet(): @@ -7493,7 +7533,7 @@ def StepUntilRet(): @return: success """ - return idaapi.step_until_ret() + return ida_dbg.step_until_ret() def GetDebuggerEvent(wfne, timeout): @@ -7507,7 +7547,7 @@ def GetDebuggerEvent(wfne, timeout): @return: debugger event codes, see below """ - return idaapi.wait_for_next_event(wfne, timeout) + return ida_dbg.wait_for_next_event(wfne, timeout) def ResumeProcess(): @@ -7518,7 +7558,7 @@ def SendDbgCommand(cmd): 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");' % idaapi.str2user(cmd)) + s = Eval('SendDbgCommand("%s");' % ida_kernwin.str2user(cmd)) if s.startswith("IDC_FAILURE"): raise Exception, "Debugger command is available only when the debugger is active!" return s @@ -7567,7 +7607,7 @@ def RefreshDebuggerMemory(): information, memory contents, or names of a non-suspended process. This is an expensive call. """ - return idaapi.refresh_debugger_memory() + return ida_dbg.refresh_debugger_memory() def TakeMemorySnapshot(only_loader_segs): @@ -7577,7 +7617,7 @@ def TakeMemorySnapshot(only_loader_segs): @param only_loader_segs: 0-copy all segments to idb 1-copy only SFL_LOADER segments """ - return idaapi.take_memory_snapshot(only_loader_segs) + return ida_segment.take_memory_snapshot(only_loader_segs) def GetProcessState(): @@ -7586,7 +7626,7 @@ def GetProcessState(): @return: one of the DBG_... constants (see below) """ - return idaapi.get_process_state() + return ida_dbg.get_process_state() DSTATE_SUSP = -1 # process is suspended DSTATE_NOTASK = 0 # no process is currently debugged @@ -7609,7 +7649,7 @@ def GetEventId(): @return: event ID """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" return ev.eid @@ -7620,7 +7660,7 @@ def GetEventPid(): @return: process ID """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" return ev.pid @@ -7631,7 +7671,7 @@ def GetEventTid(): @return: type ID """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" return ev.tid @@ -7642,7 +7682,7 @@ def GetEventEa(): @return: ea """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" return ev.ea @@ -7653,7 +7693,7 @@ def IsEventHandled(): @return: boolean """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" return ev.handled @@ -7666,9 +7706,9 @@ def GetEventModuleName(): @return: module name """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_module_name(ev) + return ida_idd.get_event_module_name(ev) def GetEventModuleBase(): @@ -7677,9 +7717,9 @@ def GetEventModuleBase(): @return: module base """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_module_base(ev) + return ida_idd.get_event_module_base(ev) def GetEventModuleSize(): @@ -7688,9 +7728,9 @@ def GetEventModuleSize(): @return: module size """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_module_size(ev) + return ida_idd.get_event_module_size(ev) def GetEventExitCode(): @@ -7699,7 +7739,7 @@ def GetEventExitCode(): @return: exit code for PROCESS_EXIT, THREAD_EXIT events """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" return ev.exit_code @@ -7711,9 +7751,9 @@ def GetEventInfo(): @return: event info: for LIBRARY_UNLOAD (unloaded library name) for INFORMATION (message to display) """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_info(ev) + return ida_idd.get_event_info(ev) def GetEventBptHardwareEa(): @@ -7722,9 +7762,9 @@ def GetEventBptHardwareEa(): @return: hardware address """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_bpt_hea(ev) + return ida_idd.get_event_bpt_hea(ev) def GetEventExceptionCode(): @@ -7733,9 +7773,9 @@ def GetEventExceptionCode(): @return: exception code """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_exc_code(ev) + return ida_idd.get_event_exc_code(ev) def GetEventExceptionEa(): @@ -7744,9 +7784,9 @@ def GetEventExceptionEa(): @return: adress of exception """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_exc_ea(ev) + return ida_idd.get_event_exc_ea(ev) def CanExceptionContinue(): @@ -7755,9 +7795,9 @@ def CanExceptionContinue(): @return: boolean """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.can_exc_continue(ev) + return ida_idd.can_exc_continue(ev) def GetEventExceptionInfo(): @@ -7766,9 +7806,9 @@ def GetEventExceptionInfo(): @return: info string """ - ev = idaapi.get_debug_event() + ev = ida_dbg.get_debug_event() assert ev, "Could not retrieve debug event" - return idaapi.get_event_exc_info(ev) + return ida_idd.get_event_exc_info(ev) def SetDebuggerOptions(opt): @@ -7779,7 +7819,7 @@ def SetDebuggerOptions(opt): @return: old options """ - return idaapi.set_debugger_options(opt) + return ida_dbg.set_debugger_options(opt) DOPT_SEGM_MSGS = 0x00000001 # print messages on debugger segments modifications @@ -7807,14 +7847,14 @@ def GetDebuggerEventCondition(): """ Return the debugger event condition """ - return idaapi.get_debugger_event_cond() + return ida_dbg.get_debugger_event_cond() def SetDebuggerEventCondition(cond): """ Set the debugger event condition """ - return idaapi.set_debugger_event_cond(cond) + return ida_dbg.set_debugger_event_cond(cond) def SetRemoteDebugger(hostname, password, portnum): @@ -7827,48 +7867,9 @@ def SetRemoteDebugger(hostname, password, portnum): @return: nothing """ - return idaapi.set_remote_debugger(hostname, password, portnum) + return ida_dbg.set_remote_debugger(hostname, password, portnum) -def GetExceptionQty(): - """ - Get number of defined exception codes - """ - return idaapi.get_exception_qty() - - -def GetExceptionCode(idx): - """ - Get exception code - - @param idx: number of exception in the vector (0..GetExceptionQty()-1) - - @return: exception code (0 - error) - """ - return idaapi.get_exception_code(idx) - - -def GetExceptionName(code): - """ - Get exception information - - @param code: exception code - - @return: "" on error - """ - return idaapi.get_exception_name(code) - - -def GetExceptionFlags(code): - """ - Get exception information - - @param code: exception code - - @return: -1 on error - """ - return idaapi.get_exception_flags(code) - def DefineException(code, name, desc, flags): """ Add exception handling information @@ -7880,31 +7881,12 @@ def DefineException(code, name, desc, flags): @return: failure description or "" """ - return idaapi.define_exception(code, name, desc, flags) + return ida_dbg.define_exception(code, name, desc, flags) EXC_BREAK = 0x0001 # break on the exception EXC_HANDLE = 0x0002 # should be handled by the debugger? -def SetExceptionFlags(code, flags): - """ - Set exception flags - - @param code: exception code - @param flags: exception flags (combination of EXC_...) - """ - return idaapi.set_exception_flags(code, flags) - - -def ForgetException(code): - """ - Delete exception handling information - - @param code: exception code - """ - return idaapi.forget_exception(code) - - def GetRegValue(name): """ Get register value @@ -7918,8 +7900,8 @@ def GetRegValue(name): @return: register value (integer or floating point) """ - rv = idaapi.regval_t() - res = idaapi.get_reg_val(name, rv) + rv = ida_idd.regval_t() + res = ida_dbg.get_reg_val(name, rv) assert res, "get_reg_val() failed, bogus register name ('%s') perhaps?" % name return rv.ival @@ -7935,7 +7917,7 @@ def SetRegValue(value, name): It is not necessary to use this function to set register values. A register name in the left side of an assignment will do too. """ - rv = idaapi.regval_t() + rv = ida_idd.regval_t() if type(value) == types.StringType: value = int(value, 16) elif type(value) != types.IntType and type(value) != types.LongType: @@ -7947,7 +7929,7 @@ def SetRegValue(value, name): value &= 0xFFFFFFFF rv.ival = value - return idaapi.set_reg_val(name, rv) + return ida_dbg.set_reg_val(name, rv) def GetBptQty(): @@ -7956,7 +7938,7 @@ def GetBptQty(): @return: number of breakpoints """ - return idaapi.get_bpt_qty() + return ida_dbg.get_bpt_qty() def GetBptEA(n): @@ -7967,9 +7949,9 @@ def GetBptEA(n): @return: addresss of the breakpoint or BADADDR """ - bpt = idaapi.bpt_t() + bpt = ida_dbg.bpt_t() - if idaapi.getn_bpt(n, bpt): + if ida_dbg.getn_bpt(n, bpt): return bpt.ea else: return BADADDR @@ -7984,9 +7966,9 @@ def GetBptAttr(ea, bptattr): @return: the desired attribute value or -1 """ - bpt = idaapi.bpt_t() + bpt = ida_dbg.bpt_t() - if not idaapi.get_bpt(ea, bpt): + if not ida_dbg.get_bpt(ea, bpt): return -1 else: if bptattr == BPTATTR_EA: @@ -8056,9 +8038,9 @@ def SetBptAttr(address, bptattr, value): @return: success """ - bpt = idaapi.bpt_t() + bpt = ida_dbg.bpt_t() - if not idaapi.get_bpt(address, bpt): + if not ida_dbg.get_bpt(address, bpt): return False else: if bptattr not in [ BPTATTR_SIZE, BPTATTR_TYPE, BPTATTR_FLAGS, BPTATTR_COUNT ]: @@ -8072,7 +8054,7 @@ def SetBptAttr(address, bptattr, value): if bptattr == BPTATTR_FLAGS: bpt.flags = value - return idaapi.update_bpt(bpt) + return ida_dbg.update_bpt(bpt) def SetBptCndEx(ea, cnd, is_lowcnd): """ @@ -8084,9 +8066,9 @@ def SetBptCndEx(ea, cnd, is_lowcnd): @return: success """ - bpt = idaapi.bpt_t() + bpt = ida_dbg.bpt_t() - if not idaapi.get_bpt(ea, bpt): + if not ida_dbg.get_bpt(ea, bpt): return False bpt.condition = cnd @@ -8095,7 +8077,7 @@ def SetBptCndEx(ea, cnd, is_lowcnd): else: bpt.flags &= ~BPT_LOWCND - return idaapi.update_bpt(bpt) + return ida_dbg.update_bpt(bpt) def SetBptCnd(ea, cnd): @@ -8122,7 +8104,7 @@ def AddBptEx(ea, size, bpttype): @note: Only one breakpoint can exist at a given address. """ - return idaapi.add_bpt(ea, size, bpttype) + return ida_dbg.add_bpt(ea, size, bpttype) def AddBpt(ea): @@ -8137,7 +8119,7 @@ def DelBpt(ea): @return: success """ - return idaapi.del_bpt(ea) + return ida_dbg.del_bpt(ea) def EnableBpt(ea, enable): @@ -8150,7 +8132,7 @@ def EnableBpt(ea, enable): @note: Disabled breakpoints are not written to the process memory """ - return idaapi.enable_bpt(ea, enable) + return ida_dbg.enable_bpt(ea, enable) def CheckBpt(ea): @@ -8161,7 +8143,7 @@ def CheckBpt(ea): @return: one of BPTCK_... constants """ - return idaapi.check_bpt(ea) + return ida_dbg.check_bpt(ea) BPTCK_NONE = -1 # breakpoint does not exist BPTCK_NO = 0 # breakpoint is disabled @@ -8182,13 +8164,13 @@ def EnableTracing(trace_level, enable): "trace_level must be one of TRACE_* constants" if trace_level == TRACE_STEP: - return idaapi.enable_step_trace(enable) + return ida_dbg.enable_step_trace(enable) if trace_level == TRACE_INSN: - return idaapi.enable_insn_trace(enable) + return ida_dbg.enable_insn_trace(enable) if trace_level == TRACE_FUNC: - return idaapi.enable_func_trace(enable) + return ida_dbg.enable_func_trace(enable) return False @@ -8203,7 +8185,7 @@ def GetStepTraceOptions(): @return: a combination of ST_... constants """ - return idaapi.get_step_trace_options() + return ida_dbg.get_step_trace_options() def SetStepTraceOptions(options): @@ -8211,7 +8193,7 @@ def SetStepTraceOptions(options): Set step current tracing options. @param options: combination of ST_... constants """ - return idaapi.set_step_trace_options(options) + return ida_dbg.set_step_trace_options(options) ST_OVER_DEBUG_SEG = 0x01 # step tracing will be disabled when IP is in a debugger segment @@ -8224,7 +8206,7 @@ def LoadTraceFile(filename): Load a previously recorded binary trace file @param filename: trace file """ - return idaapi.load_trace_file(filename) + return ida_dbg.load_trace_file(filename) def SaveTraceFile(filename, description): """ @@ -8232,34 +8214,34 @@ def SaveTraceFile(filename, description): @param filename: trace file @param description: trace description """ - return idaapi.save_trace_file(filename, description) + return ida_dbg.save_trace_file(filename, description) def CheckTraceFile(filename): """ Check the given binary trace file @param filename: trace file """ - return idaapi.is_valid_trace_file(filename) + return ida_dbg.is_valid_trace_file(filename) def DiffTraceFile(filename): """ Diff current trace buffer against given trace @param filename: trace file """ - return idaapi.diff_trace_file(filename) + return ida_dbg.diff_trace_file(filename) def ClearTraceFile(filename): """ Clear the current trace buffer """ - return idaapi.clear_trace() + return ida_dbg.clear_trace() def GetTraceDesc(filename): """ Get the trace description of the given binary trace file @param filename: trace file """ - return idaapi.get_trace_file_desc(filename) + return ida_dbg.get_trace_file_desc(filename) def SetTraceDesc(filename, description): """ @@ -8267,20 +8249,20 @@ def SetTraceDesc(filename, description): @param filename: trace file @description: trace description """ - return idaapi.set_trace_file_desc(filename, description) + return ida_dbg.set_trace_file_desc(filename, description) def GetMaxTev(): """ Return the total number of recorded events """ - return idaapi.get_tev_qty() + return ida_dbg.get_tev_qty() def GetTevEa(tev): """ Return the address of the specified event @param tev: event number """ - return idaapi.get_tev_ea(tev) + return ida_dbg.get_tev_ea(tev) TEV_NONE = 0 # no event TEV_INSN = 1 # an instruction trace @@ -8295,14 +8277,14 @@ def GetTevType(tev): Return the type of the specified event (TEV_... constants) @param tev: event number """ - return idaapi.get_tev_type(tev) + return ida_dbg.get_tev_type(tev) def GetTevTid(tev): """ Return the thread id of the specified event @param tev: event number """ - return idaapi.get_tev_tid(tev) + return ida_dbg.get_tev_tid(tev) def GetTevRegVal(tev, reg): """ @@ -8310,7 +8292,7 @@ def GetTevRegVal(tev, reg): @param tev: event number @param reg: register name (like EAX, RBX, ...) """ - return idaapi.get_tev_reg_val(tev, reg) + return ida_dbg.get_tev_reg_val(tev, reg) def GetTevRegMemQty(tev): """ @@ -8320,7 +8302,7 @@ def GetTevRegMemQty(tev): @param tev: event number """ - return idaapi.get_tev_reg_mem_qty(tev) + return ida_dbg.get_tev_reg_mem_qty(tev) def GetTevRegMem(tev, idx): """ @@ -8331,7 +8313,7 @@ def GetTevRegMem(tev, idx): @param tev: event number @param idx: memory address index """ - return idaapi.get_tev_reg_mem(tev, idx) + return ida_dbg.get_tev_reg_mem(tev, idx) def GetTevRegMemEa(tev, idx): """ @@ -8342,28 +8324,28 @@ def GetTevRegMemEa(tev, idx): @param tev: event number @param idx: memory address index """ - return idaapi.get_tev_reg_mem_ea(tev, idx) + return ida_dbg.get_tev_reg_mem_ea(tev, idx) def GetTevCallee(tev): """ Return the address of the callee for the specified event @param tev: event number """ - return idaapi.get_call_tev_callee(tev) + return ida_dbg.get_call_tev_callee(tev) def GetTevReturn(tev): """ Return the return address for the specified event @param tev: event number """ - return idaapi.get_ret_tev_return(tev) + return ida_dbg.get_ret_tev_return(tev) def GetBptTevEa(tev): """ Return the address of the specified TEV_BPT event @param tev: event number """ - return idaapi.get_bpt_tev_ea(tev) + return ida_dbg.get_bpt_tev_ea(tev) #-------------------------------------------------------------------------- @@ -8383,17 +8365,17 @@ def GetColor(ea, what): raise ValueError, "'what' must be one of CIC_ITEM, CIC_FUNC and CIC_SEGM" if what == CIC_ITEM: - return idaapi.get_item_color(ea) + return ida_nalt.get_item_color(ea) if what == CIC_FUNC: - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if func: return func.color else: return DEFCOLOR if what == CIC_SEGM: - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if seg: return seg.color else: @@ -8421,18 +8403,18 @@ def SetColor(ea, what, color): raise ValueError, "'what' must be one of CIC_ITEM, CIC_FUNC and CIC_SEGM" if what == CIC_ITEM: - return idaapi.set_item_color(ea, color) + return ida_nalt.set_item_color(ea, color) if what == CIC_FUNC: - func = idaapi.get_func(ea) + func = ida_funcs.get_func(ea) if func: func.color = color - return bool(idaapi.update_func(func)) + return bool(ida_funcs.update_func(func)) else: return False if what == CIC_SEGM: - seg = idaapi.getseg(ea) + seg = ida_segment.getseg(ea) if seg: seg.color = color return bool(seg.update()) @@ -8546,8 +8528,8 @@ def WriteExe(filepath): return GenerateFile(OFILE_EXE, filepath, 0, BADADDR, 0) -UTP_STRUCT = idaapi.UTP_STRUCT -UTP_ENUM = idaapi.UTP_ENUM +UTP_STRUCT = ida_typeinf.UTP_STRUCT +UTP_ENUM = ida_typeinf.UTP_ENUM def BeginTypeUpdating(utp): @@ -8559,7 +8541,7 @@ def BeginTypeUpdating(utp): @param utp: one of UTP_xxxx consts @return: None """ - return idaapi.begin_type_updating(utp) + return ida_typeinf.begin_type_updating(utp) def EndTypeUpdating(utp): @@ -8567,13 +8549,13 @@ def EndTypeUpdating(utp): End type updating. Refreshes the type system at the end of type modification operations - @param utp: one of idaapi.UTP_xxxx consts + @param utp: one of ida_typeinf.UTP_xxxx consts @return: None """ - return idaapi.end_type_updating(utp) + return ida_typeinf.end_type_updating(utp) -def AddConst(enum_id, name,value): return AddConstEx(enum_id, name, value, idaapi.BADADDR) +def AddConst(enum_id, name,value): return AddConstEx(enum_id, name, value, ida_idaapi.BADADDR) def AddStruc(index, name): return AddStrucEx(index,name, 0) def AddUnion(index, name): return AddStrucEx(index,name, 1) def OpStroff(ea, n, strid): return OpStroffEx(ea,n,strid, 0) diff --git a/python/init.py b/python/init.py old mode 100644 new mode 100755 index 06b74b4..3649343 --- a/python/init.py +++ b/python/init.py @@ -15,10 +15,21 @@ import os import sys import time import warnings -import _idaapi + +# Prepare sys.path so loading of the shared objects works +sys.path.append(os.path.join(sys.executable, IDAPYTHON_DYNLOAD_BASE, "python", "lib", "python2.7", "lib-dynload", IDAPYTHON_DYNLOAD_RELPATH)) +try: + import ida_idaapi + import ida_kernwin + import ida_diskio +except ImportError as e: + print "Import failed: %s. Current sys.path:" % str(e) + for p in sys.path: + print "\t%s" % p + raise # __EA64__ is set if IDA is running in 64-bit mode -__EA64__ = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL +__EA64__ = ida_idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL # ----------------------------------------------------------------------- # Take over the standard text outputs @@ -30,7 +41,7 @@ class IDAPythonStdOut: def write(self, text): # NB: in case 'text' is Unicode, msg() will decode it # and call umsg() to print it - _idaapi.msg(text) + ida_kernwin.msg(text) def flush(self): pass @@ -49,8 +60,8 @@ def runscript(script): @return: Error string or None on success """ - import idaapi - return idaapi.IDAPython_ExecScript(script, globals()) + import ida_idaapi + return ida_idaapi.IDAPython_ExecScript(script, globals()) # ----------------------------------------------------------------------- def print_banner(): @@ -77,14 +88,14 @@ sys.stdout = sys.stderr = IDAPythonStdOut() import pydoc class IDAPythonHelpPrompter: def readline(self): - return idaapi.askstr(0, '', 'Help topic?') + return ida_kernwin.askstr(0, '', 'Help topic?') help = pydoc.Helper(input = IDAPythonHelpPrompter(), output = sys.stdout) # Assign a default sys.argv sys.argv = [""] # Have to make sure Python finds our modules -sys.path.append(_idaapi.idadir("python")) +sys.path.append(ida_diskio.idadir("python")) # Remove current directory from the top of the patch search if '' in sys.path: # On non Windows, the empty path is added @@ -97,15 +108,16 @@ if os.getcwd() in sys.path: if not IDAPYTHON_REMOVE_CWD_SYS_PATH: sys.path.append(os.getcwd()) -# Import all the required modules -from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall, Form -from idc import * -from idautils import * -import idaapi +if IDAPYTHON_COMPAT_AUTOIMPORT_MODULES: + # Import all the required modules + from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall, Form + from idc import * + from idautils import * + import idaapi # Load the users personal init file -userrc = os.path.join(get_user_idadir(), "idapythonrc.py") +userrc = os.path.join(ida_diskio.get_user_idadir(), "idapythonrc.py") if os.path.exists(userrc): - idaapi.IDAPython_ExecScript(userrc, globals()) + ida_idaapi.IDAPython_ExecScript(userrc, globals()) # All done, ready to rock. diff --git a/pywraps.cpp b/pywraps.cpp new file mode 100644 index 0000000..7cafd3c --- /dev/null +++ b/pywraps.cpp @@ -0,0 +1,2515 @@ + +#include +#include + +#include + +#include "pywraps.hpp" + +//lint -esym(843,pywraps_initialized) could be declared const +//lint -esym(843,g_nw) could be declared const +//lint -esym(844,g_nw) could be declared const +//lint -esym(843,g_use_local_python) could be declared const + +//------------------------------------------------------------------------ +// 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 + +static ref_t get_idaapi_attr_by_id(const int class_id); +static ref_t get_idaapi_attr(const char *attr); + + +//------------------------------------------------------------------------- +static 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 < size; i++ ) + { + // Get the item + ref_t py_item; + if ( is_seq ) + py_item = newref_t(PySequence_GetItem(o, i)); + else + py_item = borref_t(PyList_GetItem(o, i)); + + if ( py_item == NULL || cb(py_item, i, ud) < CIP_OK ) + break; + } + size = i; + } while ( false ); + return size; +} + +//------------------------------------------------------------------------- +Py_ssize_t ida_export pyvar_walk_list( + PyObject *py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), + void *ud) +{ + borref_t r(py_list); + return pyvar_walk_list(r, cb, ud); +} + +//--------------------------------------------------------------------------- +ref_t ida_export PyW_IntVecToPyList(const intvec_t &intvec) +{ + size_t c = intvec.size(); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_list(PyList_New(c)); + for ( size_t i=0; i < c; i++ ) + PyList_SetItem(py_list.o, i, PyInt_FromLong(intvec[i])); + return ref_t(py_list); +} + +//--------------------------------------------------------------------------- +static int idaapi pylist_to_intvec_cb( + const ref_t &py_item, + Py_ssize_t /*index*/, + void *ud) +{ + intvec_t &intvec = *(intvec_t *) ud; + uint64 num; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyW_GetNumber(py_item.o, &num) ) + num = 0; + } + + intvec.push_back(int(num)); + return CIP_OK; +} + +//--------------------------------------------------------------------------- +bool ida_export PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec) +{ + intvec.clear(); + return pyvar_walk_list(py_list, pylist_to_intvec_cb, &intvec) != CIP_FAILED; +} + +//------------------------------------------------------------------------- +static int idaapi pylist_to_eavec_cb( + const ref_t &py_item, + Py_ssize_t index, + void *ud) +{ + eavec_t &eavec = *(eavec_t *) ud; + ea_t ea = BADADDR; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyInt_Check(py_item.o) ) + { + ea = PyInt_AsUnsignedLongMask(py_item.o); + } + else + { + if ( PyLong_Check(py_item.o) ) + { + ea = ea_t(PyLong_AsUnsignedLongLong(py_item.o)); + } + else + { + qstring m; + m.sprnt("Item #%d cannot be converted to an ea_t", int(index)); + PyErr_SetString(PyExc_ValueError, m.c_str()); + return CIP_FAILED; + } + } + } + + eavec.push_back(ea); + return CIP_OK; +} + +//--------------------------------------------------------------------------- +bool ida_export PyW_PyListToEaVec(PyObject *py_list, eavec_t &eavec) +{ + eavec.clear(); + return pyvar_walk_list(py_list, pylist_to_eavec_cb, &eavec) != CIP_FAILED; +} + +//--------------------------------------------------------------------------- +static int idaapi pylist_to_strvec_cb( + const ref_t &py_item, + Py_ssize_t /*index*/, + void *ud) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + qstrvec_t &strvec = *(qstrvec_t *)ud; + const char *s; + if ( !PyString_Check(py_item.o) ) + s = ""; + else + s = PyString_AsString(py_item.o); + + strvec.push_back(s); + return CIP_OK; +} + +//--------------------------------------------------------------------------- +bool ida_export PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec) +{ + strvec.clear(); + return pyvar_walk_list(py_list, pylist_to_strvec_cb, &strvec) != CIP_FAILED; +} + +//------------------------------------------------------------------------- +bool ida_export PyWStringOrNone_Check(PyObject *tp) +{ + return tp == Py_None || PyString_Check(tp); +} + +//------------------------------------------------------------------------- +PyObject *ida_export 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; +} + +//------------------------------------------------------------------------- +static qvector py_compiled_form_vec; + +//------------------------------------------------------------------------- +void ida_export PyW_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); +} + +//------------------------------------------------------------------------- +void ida_export PyW_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); +} + +//------------------------------------------------------------------------- +static 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", "()")); + } +} + +//------------------------------------------------------------------------- +// Checks if the given py_var is a special PyIdc_cvt_helper object. +// It does that by examining the magic attribute and returns its numeric value. +// It returns -1 if the object is not a recognized helper object. +// Any Python object can be treated as an cvt object if this attribute is created. +static int get_pyidc_cvt_type(PyObject *py_var) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + // Check if this our special by reference object + ref_t attr(PyW_TryGetAttrString(py_var, S_PY_IDCCVT_ID_ATTR)); + if ( attr == NULL || (!PyInt_Check(attr.o) && !PyLong_Check(attr.o)) ) + return -1; + return (int)PyInt_AsLong(attr.o); +} + +//------------------------------------------------------------------------- +// Utility function to convert a python object to an IDC object +// and sets a python exception on failure. +bool ida_export pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + int sn = 0; + bool ok = pyvar_to_idcvar(py_obj, idc_obj, &sn) >= 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 ida_export 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 < size; i++ ) + { + // Get the item + ref_t py_item; + if ( is_seq ) + py_item = newref_t(PySequence_GetItem(py_var.o, i)); + else + py_item = borref_t(PyList_GetItem(py_var.o, i)); + + // Convert the item into an IDC variable + idc_value_t v; + ok = pyvar_to_idcvar(py_item, &v, gvar_sn) >= 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 < size; i++ ) + { + // Get item[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 < size; i++ ) + { + borref_t item(PyList_GetItem(py_dir.o, i)); + const char *field_name = PyString_AsString(item.o); + if ( field_name == NULL ) + continue; + + size_t len = strlen(field_name); + + // Skip private attributes + if ( (len > 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 ida_export 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 ida_export 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 < nargs; i++ ) + { + ref_t py_obj; + int cvt = idcvar_to_pyvar(args[i], &py_obj); + if ( cvt < CIP_OK ) + { + if ( errbuf != 0 && errbufsize > 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; +} + +//------------------------------------------------------------------------ +static ref_t ida_idaapi_module; +static ref_t compat_idaapi_module; +static bool pywraps_initialized = false; + +#define SWIG_RUNTIME_VERSION "4" + +//------------------------------------------------------------------------ +// check if we have a file which is known to be executed automatically +// by SWIG or Python runtime +static 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 ida_export 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 +static 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 ( ida_idaapi_module == NULL ) + { + // Take a reference to the module so we can create the needed class instances + ida_idaapi_module = PyW_TryImportModule(S_PY_IDA_IDAAPI_MODNAME); + if ( ida_idaapi_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 +static void deinit_pywraps() +{ + if ( !pywraps_initialized ) + return; + + pywraps_initialized = false; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + ida_idaapi_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 linked class instances +ref_t ida_export create_linked_class_instance( + const char *modname, + const char *clsname, + void *lnk) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_module(PyImport_ImportModule(modname)); + ref_t result; + if ( py_module != NULL ) + { + ref_t py_class = PyW_TryGetAttrString(py_module.o, clsname); + if ( py_class != NULL ) + { + newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_class.o, py_lnk.o, NULL)); + if ( !PyW_GetError() && py_obj != NULL ) + result = py_obj; + } + } + return result; +} + +//------------------------------------------------------------------------ +// 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 +static ref_t get_idaapi_attr_by_id(const int class_id) +{ + if ( class_id >= PY_CLSID_LAST || ida_idaapi_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(ida_idaapi_module.o, class_names[class_id])); +} + +//------------------------------------------------------------------------ +// Gets a class reference by name +static ref_t get_idaapi_attr(const char *attrname) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + return ida_idaapi_module == NULL + ? ref_t() + : PyW_TryGetAttrString(ida_idaapi_module.o, attrname); +} + +//------------------------------------------------------------------------ +// Returns a qstring from an object attribute +bool ida_export 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); + 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 ida_export 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 ida_export 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 ida_export 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 ida_export 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 ida_export 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 ida_export 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 ida_export 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; +} + +//------------------------------------------------------------------------- +static 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 ida_export PyW_ShowCbErr(const char *cb_name) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + + static qstring err_str; + if ( !PyW_GetError(&err_str) ) + return false; + + msg("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); + return true; +} + +//--------------------------------------------------------------------------- +void *ida_export 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; +} + +//------------------------------------------------------------------------ +int idaapi pywraps_notify_when_t::idp_callback(void *ud, int event_id, va_list va) +{ + pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; + switch ( event_id ) + { + case processor_t::newfile: + case processor_t::oldfile: + { + // This hook gets called from the kernel. Ensure we hold the GIL. + // Note that PYW_GIL_GET appears in each case of the switch, which is to + // ensure that the GIL is retrieved ONLY when we need it. If PYW_GIL_GET + // appears outside the switch, it will be executed each time this callback + // is called, which results in a huge slowdown (at least on mac). + PYW_GIL_GET; + 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: + { + PYW_GIL_GET; + _this->notify(NW_CLOSEIDB_SLOT); + } + break; + } + // event not processed, let other plugins or the processor module handle it + return 0; +} + +//------------------------------------------------------------------------ +bool pywraps_notify_when_t::unnotify_when(int when, PyObject *py_callable) +{ + int cnt = 0; + for ( int slot=0; slot < NW_EVENTSCNT; slot++ ) + { + // convert index to flag and see + if ( ((1 << slot) & when) != 0 ) + { + unregister_callback(slot, py_callable); + ++cnt; + } + } + return cnt > 0; +} + +//------------------------------------------------------------------------ +void pywraps_notify_when_t::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 pywraps_notify_when_t::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); +} + +//------------------------------------------------------------------------ +bool pywraps_notify_when_t::init() +{ + return hook_to_notification_point(HT_IDP, idp_callback, this); +} + +//------------------------------------------------------------------------ +bool pywraps_notify_when_t::deinit() +{ + // Uninstall all objects + ref_vec_t::iterator it, it_end; + for ( int slot=0; slot < NW_EVENTSCNT; slot++ ) + { + for ( it = table[slot].begin(), it_end = table[slot].end(); it != it_end; ++it ) + unregister_callback(slot, it->o); + } + // ...and remove the notification + return unhook_from_notification_point(HT_IDP, idp_callback, this); +} + +//------------------------------------------------------------------------ +bool pywraps_notify_when_t::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 < NW_EVENTSCNT; slot++ ) + { + // is this flag set? + if ( ((1 << slot) & when) != 0 ) + { + register_callback(slot, py_callable); + ++cnt; + } + } + return cnt > 0; +} + +//------------------------------------------------------------------------ +bool pywraps_notify_when_t::notify(int slot, ...) +{ + va_list va; + va_start(va, slot); + bool ok = notify_va(slot, va); + va_end(va); + return ok; +} + +//------------------------------------------------------------------------ +bool pywraps_notify_when_t::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() ) + { + notify_when_args_vec_t::iterator it, it_end; + for ( 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; +} + +//------------------------------------------------------------------------- +static pywraps_notify_when_t *g_nw = NULL; + +//------------------------------------------------------------------------- +bool ida_export add_notify_when(int when, PyObject *py_callable) +{ + return g_nw != NULL && g_nw->notify_when(when, py_callable); +} + +//------------------------------------------------------------------------ +// Initializes the notify_when mechanism +// (Normally called by IDAPython plugin.init()) +static 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; +} + +//------------------------------------------------------------------------ +static 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 +static 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; +} + +//------------------------------------------------------------------------- +// lookup_info_t +//------------------------------------------------------------------------- +lookup_entry_t &ida_export lookup_info_t_new_entry(lookup_info_t *_this, py_customidamemo_t *py_view) +{ + QASSERT(30454, py_view != NULL && !_this->find_by_py_view(NULL, NULL, py_view)); + lookup_entry_t &e = _this->entries.push_back(); + e.py_view = py_view; + return e; +} + +//------------------------------------------------------------------------- +void ida_export lookup_info_t_commit(lookup_info_t *_this, lookup_entry_t &e, TForm *form, TCustomControl *view) +{ + QASSERT(30455, &e >= _this->entries.begin() && &e < _this->entries.end()); + QASSERT(30456, form != NULL && view != NULL && e.py_view != NULL + && !_this->find_by_form(NULL, NULL, form) + && !_this->find_by_view(NULL, NULL, view) + && _this->find_by_py_view(NULL, NULL, e.py_view)); + e.form = form; + e.view = view; +} + +//------------------------------------------------------------------------- +#define FIND_BY__BODY(self, crit, res1, res2) \ + { \ + for ( lookup_entries_t::const_iterator it = self->entries.begin(); it != self->entries.end(); ++it ) \ + { \ + const lookup_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 ida_export lookup_info_t_find_by_form( + const lookup_info_t *_this, + TCustomControl **out_view, + py_customidamemo_t **out_py_view, + const TForm *form) + FIND_BY__BODY(_this, form, view, py_view); + +bool ida_export lookup_info_t_find_by_py_view( + const lookup_info_t *_this, + TForm **out_form, + TCustomControl **out_view, + const py_customidamemo_t *py_view) + FIND_BY__BODY(_this, py_view, view, form); + +bool lookup_info_t::find_by_view( + TForm **out_form, + py_customidamemo_t **out_py_view, + const TCustomControl *view) const + FIND_BY__BODY(this, view, form, py_view); +#undef FIND_BY__BODY + +//------------------------------------------------------------------------- +bool ida_export lookup_info_t_del_by_py_view( + lookup_info_t *_this, + const py_customidamemo_t *py_view) +{ + for ( lookup_entries_t::iterator it = _this->entries.begin(); it != _this->entries.end(); ++it ) + { + if ( it->py_view == py_view ) + { + _this->entries.erase(it); + return true; + } + } + return false; +} + +//------------------------------------------------------------------------- +lookup_info_t pycim_lookup_info; + +//------------------------------------------------------------------------- +// py_customidamemo_t +//------------------------------------------------------------------------- +void py_customidamemo_t::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 +} + +//------------------------------------------------------------------------- +void ida_export 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 ( pycim_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 ida_export py_customidamemo_t_set_node_info( + py_customidamemo_t *_this, + 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; + _this->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(_this->view, idx, ni, flgs); +} + +//------------------------------------------------------------------------- +void ida_export py_customidamemo_t_set_nodes_infos( + py_customidamemo_t *_this, + 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; + _this->convert_node_info(&ni, &flags, value); + int idx = PyInt_AsLong(key.o); + viewer_set_node_info(_this->view, idx, ni, flags); + } +} + +//------------------------------------------------------------------------- +PyObject *ida_export py_customidamemo_t_get_node_info( + py_customidamemo_t *_this, + PyObject *py_node_idx) +{ + if ( !PyNumber_Check(py_node_idx) ) + Py_RETURN_NONE; + node_info_t ni; + if ( !viewer_get_node_info(_this->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 ida_export py_customidamemo_t_del_nodes_infos( + py_customidamemo_t *_this, + 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(_this->view, idx); + } +} + +//------------------------------------------------------------------------- +PyObject *ida_export py_customidamemo_t_get_current_renderer_type( + py_customidamemo_t *_this) +{ + tcc_renderer_type_t rt = get_view_renderer_type(_this->view); + return PyLong_FromLong(long(rt)); +} + +//------------------------------------------------------------------------- +void ida_export py_customidamemo_t_set_current_renderer_type( + py_customidamemo_t *_this, + 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(_this->view, rt); + } +} + +//------------------------------------------------------------------------- +PyObject *ida_export py_customidamemo_t_create_groups( + py_customidamemo_t *_this, + 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(_this->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 *ida_export py_customidamemo_t_delete_groups( + py_customidamemo_t *_this, + 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(_this->view, ida_groups, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +PyObject *ida_export py_customidamemo_t_set_groups_visibility( + py_customidamemo_t *_this, + 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(_this->view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +bool ida_export py_customidamemo_t_bind(py_customidamemo_t *_this, 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, _this->_self, _this->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 ida_export py_customidamemo_t_unbind(py_customidamemo_t *_this, bool clear_view) +{ + if ( _this->self == NULL ) + return; + PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", _this, _this->self.o, _this->view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); + PyObject_SetAttrString(_this->self.o, S_M_THIS, py_cobj.o); + _this->self = newref_t(NULL); + if ( clear_view ) + _this->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 ida_export py_customidamemo_t_collect_class_callbacks_ids( + py_customidamemo_t *_this, + pycim_callbacks_ids_t *out) +{ + out->add(S_ON_VIEW_ACTIVATED, _this->GRBASE_HAVE_VIEW_ACTIVATED); + out->add(S_ON_VIEW_DEACTIVATED, _this->GRBASE_HAVE_VIEW_DEACTIVATED); + out->add(S_ON_VIEW_KEYDOWN, _this->GRBASE_HAVE_KEYDOWN); + out->add(S_ON_POPUP, _this->GRBASE_HAVE_POPUP); + out->add(S_ON_VIEW_CLICK, _this->GRBASE_HAVE_VIEW_CLICK); + out->add(S_ON_VIEW_DBLCLICK, _this->GRBASE_HAVE_VIEW_DBLCLICK); + out->add(S_ON_VIEW_CURPOS, _this->GRBASE_HAVE_VIEW_CURPOS); + out->add(S_ON_CLOSE, _this->GRBASE_HAVE_CLOSE); + out->add(S_ON_VIEW_SWITCHED, _this->GRBASE_HAVE_VIEW_SWITCHED); + out->add(S_ON_VIEW_MOUSE_OVER, _this->GRBASE_HAVE_VIEW_MOUSE_OVER); + out->add(S_ON_VIEW_MOUSE_MOVED, _this->GRBASE_HAVE_VIEW_MOUSE_MOVED); +} + +//------------------------------------------------------------------------- +bool ida_export py_customidamemo_t_collect_pyobject_callbacks( + py_customidamemo_t *_this, + PyObject *o) +{ + pycim_callbacks_ids_t cbids; + _this->collect_class_callbacks_ids(&cbids); + _this->cb_flags = 0; + for ( pycim_callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it ) + { + const pycim_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 ) + _this->cb_flags |= have; + } + + return true; +} + +//------------------------------------------------------------------------- +void ida_export py_customidamemo_t_install_custom_viewer_handlers( + py_customidamemo_t *_this) +{ + if ( _this->has_callback(_this->GRBASE_HAVE_VIEW_MOUSE_MOVED) ) + { + // Set user-data + set_custom_viewer_handler(_this->view, CVH_USERDATA, (void *)_this); + + // + set_custom_viewer_handler(_this->view, CVH_MOUSEMOVE, (void *) py_customidamemo_t::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) +{ + newref_t py_module(PyImport_ImportModule(S_IDA_KERNWIN_MODNAME)); + ref_t py_result; + if ( py_module != NULL ) + { + ref_t py_class = PyW_TryGetAttrString(py_module.o, "renderer_pos_info_t"); + if ( py_class != NULL ) + { + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_class.o, NULL)); + if ( py_obj != NULL ) + { + newref_t py_node(PyInt_FromLong(event->renderer_pos.node)); + PyObject_SetAttrString(py_obj.o, "node", py_node.o); + newref_t py_cx(PyInt_FromLong(event->renderer_pos.cx)); + PyObject_SetAttrString(py_obj.o, "cx", py_cx.o); + newref_t py_cy(PyInt_FromLong(event->renderer_pos.cy)); + PyObject_SetAttrString(py_obj.o, "cy", py_cy.o); + newref_t py_sx(PyInt_FromLong(event->renderer_pos.sx)); + PyObject_SetAttrString(py_obj.o, "sx", py_sx.o); + py_result = py_obj; + } + } + } + if ( py_result != NULL ) + { + py_result.incref(); + return py_result.o; + } + else + { + return NULL; + } +} + +//------------------------------------------------------------------------- +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 + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +// 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(); } + +static 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 ida_export 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 ida_export 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); + +//------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +static qvector live_timers; +py_timer_ctx_t *ida_export python_timer_new(PyObject *py_callback) +{ + py_timer_ctx_t *t = new py_timer_ctx_t(); + t->pycallback = py_callback; + Py_INCREF(t->pycallback); + live_timers.push_back(t); + return t; +} + +//------------------------------------------------------------------------- +void ida_export python_timer_del(py_timer_ctx_t *t) +{ + QASSERT(30491, live_timers.del(t)); + Py_DECREF(t->pycallback); + delete t; +} + +//------------------------------------------------------------------------- +static void clear_python_timer_instances(void) +{ + // Pre-emptive strike: clear all the existing python-exposed + // timers, or there's a chance IDA will try and execute some + // python code after IDAPython is removed from memory. + while ( !live_timers.empty() ) + { + py_timer_ctx_t *t = live_timers[0]; + unregister_timer(t->timer_id); + python_timer_del(t); + } +} + +#undef DEF_REG_UNREG_REFCOUNTED diff --git a/pywraps.hpp b/pywraps.hpp index 1e6a138..afe7760 100644 --- a/pywraps.hpp +++ b/pywraps.hpp @@ -1,6 +1,8 @@ #ifndef __PYWRAPS_HPP__ #define __PYWRAPS_HPP__ +#include + //------------------------------------------------------------------------ // Types #ifndef PYUL_DEFINED @@ -14,6 +16,10 @@ #endif #endif +// a few forward decls +class insn_t; +class op_t; +struct switch_info_ex_t; // "A pointer can be explicitly converted to any integral type large // enough to hold it. The mapping function is implementation-defined." @@ -37,6 +43,11 @@ #define PTR2U64(Binding) (uint64(uint32(Binding))) #endif +#if defined(__LINUX__) || defined(__MAC__) +#define exported __attribute__((visibility("default"))) +#else +#define exported +#endif #ifdef __EA64__ #define PY_FMT64 "K" @@ -47,7 +58,10 @@ #endif //------------------------------------------------------------------------ -#define S_IDAAPI_MODNAME "idaapi" +#define S_IDA_IDAAPI_MODNAME "ida_idaapi" +#define S_IDA_NALT_MODNAME "ida_nalt" +#define S_IDA_UA_MODNAME "ida_ua" +#define S_IDA_KERNWIN_MODNAME "ida_kernwin" #define S_IDC_MODNAME "idc" #define S_IDAAPI_EXECSCRIPT "IDAPython_ExecScript" #define S_IDAAPI_COMPLETION "IDAPython_Completion" @@ -55,6 +69,83 @@ #define S_IDAAPI_LOADPROCMOD "IDAPython_LoadProcMod" #define S_IDAAPI_UNLOADPROCMOD "IDAPython_UnLoadProcMod" + +//------------------------------------------------------------------------ +// 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"; +static const char S_MAIN[] = "__main__"; + + +#ifdef __PYWRAPS__ +static const char S_PY_IDA_IDAAPI_MODNAME[] = "__main__"; +#else +static const char S_PY_IDA_IDAAPI_MODNAME[] = S_IDA_IDAAPI_MODNAME; +#endif + //------------------------------------------------------------------------ // PyIdc conversion object IDs #define PY_ICID_INT64 0 @@ -100,6 +191,13 @@ public: // Declare a variable to acquire/release the GIL #define PYW_GIL_GET gil_lock_t lock; +// Let's declare just the relevant bits, in order to not have to force +// including 'kernwin.hpp' in all files +#ifndef __KERNWIN_HPP +idaman uint32 ida_export_data debug; +#define IDA_DEBUG_PLUGIN 0x00000020 +THREAD_SAFE AS_PRINTF(1, 2) inline int msg(const char *format, ...); +#endif // __KERNWIN_HPP #define GIL_CHKCONDFAIL (((debug & IDA_DEBUG_PLUGIN) == IDA_DEBUG_PLUGIN) \ && PyGILState_GetThisThreadState() != _PyThreadState_Current) @@ -128,10 +226,14 @@ struct exc_report_t #define PYW_GIL_GET_AND_REPORT_ERROR PYW_GIL_GET; exc_report_t exc; +// Returns the linked object (void *) from a PyObject +idaman void * ida_export pyobj_get_clink(PyObject *pyobj); + //------------------------------------------------------------------------ // All the exported functions from PyWraps are forward declared here -insn_t *insn_t_get_clink(PyObject *self); -op_t *op_t_get_clink(PyObject *self); +inline insn_t *insn_t_get_clink(PyObject *self) { return (insn_t *)pyobj_get_clink(self); } +inline op_t *op_t_get_clink(PyObject *self) { return (op_t *)pyobj_get_clink(self); } +inline switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) { return (switch_info_ex_t *)pyobj_get_clink(self); } //------------------------------------------------------------------------- // The base for a reference. Will automatically increase the reference @@ -265,65 +367,52 @@ struct ref_vec_t : public qvector } }; - -// Returns a new reference to a class -// Return value: New reference. -ref_t get_idaapi_attr(const char *attr); - -// Returns a new reference to a class by its ID -// Return value: New reference. -ref_t get_idaapi_attr_by_id(const int class_id); - // Tries to import a module and swallows the exception if it fails and returns NULL // Return value: New reference. -ref_t PyW_TryImportModule(const char *name); +exported ref_t ida_export PyW_TryImportModule(const char *name); // Tries to get an attribute and swallows the exception if it fails and returns NULL -ref_t PyW_TryGetAttrString(PyObject *py_var, const char *attr); - -// Returns the linked object (void *) from a PyObject -void *pyobj_get_clink(PyObject *pyobj); +exported ref_t ida_export PyW_TryGetAttrString(PyObject *py_var, const char *attr); // Converts a Python number (LONGLONG or normal integer) to an IDC variable (VT_LONG or VT_INT64) -bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var); +exported bool ida_export PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var); // Returns a qstring from a Python attribute string -bool PyW_GetStringAttr( +exported bool ida_export PyW_GetStringAttr( PyObject *py_obj, const char *attr_name, qstring *str); // Converts a Python number to an uint64 and indicates whether the number was a long number -bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64 = NULL); +exported bool ida_export PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64 = NULL); // Checks if an Python object can be treated like a sequence -bool PyW_IsSequenceType(PyObject *obj); +exported bool ida_export PyW_IsSequenceType(PyObject *obj); // Returns an error string from the last exception (and clears it) -bool PyW_GetError(qstring *out = NULL, bool clear_err = true); -bool PyW_GetError(char *buf, size_t bufsz, bool clear_err = true); +exported bool ida_export PyW_GetError(qstring *out = NULL, bool clear_err = true); // If an error occured (it calls PyGetError) it displays it and return TRUE // This function is used when calling callbacks -bool PyW_ShowCbErr(const char *cb_name); +exported bool ida_export PyW_ShowCbErr(const char *cb_name); // Utility function to create linked class instances -ref_t create_idaapi_linked_class_instance(const char *clsname, void *lnk); +exported ref_t ida_export create_linked_class_instance(const char *modname, const char *clsname, void *lnk); // Returns the string representation of a PyObject -bool PyW_ObjectToString(PyObject *obj, qstring *out); +exported bool ida_export PyW_ObjectToString(PyObject *obj, qstring *out); // Utility function to convert a python object to an IDC object // and sets a python exception on failure. -bool pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj); +exported bool ida_export pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj); // Creates and initializes an IDC exception -error_t PyW_CreateIdcException(idc_value_t *res, const char *msg); +exported error_t ida_export PyW_CreateIdcException(idc_value_t *res, const char *msg); // // Conversion functions // -bool pyw_convert_idc_args( +exported bool ida_export pyw_convert_idc_args( const idc_value_t args[], int nargs, ref_vec_t &pargs, @@ -333,69 +422,81 @@ bool pyw_convert_idc_args( // Converts Python variable to IDC variable // gvar_sn is used in case the Python object was a created from a call to idcvar_to_pyvar and the IDC object was a VT_REF -int pyvar_to_idcvar( +exported int ida_export pyvar_to_idcvar( const ref_t &py_var, idc_value_t *idc_var, int *gvar_sn = NULL); // Converts from IDC to Python // We support converting VT_REF IDC variable types -int idcvar_to_pyvar( - const idc_value_t &idc_var, - ref_t *py_var); +exported int ida_export idcvar_to_pyvar( + const idc_value_t &idc_var, + ref_t *py_var); // Walks a Python list or Sequence and calls the callback -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) = NULL, - void *ud = NULL); -Py_ssize_t pyvar_walk_list( +exported Py_ssize_t ida_export pyvar_walk_list( PyObject *py_list, int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud) = NULL, void *ud = NULL); // Converts an intvec_t to a Python list object -ref_t PyW_IntVecToPyList(const intvec_t &intvec); +exported ref_t ida_export PyW_IntVecToPyList(const intvec_t &intvec); // Converts an Python list to an intvec -bool PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec); +exported bool ida_export PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec); + +// Converts an Python list to an eavec +exported bool ida_export PyW_PyListToEaVec(PyObject *py_list, eavec_t &eavec); // Converts a Python list to a qstrvec -bool PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec); +exported bool ida_export PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec); //------------------------------------------------------------------------- -PyObject *qstrvec2pylist(qstrvec_t &vec); +exported bool ida_export PyWStringOrNone_Check(PyObject *tp); //------------------------------------------------------------------------- -inline bool PyWStringOrNone_Check(PyObject *tp) -{ - return tp == Py_None || PyString_Check(tp); -} +#include +exported PyObject *ida_export meminfo_vec_t_to_py(meminfo_vec_t &areas); //------------------------------------------------------------------------- -inline const p_list * PyW_Fields(PyObject *tp) -{ - return tp == Py_None ? NULL : (const p_list *) PyString_AsString(tp); -} +exported void ida_export PyW_register_compiled_form(PyObject *py_form); + +//------------------------------------------------------------------------- +exported void ida_export PyW_unregister_compiled_form(PyObject *py_form); //--------------------------------------------------------------------------- -// // notify_when() -// -bool pywraps_nw_term(); -bool pywraps_nw_notify(int slot, ...); -bool pywraps_nw_init(); +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; -//--------------------------------------------------------------------------- -bool pywraps_check_autoscripts(char *buf, size_t bufsize); + static int idaapi idp_callback(void *ud, int event_id, va_list va); + bool unnotify_when(int when, PyObject *py_callable); + void register_callback(int slot, PyObject *py_callable); + void unregister_callback(int slot, PyObject *py_callable); -// [De]Initializes PyWraps -bool init_pywraps(); -void deinit_pywraps(); +public: + bool init(); + bool deinit(); + bool notify_when(int when, PyObject *py_callable); + bool notify(int slot, ...); + bool notify_va(int slot, va_list va); + pywraps_notify_when_t() : in_notify(false) {} +}; +exported bool ida_export add_notify_when(int when, PyObject *py_callable); -void hexrays_clear_python_cfuncptr_t_references(void); +// void hexrays_clear_python_cfuncptr_t_references(void); -void free_compiled_form_instances(void); +// void free_compiled_form_instances(void); //#define PYGDBG_ENABLED #ifdef PYGDBG_ENABLED @@ -407,8 +508,17 @@ void free_compiled_form_instances(void); //------------------------------------------------------------------------- struct pycall_res_t { - pycall_res_t(PyObject *pyo); - ~pycall_res_t(); + pycall_res_t(PyObject *pyo) + : result(pyo) + { + PYGLOG("return code: %p\n", result.o); + } + + ~pycall_res_t() + { + if ( PyErr_Occurred() ) + PyErr_Print(); + } inline bool success() const { return result.o != NULL; } @@ -418,4 +528,326 @@ private: pycall_res_t(); // No. }; -#endif +//------------------------------------------------------------------------- +// CustomIDAMemo wrappers +//-------------------------------------------------------------------------i +class lookup_info_t; +class py_customidamemo_t; +struct lookup_entry_t +{ + lookup_entry_t() : form(NULL), view(NULL), py_view(NULL) {} + + TForm *form; + TCustomControl *view; + py_customidamemo_t *py_view; +}; +DECLARE_TYPE_AS_MOVABLE(lookup_entry_t); +typedef qvector lookup_entries_t; + +#include +#define PY_LINFO_HLPPRM_new_entry (lookup_info_t *_this, py_customidamemo_t *py_view) +#define PY_LINFO_PARAMS_new_entry (py_customidamemo_t *py_view) +#define PY_LINFO_TRANSM_new_entry (this, py_view) + +#define PY_LINFO_HLPPRM_commit (lookup_info_t *_this, lookup_entry_t &e, TForm *form, TCustomControl *view) +#define PY_LINFO_PARAMS_commit (lookup_entry_t &e, TForm *form, TCustomControl *view) +#define PY_LINFO_TRANSM_commit (this, e, form, view) + +#define PY_LINFO_HLPPRM_find_by_form (const lookup_info_t *_this, TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) +#define PY_LINFO_PARAMS_find_by_form (TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) const +#define PY_LINFO_TRANSM_find_by_form (this, out_view, out_py_view, form) + +#define PY_LINFO_HLPPRM_find_by_py_view (const lookup_info_t *_this, TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) +#define PY_LINFO_PARAMS_find_by_py_view (TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) const +#define PY_LINFO_TRANSM_find_by_py_view (this, out_form, out_view, py_view) + +#define PY_LINFO_HLPPRM_del_by_py_view (lookup_info_t *_this, const py_customidamemo_t *py_view) +#define PY_LINFO_PARAMS_del_by_py_view (const py_customidamemo_t *py_view) +#define PY_LINFO_TRANSM_del_by_py_view (this, py_view) + +#define DECL_LINFO_HELPER(decl, RType, MName, MParams) \ + decl RType ida_export lookup_info_t_##MName MParams + +#define DECL_LINFO_HELPERS(decl) \ + DECL_LINFO_HELPER(decl, lookup_entry_t&, new_entry, PY_LINFO_HLPPRM_new_entry); \ + DECL_LINFO_HELPER(decl, void, commit, PY_LINFO_HLPPRM_commit); \ + DECL_LINFO_HELPER(decl, bool, find_by_form, PY_LINFO_HLPPRM_find_by_form); \ + DECL_LINFO_HELPER(decl, bool, find_by_py_view, PY_LINFO_HLPPRM_find_by_py_view);\ + DECL_LINFO_HELPER(decl, bool, del_by_py_view, PY_LINFO_HLPPRM_del_by_py_view); + + +DECL_LINFO_HELPERS(idaman); + +class py_customidamemo_t; +class lookup_info_t +{ + DECL_LINFO_HELPERS(friend); + +public: +#define PY_LINFO_TRAMPOLINE(RType, MName, MParams, PNames) \ + RType MName MParams { return lookup_info_t_##MName PNames; } + + PY_LINFO_TRAMPOLINE(lookup_entry_t&, new_entry, PY_LINFO_PARAMS_new_entry, PY_LINFO_TRANSM_new_entry); + PY_LINFO_TRAMPOLINE(void, commit, PY_LINFO_PARAMS_commit, PY_LINFO_TRANSM_commit); + PY_LINFO_TRAMPOLINE(bool, find_by_form, PY_LINFO_PARAMS_find_by_form, PY_LINFO_TRANSM_find_by_form); + PY_LINFO_TRAMPOLINE(bool, find_by_py_view, PY_LINFO_PARAMS_find_by_py_view, PY_LINFO_TRANSM_find_by_py_view); + PY_LINFO_TRAMPOLINE(bool, del_by_py_view, PY_LINFO_PARAMS_del_by_py_view, PY_LINFO_TRANSM_del_by_py_view); +#undef PY_LINFO_TRAMPOLINE + + bool find_by_view(TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const; + +private: + lookup_entries_t entries; +}; + +extern exported lookup_info_t ida_export_data pycim_lookup_info; + +//------------------------------------------------------------------------- +struct pycim_callback_id_t +{ + qstring name; + int have; +}; + +struct pycim_callbacks_ids_t : public qvector +{ + void add(const char *_n, int _h) + { + pycim_callback_id_t &o = push_back(); + o.name = _n; + o.have = _h; + } +}; + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- +#define PY_CIM_HLPPRM_set_node_info (py_customidamemo_t *_this, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) +#define PY_CIM_PARAMS_set_node_info (PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) +#define PY_CIM_TRANSM_set_node_info (this, py_node_idx, py_node_info, py_flags) + +#define PY_CIM_HLPPRM_set_nodes_infos (py_customidamemo_t *_this, PyObject *dict) +#define PY_CIM_PARAMS_set_nodes_infos (PyObject *dict) +#define PY_CIM_TRANSM_set_nodes_infos (this, dict) + +#define PY_CIM_HLPPRM_get_node_info (py_customidamemo_t *_this, PyObject *py_node_idx) +#define PY_CIM_PARAMS_get_node_info (PyObject *py_node_idx) +#define PY_CIM_TRANSM_get_node_info (this, py_node_idx) + +#define PY_CIM_HLPPRM_del_nodes_infos (py_customidamemo_t *_this, PyObject *py_nodes) +#define PY_CIM_PARAMS_del_nodes_infos (PyObject *py_nodes) +#define PY_CIM_TRANSM_del_nodes_infos (this, py_nodes) + +#define PY_CIM_HLPPRM_get_current_renderer_type (py_customidamemo_t *_this) +#define PY_CIM_PARAMS_get_current_renderer_type () +#define PY_CIM_TRANSM_get_current_renderer_type (this) + +#define PY_CIM_HLPPRM_set_current_renderer_type (py_customidamemo_t *_this, PyObject *py_rto) +#define PY_CIM_PARAMS_set_current_renderer_type (PyObject *py_rto) +#define PY_CIM_TRANSM_set_current_renderer_type (this, py_rto) + +#define PY_CIM_HLPPRM_create_groups (py_customidamemo_t *_this, PyObject *groups_infos) +#define PY_CIM_PARAMS_create_groups (PyObject *groups_infos) +#define PY_CIM_TRANSM_create_groups (this, groups_infos) + +#define PY_CIM_HLPPRM_delete_groups (py_customidamemo_t *_this, PyObject *groups, PyObject *new_current) +#define PY_CIM_PARAMS_delete_groups (PyObject *groups, PyObject *new_current) +#define PY_CIM_TRANSM_delete_groups (this, groups, new_current) + +#define PY_CIM_HLPPRM_set_groups_visibility (py_customidamemo_t *_this, PyObject *groups, PyObject *expand, PyObject *new_current) +#define PY_CIM_PARAMS_set_groups_visibility (PyObject *groups, PyObject *expand, PyObject *new_current) +#define PY_CIM_TRANSM_set_groups_visibility (this, groups, expand, new_current) + +#define PY_CIM_HLPPRM_collect_pyobject_callbacks (py_customidamemo_t *_this, PyObject *self) +#define PY_CIM_PARAMS_collect_pyobject_callbacks (PyObject *in_self) +#define PY_CIM_TRANSM_collect_pyobject_callbacks (this, in_self) + +#define PY_CIM_HLPPRM_collect_class_callbacks_ids (py_customidamemo_t *_this, pycim_callbacks_ids_t *out) +#define PY_CIM_PARAMS_collect_class_callbacks_ids (pycim_callbacks_ids_t *out) +#define PY_CIM_TRANSM_collect_class_callbacks_ids (this, out) + +#define PY_CIM_HLPPRM_install_custom_viewer_handlers (py_customidamemo_t *_this) +#define PY_CIM_PARAMS_install_custom_viewer_handlers () +#define PY_CIM_TRANSM_install_custom_viewer_handlers (this) + +#define PY_CIM_HLPPRM_bind (py_customidamemo_t *_this, PyObject *self, TCustomControl *view) +#define PY_CIM_PARAMS_bind (PyObject *in_self, TCustomControl *in_view) +#define PY_CIM_TRANSM_bind (this, in_self, in_view) + +#define PY_CIM_HLPPRM_unbind (py_customidamemo_t *_this, bool clear_view) +#define PY_CIM_PARAMS_unbind (bool clear_view) +#define PY_CIM_TRANSM_unbind (this, clear_view) + +#define PY_CIM_HLPPRM_ensure_view_callbacks_installed () + +#define DECL_CIM_HELPER(decl, RType, MName, MParams) \ + decl RType ida_export py_customidamemo_t_##MName MParams + +#define DECL_CIM_HELPERS(decl) \ + DECL_CIM_HELPER(decl, void, set_node_info, PY_CIM_HLPPRM_set_node_info); \ + DECL_CIM_HELPER(decl, void, set_nodes_infos, PY_CIM_HLPPRM_set_nodes_infos); \ + DECL_CIM_HELPER(decl, PyObject*, get_node_info, PY_CIM_HLPPRM_get_node_info); \ + DECL_CIM_HELPER(decl, void, del_nodes_infos, PY_CIM_HLPPRM_del_nodes_infos); \ + DECL_CIM_HELPER(decl, PyObject*, get_current_renderer_type, PY_CIM_HLPPRM_get_current_renderer_type); \ + DECL_CIM_HELPER(decl, void, set_current_renderer_type, PY_CIM_HLPPRM_set_current_renderer_type); \ + DECL_CIM_HELPER(decl, PyObject*, create_groups, PY_CIM_HLPPRM_create_groups); \ + DECL_CIM_HELPER(decl, PyObject*, delete_groups, PY_CIM_HLPPRM_delete_groups); \ + DECL_CIM_HELPER(decl, PyObject*, set_groups_visibility, PY_CIM_HLPPRM_set_groups_visibility);\ + \ + DECL_CIM_HELPER(decl, bool, collect_pyobject_callbacks, PY_CIM_HLPPRM_collect_pyobject_callbacks); \ + DECL_CIM_HELPER(decl, void, collect_class_callbacks_ids, PY_CIM_HLPPRM_collect_class_callbacks_ids); \ + DECL_CIM_HELPER(decl, void, install_custom_viewer_handlers, PY_CIM_HLPPRM_install_custom_viewer_handlers); \ + DECL_CIM_HELPER(decl, bool, bind, PY_CIM_HLPPRM_bind); \ + DECL_CIM_HELPER(decl, void, unbind, PY_CIM_HLPPRM_unbind); \ + DECL_CIM_HELPER(decl, void, ensure_view_callbacks_installed, PY_CIM_HLPPRM_ensure_view_callbacks_installed) + +DECL_CIM_HELPERS(idaman); + +//------------------------------------------------------------------------- +#define PY_CIM_TRAMPOLINE(RType, MName, MParams, PNames) \ + RType MName MParams { return py_customidamemo_t_##MName PNames; } + +class py_customidamemo_t +{ + void convert_node_info( + node_info_t *out, + uint32 *out_flags, + ref_t py_nodeinfo); + + 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, + }; + + 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 + + // 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); + 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); + + DECL_CIM_HELPERS(friend); + +protected: + ref_t self; + TCustomControl *view; + pycim_callbacks_ids_t cbids; + + PY_CIM_TRAMPOLINE(bool, collect_pyobject_callbacks, PY_CIM_PARAMS_collect_pyobject_callbacks, PY_CIM_TRANSM_collect_pyobject_callbacks); + PY_CIM_TRAMPOLINE(virtual void, collect_class_callbacks_ids, PY_CIM_PARAMS_collect_class_callbacks_ids, PY_CIM_TRANSM_collect_class_callbacks_ids); + PY_CIM_TRAMPOLINE(void, install_custom_viewer_handlers, PY_CIM_PARAMS_install_custom_viewer_handlers, PY_CIM_TRANSM_install_custom_viewer_handlers); + PY_CIM_TRAMPOLINE(bool, bind, PY_CIM_PARAMS_bind, PY_CIM_TRANSM_bind); + PY_CIM_TRAMPOLINE(void, unbind, PY_CIM_PARAMS_unbind, PY_CIM_TRANSM_unbind); + + friend TForm *pycim_get_tform(PyObject *self); + friend TCustomControl *pycim_get_tcustom_control(PyObject *self); + +public: + py_customidamemo_t() + : self(newref_t(NULL)), + view(NULL) + { + PYGLOG("%p: py_customidamemo_t()\n", this); + py_customidamemo_t_ensure_view_callbacks_installed(); + ovc_num_args = -1; + ovdc_num_args = -1; + ovmo_num_args = -1; + ovmm_num_args = -1; + } + virtual ~py_customidamemo_t() + { + PYGLOG("%p: ~py_customidamemo_t()\n", this); + unbind(true); + pycim_lookup_info.del_by_py_view(this); + } + + virtual void refresh() + { + refresh_viewer(view); + } + inline bool has_callback(int flag) { return (cb_flags & flag) != 0; } + + PY_CIM_TRAMPOLINE(void, set_node_info, PY_CIM_PARAMS_set_node_info, PY_CIM_TRANSM_set_node_info); + PY_CIM_TRAMPOLINE(void, set_nodes_infos, PY_CIM_PARAMS_set_nodes_infos, PY_CIM_TRANSM_set_nodes_infos); + PY_CIM_TRAMPOLINE(PyObject*, get_node_info, PY_CIM_PARAMS_get_node_info, PY_CIM_TRANSM_get_node_info); + PY_CIM_TRAMPOLINE(void, del_nodes_infos, PY_CIM_PARAMS_del_nodes_infos, PY_CIM_TRANSM_del_nodes_infos); + PY_CIM_TRAMPOLINE(PyObject*, get_current_renderer_type, PY_CIM_PARAMS_get_current_renderer_type, PY_CIM_TRANSM_get_current_renderer_type); + PY_CIM_TRAMPOLINE(void, set_current_renderer_type, PY_CIM_PARAMS_set_current_renderer_type, PY_CIM_TRANSM_set_current_renderer_type); + PY_CIM_TRAMPOLINE(PyObject*, create_groups, PY_CIM_PARAMS_create_groups, PY_CIM_TRANSM_create_groups); + PY_CIM_TRAMPOLINE(PyObject*, delete_groups, PY_CIM_PARAMS_delete_groups, PY_CIM_TRANSM_delete_groups); + PY_CIM_TRAMPOLINE(PyObject*, set_groups_visibility, PY_CIM_PARAMS_set_groups_visibility, PY_CIM_TRANSM_set_groups_visibility); +}; +#undef PY_CIM_TRAMPOLINE + +#undef DECL_CIM_HELPERS +#undef DECL_CIM_HELPER + + +//------------------------------------------------------------------------- +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); +} + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- +#include +#define DECL_REG_UNREG_REFCOUNTED(Type) \ + exported void ida_export til_register_python_##Type##_instance(Type *inst); \ + exported void ida_export til_deregister_python_##Type##_instance(Type *inst); +DECL_REG_UNREG_REFCOUNTED(tinfo_t); +DECL_REG_UNREG_REFCOUNTED(ptr_type_data_t); +DECL_REG_UNREG_REFCOUNTED(array_type_data_t); +DECL_REG_UNREG_REFCOUNTED(func_type_data_t); +DECL_REG_UNREG_REFCOUNTED(udt_type_data_t); +#undef DECL_REG_UNREG_REFCOUNTED + +//------------------------------------------------------------------------- +// Context structure used by register/unregister timer +struct py_timer_ctx_t +{ + py_timer_ctx_t() : timer_id(NULL), pycallback(NULL) {} + qtimer_t timer_id; + PyObject *pycallback; +}; +exported py_timer_ctx_t *ida_export python_timer_new(PyObject *py_callback); +exported void ida_export python_timer_del(py_timer_ctx_t *t); + +#endif // __PYWRAPS_HPP__ diff --git a/pywraps/deploy_all.py b/pywraps/deploy_all.py deleted file mode 100644 index 90f859c..0000000 --- a/pywraps/deploy_all.py +++ /dev/null @@ -1,212 +0,0 @@ -# Please use the same tag for the same .i file -# That means if many insertions are going to happen in one -# given .i file then don't use more than code marking tag - -print "\n-------- DEPLOY started --------------------------------------------------\n" - -deploys = { - "idaapi (common functions, notifywhen)" : { - "tag" : "py_idaapi", - "src" : ["py_cvt.hpp", "py_idaapi.hpp", "py_idaapi.py", "py_notifywhen.hpp", "py_notifywhen.py"], - "tgt" : "../swig/idaapi.i" - }, - - "View (common)" : { - "tag" : "py_view_base", - "src" : ["py_view_base.hpp", "py_view_base.py"], - "tgt" : "../swig/view.i" - }, - - "IDAView" : { - "tag" : "py_idaview", - "src" : ["py_idaview.hpp", "py_idaview.py"], - "tgt" : "../swig/view.i" - }, - - "Graph" : { - "tag" : "py_graph", - "src" : ["py_graph.hpp", "py_graph.py"], - "tgt" : "../swig/graph.i" - }, - - "custview" : { - "tag" : "py_custviewer", - "src" : ["py_custview.py","py_custview.hpp"], - "tgt" : "../swig/kernwin.i.in" - }, - - "plgform" : { - "tag" : "py_plgform", - "src" : ["py_plgform.hpp","py_plgform.py"], - "tgt" : "../swig/kernwin.i.in" - }, - - "expr" : { - "tag" : "py_expr", - "src" : ["py_expr.hpp","py_expr.py"], - "tgt" : "../swig/expr.i" - }, - - "cli" : { - "tag" : "py_cli", - "src" : ["py_cli.py","py_cli.hpp"], - "tgt" : "../swig/kernwin.i.in" - }, - - "Loader" : { - "tag" : "py_loader", - "src" : ["py_loader.hpp"], - "tgt" : "../swig/loader.i" - }, - - "kernwin, choose2, askusingform" : { - "tag" : "py_kernwin", - "src" : ["py_kernwin.hpp","py_kernwin.py","py_choose.hpp","py_choose2.hpp","py_choose2.py","py_askusingform.hpp","py_askusingform.py"], - "tgt" : "../swig/kernwin.i.in" - }, - - "idd" : { - "tag" : "py_idd", - "src" : ["py_dbg.hpp","py_appcall.py"], - "tgt" : "../swig/idd.i" - }, - - "idd (python)" : { - "tag" : "py_idd_2", - "src" : ["py_dbg.py"], - "tgt" : "../swig/idd.i" - }, - - "nalt" : { - "tag" : "py_nalt", - "src" : ["py_nalt.hpp","py_nalt.py"], - "tgt" : "../swig/nalt.i" - }, - - "dbg" : { - "tag" : "py_dbg", - "src" : ["py_dbg.hpp"], - "tgt" : "../swig/dbg.i.in" - }, - - "linput/diskio" : { - "tag" : "py_diskio", - "src" : ["py_linput.hpp","py_diskio.hpp","py_diskio.py"], - "tgt" : "../swig/diskio.i" - }, - - "name" : { - "tag" : "py_name", - "src" : ["py_name.hpp","py_name.py"], - "tgt" : "../swig/name.i" - }, - - "qfile" : { - "tag" : "py_qfile", - "src" : ["py_qfile.hpp"], - "tgt" : "../swig/fpro.i" - }, - - "bytes" : { - "tag" : "py_bytes", - "src" : ["py_bytes.hpp","py_custdata.py","py_custdata.hpp"], - "tgt" : "../swig/bytes.i" - }, - - "typeinf" : { - "tag" : "py_typeinf", - "src" : ["py_typeinf.hpp","py_typeinf.py"], - "tgt" : "../swig/typeinf.i" - }, - - "gdl" : { - "tag" : "py_gdl", - "src" : ["py_gdl.py"], - "tgt" : "../swig/gdl.i" - }, - - "ua" : { - "tag" : "py_ua", - "src" : ["py_ua.hpp","py_ua.py"], - "tgt" : "../swig/ua.i" - }, - - "idp" : { - "tag" : "py_idp", - "src" : ["py_idp.hpp"], - "tgt" : "../swig/idp.i.in" - }, - - "lines" : { - "tag" : "py_lines", - "src" : ["py_lines.hpp","py_lines.py"], - "tgt" : "../swig/lines.i" - }, - - "registry" : { - "tag" : "py_registry", - "src" : ["py_registry.hpp"], - "tgt" : "../swig/registry.i" - }, - - "struct" : { - "tag" : "py_struct", - "src" : ["py_struct.hpp"], - "tgt" : "../swig/struct.i" - }, - - "pc_win32_appcall" : { - "tag" : "appcalltest", - "src" : ["py_appcall.py"], - "tgt" : "../../../tests/input/pc_win32_appcall.pe.hints" - }, - - "ex_custdata example" : { - "tag" : "ex_custdata", - "src" : ["../examples/ex_custdata.py"], - "tgt" : "../../../tests/input/pc_win32_custdata1.pe.hints" - }, - - "ex_choose2" : { - "tag" : "py_choose2ex1", - "src" : ["py_choose2.py"], - "tgt" : "../examples/ex_choose2.py" - }, - - "ex_formchooser" : { - "tag" : "ex_formchooser", - "src" : ["py_askusingform.py"], - "tgt" : "../../formchooser/formchooser.py" - }, - - "ex_askusingform" : { - "tag" : "ex_askusingform", - "src" : ["py_askusingform.py"], - "tgt" : "../examples/ex_askusingform.py" - }, - - "ex_cli example" : { - "tag" : "ex_cli_ex1", - "src" : ["py_cli.py"], - "tgt" : "../examples/ex_cli.py" - }, - - "ex_expr example" : { - "tag" : "ex_expr", - "src" : ["py_expr.py"], - "tgt" : "../examples/ex_expr.py" - }, - - "ex_custview.py example" : { - "tag" : "py_custviewerex1", - "src" : ["py_custview.py"], - "tgt" : "../examples/ex_custview.py" - } - } - -import deploy -for name in deploys: - data = deploys[name] - print "Deploying %s" % name - deploy.deploy(data["tag"], data["src"], data["tgt"]) - diff --git a/pywraps/driver.cpp b/pywraps/driver.cpp new file mode 100644 index 0000000..05412f3 --- /dev/null +++ b/pywraps/driver.cpp @@ -0,0 +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 +}; diff --git a/pywraps/driver_bytes.cpp b/pywraps/driver_bytes.cpp new file mode 100644 index 0000000..cf9446b --- /dev/null +++ b/pywraps/driver_bytes.cpp @@ -0,0 +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 } +}; +DRIVER_INIT_METHODS(bytes); \ No newline at end of file diff --git a/pywraps/driver_chooser.cpp b/pywraps/driver_chooser.cpp new file mode 100644 index 0000000..f3775dc --- /dev/null +++ b/pywraps/driver_chooser.cpp @@ -0,0 +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); diff --git a/pywraps/driver_cli.cpp b/pywraps/driver_cli.cpp new file mode 100644 index 0000000..208c81d --- /dev/null +++ b/pywraps/driver_cli.cpp @@ -0,0 +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 } +}; +DRIVER_INIT_METHODS(cli); \ No newline at end of file diff --git a/pywraps/driver_custview.cpp b/pywraps/driver_custview.cpp new file mode 100644 index 0000000..fec3d8c --- /dev/null +++ b/pywraps/driver_custview.cpp @@ -0,0 +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; i < qnumber(sample_lines); i++ ) + { + lines.push_back(simpleline_t("")); // add empty line + lines.push_back(simpleline_t(sample_lines[i].text)); + lines.back().bgcolor = sample_lines[i].color; + } + } + my_custviewer() + { + id_n = 0; + init_sample_lines(); + data.set_minmax(); + } + bool init(const char *title) + { + if ( id_n != 0 ) + return true; + if ( !create(title, HAVE_HINT | HAVE_CLICK | HAVE_KEYDOWN | HAVE_CURPOS, &data) ) + return false; + id_n = add_popup_menu("Do this", "N"); + return true; + } +}; + +my_custviewer *g_cv; + +//------------------------------------------------------------------------- +static PyObject *ex_pyscv_init(PyObject *self, PyObject *args) +{ + const char *title; + PyObject *py_link; + if ( !PyArg_ParseTuple(args, "Os", &py_link, &title) ) + return NULL; + return pyscv_init(py_link, title); +} + +static PyObject *ex_pyscv_add_line(PyObject *self, PyObject *args) +{ + PyObject *py_this, *py_sl; + if ( !PyArg_ParseTuple(args, "OO", &py_this, &py_sl) ) + return NULL; + return Py_BuildValue("i", pyscv_add_line(py_this, py_sl)); +} + +static PyObject *ex_pyscv_delete(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return Py_BuildValue("i", pyscv_delete(py_this)); +} + +static PyObject *ex_pyscv_show(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return Py_BuildValue("i", pyscv_show(py_this)); +} + +static PyObject *ex_pyscv_refresh(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return Py_BuildValue("i", pyscv_refresh(py_this)); +} + +static PyObject *ex_pyscv_close(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + pyscv_close(py_this); + Py_RETURN_NONE; +} + +static PyObject *ex_pyscv_clear_popup_menu(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + pyscv_clear_popup_menu(py_this); + Py_RETURN_NONE; +} + +static PyObject *ex_pyscv_del_line(PyObject *self, PyObject *args) +{ + PyObject *py_this; + size_t nline; + if ( !PyArg_ParseTuple(args, "O" PY_FMT64, &py_this, &nline) ) + return NULL; + return Py_BuildValue("i", pyscv_del_line(py_this, nline)); +} + +static PyObject *ex_pyscv_get_pos(PyObject *self, PyObject *args) +{ + PyObject *py_this; + int mouse; + if ( !PyArg_ParseTuple(args, "Oi", &py_this, &mouse) ) + return NULL; + return pyscv_get_pos(py_this, mouse == 0 ? false : true); +} + +static PyObject *ex_pyscv_refresh_current(PyObject *self, PyObject *args) +{ + PyObject *py_this; + int mouse; + if ( !PyArg_ParseTuple(args, "Oi", &py_this, &mouse) ) + return NULL; + return Py_BuildValue("i", pyscv_refresh_current(py_this)); +} + +static PyObject *ex_pyscv_get_current_line(PyObject *self, PyObject *args) +{ + PyObject *py_this; + int mouse, notags; + if ( !PyArg_ParseTuple(args, "Oii", &py_this, &mouse, ¬ags) ) + return NULL; + return pyscv_get_current_line(py_this, mouse == 0 ? false : true, notags == 0 ? false : true); +} + +static PyObject *ex_pyscv_is_focused(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return Py_BuildValue("i", pyscv_is_focused(py_this)); +} + +static PyObject *ex_pyscv_add_popup_menu(PyObject *self, PyObject *args) +{ + PyObject *py_this; + const char *title, *hotkey; + if ( !PyArg_ParseTuple(args, "Oss", &py_this, &title, &hotkey) ) + return NULL; + return Py_BuildValue("i", pyscv_add_popup_menu(py_this, title, hotkey)); +} + +static PyObject *ex_pyscv_get_line(PyObject *self, PyObject *args) +{ + PyObject *py_this; + size_t nline; + if ( !PyArg_ParseTuple(args, "O" PY_FMT64, &py_this, &nline) ) + return NULL; + return pyscv_get_line(py_this, nline); +} + +static PyObject *ex_pyscv_jumpto(PyObject *self, PyObject *args) +{ + PyObject *py_this; + int x, y; + size_t lineno; + if ( !PyArg_ParseTuple(args, "O" PY_FMT64 "ii", &py_this, &lineno, &x, &y) ) + return NULL; + return Py_BuildValue("i", pyscv_jumpto(py_this, lineno, x, y)); +} + +static PyObject *ex_pyscv_edit_line(PyObject *self, PyObject *args) +{ + PyObject *py_this, *py_sl; + size_t lineno; + if ( !PyArg_ParseTuple(args, "O" PY_FMT64 "O", &py_this, &lineno, &py_sl) ) + return NULL; + return Py_BuildValue("i", pyscv_edit_line(py_this, lineno, py_sl)); +} + +static PyObject *ex_pyscv_insert_line(PyObject *self, PyObject *args) +{ + PyObject *py_this, *py_sl; + size_t lineno; + if ( !PyArg_ParseTuple(args, "O" PY_FMT64 "O", &py_this, &lineno, &py_sl) ) + return NULL; + return Py_BuildValue("i", pyscv_insert_line(py_this, lineno, py_sl)); +} + +static PyObject *ex_pyscv_patch_line(PyObject *self, PyObject *args) +{ + PyObject *py_this; + size_t lineno, offs; + int value; + if ( !PyArg_ParseTuple(args, "O" PY_FMT64 PY_FMT64 "i", &py_this, &lineno, &offs, &value) ) + return NULL; + return Py_BuildValue("i", pyscv_patch_line(py_this, lineno, offs, value)); +} + +static PyObject *ex_pyscv_count(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return Py_BuildValue(PY_FMT64, pyscv_count(py_this)); +} + +static PyObject *ex_pyscv_get_selection(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return pyscv_get_selection(py_this); +} + +static PyObject *ex_pyscv_get_current_word(PyObject *self, PyObject *args) +{ + PyObject *py_this; + int mouse; + if ( !PyArg_ParseTuple(args, "Oi", &py_this, &mouse) ) + return NULL; + return pyscv_get_current_word(py_this, mouse != 0); +} + +static PyObject *ex_pyscv_clear_lines(PyObject *self, PyObject *args) +{ + PyObject *py_this; + if ( !PyArg_ParseTuple(args, "O", &py_this) ) + return NULL; + return pyscv_clear_lines(py_this); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_custview[] = +{ + { "pyscv_init", ex_pyscv_init, METH_VARARGS, "" }, + { "pyscv_close", ex_pyscv_close, METH_VARARGS, "" }, + { "pyscv_add_line", ex_pyscv_add_line, METH_VARARGS, "" }, + { "pyscv_delete", ex_pyscv_delete, METH_VARARGS, "" }, + { "pyscv_refresh", ex_pyscv_refresh, METH_VARARGS, "" }, + { "pyscv_clear_lines", ex_pyscv_clear_lines, METH_VARARGS, "" }, + { "pyscv_show", ex_pyscv_show, METH_VARARGS, "" }, + { "pyscv_clear_popup_menu", ex_pyscv_clear_popup_menu, METH_VARARGS, "" }, + { "pyscv_del_line", ex_pyscv_del_line, METH_VARARGS, "" }, + { "pyscv_get_pos", ex_pyscv_get_pos, METH_VARARGS, "" }, + { "pyscv_refresh_current", ex_pyscv_refresh_current, METH_VARARGS, "" }, + { "pyscv_get_current_line", ex_pyscv_get_current_line, METH_VARARGS, "" }, + { "pyscv_is_focused", ex_pyscv_is_focused, METH_VARARGS, "" }, + { "pyscv_add_popup_menu", ex_pyscv_add_popup_menu, METH_VARARGS, "" }, + { "pyscv_get_line", ex_pyscv_get_line, METH_VARARGS, "" }, + { "pyscv_jumpto", ex_pyscv_jumpto, METH_VARARGS, "" }, + { "pyscv_edit_line", ex_pyscv_edit_line, METH_VARARGS, "" }, + { "pyscv_insert_line", ex_pyscv_insert_line, METH_VARARGS, "" }, + { "pyscv_count", ex_pyscv_count, METH_VARARGS, "" }, + { "pyscv_patch_line", ex_pyscv_patch_line, METH_VARARGS, "" }, + { "pyscv_get_selection", ex_pyscv_get_selection, METH_VARARGS, "" }, + { "pyscv_get_current_word", ex_pyscv_get_current_word, METH_VARARGS, "" }, + { NULL, NULL, 0, NULL } /* Sentinel */ +}; +DRIVER_INIT_METHODS(custview); + +#define DRIVER_RUN +void driver_run(int) +{ + if ( !g_cv->init("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 new file mode 100644 index 0000000..f2ff86e --- /dev/null +++ b/pywraps/driver_dbg.cpp @@ -0,0 +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); diff --git a/pywraps/driver_diskio.cpp b/pywraps/driver_diskio.cpp new file mode 100644 index 0000000..8f99dda --- /dev/null +++ b/pywraps/driver_diskio.cpp @@ -0,0 +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 */ +}; +DRIVER_INIT_METHODS(diskio); \ No newline at end of file diff --git a/pywraps/driver_expr.cpp b/pywraps/driver_expr.cpp new file mode 100644 index 0000000..c636adb --- /dev/null +++ b/pywraps/driver_expr.cpp @@ -0,0 +1,56 @@ +#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)); +} + +//--------------------------------------------------------------------------- +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)); +} + +static PyObject *ex_py_set_idc_func_ex(PyObject *self, PyObject *pyargs) +{ + 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)); +} + +//--------------------------------------------------------------------------- +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); diff --git a/pywraps/driver_graph.cpp b/pywraps/driver_graph.cpp new file mode 100644 index 0000000..6216c78 --- /dev/null +++ b/pywraps/driver_graph.cpp @@ -0,0 +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 */ +}; +DRIVER_INIT_METHODS(graph); \ No newline at end of file diff --git a/pywraps/driver_kernwin.cpp b/pywraps/driver_kernwin.cpp new file mode 100644 index 0000000..1c28e45 --- /dev/null +++ b/pywraps/driver_kernwin.cpp @@ -0,0 +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 */ +}; +DRIVER_INIT_METHODS(kernwin); \ No newline at end of file diff --git a/pywraps/driver_nalt.cpp b/pywraps/driver_nalt.cpp new file mode 100644 index 0000000..395d18b --- /dev/null +++ b/pywraps/driver_nalt.cpp @@ -0,0 +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); diff --git a/pywraps/driver_notifywhen.cpp b/pywraps/driver_notifywhen.cpp new file mode 100644 index 0000000..662fe77 --- /dev/null +++ b/pywraps/driver_notifywhen.cpp @@ -0,0 +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(); +} diff --git a/pywraps/link_gen.py b/pywraps/link_gen.py deleted file mode 100755 index 1ff7608..0000000 --- a/pywraps/link_gen.py +++ /dev/null @@ -1,336 +0,0 @@ -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_area.hpp b/pywraps/py_area.hpp new file mode 100644 index 0000000..85c5a4b --- /dev/null +++ b/pywraps/py_area.hpp @@ -0,0 +1,11 @@ + +// +enum areacb_type_t +{ + AREACB_TYPE_UNKNOWN, + AREACB_TYPE_FUNC, + AREACB_TYPE_SEGMENT, + AREACB_TYPE_HIDDEN_AREA, + AREACB_TYPE_SRAREA, +}; +// diff --git a/pywraps/py_custdata.hpp b/pywraps/py_bytes_custdata.hpp similarity index 98% rename from pywraps/py_custdata.hpp rename to pywraps/py_bytes_custdata.hpp index edc4bc0..369bd9e 100644 --- a/pywraps/py_custdata.hpp +++ b/pywraps/py_bytes_custdata.hpp @@ -1,7 +1,7 @@ -#ifndef __PY_IDA_CUSTDATA__ -#define __PY_IDA_CUSTDATA__ +#ifndef __PY_IDA_BYTES_CUSTDATA__ +#define __PY_IDA_BYTES_CUSTDATA__ -// +// class py_custom_data_type_t { data_type_t dt; @@ -445,7 +445,7 @@ public: } 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 ) + for ( POS it=entries.begin(), it_end = entries.end(); it != it_end; ++it ) { if ( it->dfid == dfid && it->dtid == dtid ) { @@ -492,10 +492,10 @@ static PyObject *py_data_format_to_py_dict(const data_format_t *df) S_MENU_NAME, df->menu_name == NULL ? "" : df->menu_name, S_HOTKEY, df->hotkey == NULL ? "" : df->hotkey); } -// +// //------------------------------------------------------------------------ -// +// //------------------------------------------------------------------------ /* @@ -676,6 +676,6 @@ static PyObject *py_get_custom_data_type(int dtid) return py_data_type_to_py_dict(dt); } -// +// -#endif +#endif // __PY_IDA_BYTES_CUSTDATA__ diff --git a/pywraps/py_custdata.py b/pywraps/py_bytes_custdata.py similarity index 87% rename from pywraps/py_custdata.py rename to pywraps/py_bytes_custdata.py index a0405d4..6595424 100755 --- a/pywraps/py_custdata.py +++ b/pywraps/py_bytes_custdata.py @@ -1,34 +1,6 @@ # ----------------------------------------------------------------------- -# 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. @@ -48,7 +20,7 @@ class data_type_t(object): def register(self): """Registers the data type and returns the type id or < 0 on failure""" - return _idaapi.register_custom_data_type(self) + return _ida_bytes.register_custom_data_type(self) def unregister(self): """Unregisters the data type and returns True on success""" @@ -57,7 +29,7 @@ class data_type_t(object): return True # Try to unregister - r = _idaapi.unregister_custom_data_type(self.id) + r = _ida_bytes.unregister_custom_data_type(self.id) # Clear the ID if r: @@ -111,7 +83,7 @@ class data_format_t(object): 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) + return _ida_bytes.register_custom_data_format(dtid, self) def unregister(self, dtid): """Unregisters the data format with the given data type id""" @@ -121,7 +93,7 @@ class data_format_t(object): return True # Unregister - r = _idaapi.unregister_custom_data_format(dtid, self.id) + r = _ida_bytes.unregister_custom_data_format(dtid, self.id) # Clear the ID if r: @@ -243,5 +215,5 @@ def unregister_data_types_and_formats(formats): ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format, False) return 1 if ok else -1 -# +# # ----------------------------------------------------------------------- diff --git a/pywraps/py_dbg.hpp b/pywraps/py_dbg.hpp index 750a8ea..7aaa64b 100644 --- a/pywraps/py_dbg.hpp +++ b/pywraps/py_dbg.hpp @@ -2,7 +2,6 @@ #define __PYDBG__ // -static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); // // @@ -90,25 +89,6 @@ public: } // hookgenDBG:methods -virtual void dbg_process_start(pid_t pid, thid_t tid, ea_t ea, const char * modinfo_name, ea_t modinfo_base, asize_t modinfo_size) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(modinfo_name); qnotused(modinfo_base); qnotused(modinfo_size); } -virtual void dbg_process_exit(pid_t pid, thid_t tid, ea_t ea, int exit_code) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(exit_code); } -virtual void dbg_process_attach(pid_t pid, thid_t tid, ea_t ea, const char * modinfo_name, ea_t modinfo_base, asize_t modinfo_size) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(modinfo_name); qnotused(modinfo_base); qnotused(modinfo_size); } -virtual void dbg_process_detach(pid_t pid, thid_t tid, ea_t ea) {qnotused(pid); qnotused(tid); qnotused(ea); } -virtual void dbg_thread_start(pid_t pid, thid_t tid, ea_t ea) {qnotused(pid); qnotused(tid); qnotused(ea); } -virtual void dbg_thread_exit(pid_t pid, thid_t tid, ea_t ea, int exit_code) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(exit_code); } -virtual void dbg_library_load(pid_t pid, thid_t tid, ea_t ea, const char * modinfo_name, ea_t modinfo_base, asize_t modinfo_size) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(modinfo_name); qnotused(modinfo_base); qnotused(modinfo_size); } -virtual void dbg_library_unload(pid_t pid, thid_t tid, ea_t ea, const char * info) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(info); } -virtual void dbg_information(pid_t pid, thid_t tid, ea_t ea, const char * info) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(info); } -virtual int dbg_exception(pid_t pid, thid_t tid, ea_t ea, int exc_code, bool exc_can_cont, ea_t exc_ea, const char * exc_info) {qnotused(pid); qnotused(tid); qnotused(ea); qnotused(exc_code); qnotused(exc_can_cont); qnotused(exc_ea); qnotused(exc_info); return 0;} -virtual void dbg_suspend_process() {} -virtual int dbg_bpt(thid_t tid, ea_t bptea) {qnotused(tid); qnotused(bptea); return 0;} -virtual int dbg_trace(thid_t tid, ea_t ip) {qnotused(tid); qnotused(ip); return 0;} -virtual void dbg_request_error(int failed_command, int failed_dbg_notification) {qnotused(failed_command); qnotused(failed_dbg_notification); } -virtual void dbg_step_into() {} -virtual void dbg_step_over() {} -virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {qnotused(pid); qnotused(tid); qnotused(ea); } -virtual void dbg_step_until_ret() {} -virtual void dbg_bpt_changed(int bptev_code, bpt_t * bpt) {qnotused(bptev_code); qnotused(bpt); } }; int idaapi DBG_Callback(void *ud, int notification_code, va_list va) @@ -125,151 +105,6 @@ int idaapi DBG_Callback(void *ud, int notification_code, va_list va) switch ( notification_code ) { // hookgenDBG:notifications -case dbg_process_start: -{ - const debug_event_t * event = va_arg(va, const 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: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_process_exit(event->pid, event->tid, event->ea, event->exit_code); -} -break; - -case dbg_process_attach: -{ - const debug_event_t * event = va_arg(va, const 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: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_process_detach(event->pid, event->tid, event->ea); -} -break; - -case dbg_thread_start: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_thread_start(event->pid, event->tid, event->ea); -} -break; - -case dbg_thread_exit: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_thread_exit(event->pid, event->tid, event->ea, event->exit_code); -} -break; - -case dbg_library_load: -{ - const debug_event_t * event = va_arg(va, const 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: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_library_unload(event->pid, event->tid, event->ea, event->info); -} -break; - -case dbg_information: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_information(event->pid, event->tid, event->ea, event->info); -} -break; - -case dbg_exception: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - int * warn = va_arg(va, int *); - int _tmp = proxy->dbg_exception(event->pid, event->tid, event->ea, event->exc.code, event->exc.can_cont, event->exc.ea, event->exc.info); - ret = DBG_Hooks::store_int(_tmp, event, warn); -} -break; - -case dbg_suspend_process: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - qnotused(event); - proxy->dbg_suspend_process(); -} -break; - -case dbg_bpt: -{ - thid_t tid = va_arg(va, thid_t); - ea_t bptea = va_arg(va, ea_t); - int * warn = va_arg(va, int *); - int _tmp = proxy->dbg_bpt(tid, bptea); - ret = DBG_Hooks::store_int(_tmp, tid, bptea, warn); -} -break; - -case dbg_trace: -{ - thid_t tid = va_arg(va, thid_t); - ea_t ip = va_arg(va, ea_t); - ret = proxy->dbg_trace(tid, ip); -} -break; - -case dbg_request_error: -{ - ui_notification_t failed_command = ui_notification_t(va_arg(va, int)); - dbg_notification_t failed_dbg_notification = dbg_notification_t(va_arg(va, int)); - proxy->dbg_request_error((int) failed_command, (int) failed_dbg_notification); -} -break; - -case dbg_step_into: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - qnotused(event); - proxy->dbg_step_into(); -} -break; - -case dbg_step_over: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - qnotused(event); - proxy->dbg_step_over(); -} -break; - -case dbg_run_to: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - proxy->dbg_run_to(event->pid, event->tid, event->ea); -} -break; - -case dbg_step_until_ret: -{ - const debug_event_t * event = va_arg(va, const debug_event_t *); - qnotused(event); - proxy->dbg_step_until_ret(); -} -break; - -case dbg_bpt_changed: -{ - int bptev_code = va_arg(va, int); - bpt_t * bpt = va_arg(va, bpt_t *); - proxy->dbg_bpt_changed(bptev_code, bpt); -} -break; - } } catch (Swig::DirectorException &e) @@ -338,26 +173,5 @@ static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) : answer; } -//------------------------------------------------------------------------- -static PyObject *py_get_tev_reg_mem(int tev, int reg) -{ - tev_info_t ti; - memreg_infos_t mis; - bool ok = get_tev_info(tev, &ti) - && get_insn_tev_reg_mem(tev, &mis) - && reg >= 0 && reg < mis.size(); - if ( ok ) - { - PyObject *py_str = PyString_FromStringAndSize( - (const char *) mis[reg].bytes.begin(), - mis[reg].bytes.size()); - return py_str; - } - else - { - Py_RETURN_NONE; - } -} - // #endif diff --git a/pywraps/py_dbg.py b/pywraps/py_dbg.py new file mode 100644 index 0000000..4cacdaf --- /dev/null +++ b/pywraps/py_dbg.py @@ -0,0 +1,8 @@ + +# +def get_tev_reg_mem(tev, idx): + mis = memreg_infos_t() + if get_insn_tev_reg_mem(tev, mis): + if idx < mis.size(): + return mis[idx].bytes +# diff --git a/pywraps/py_expr.hpp b/pywraps/py_expr.hpp index 71d64d9..e6bdcaa 100644 --- a/pywraps/py_expr.hpp +++ b/pywraps/py_expr.hpp @@ -44,9 +44,10 @@ static error_t py_call_idc_func( pargs.empty() ? NULL : pargs[0].o)); error_t err; - if ( PyW_GetError(errbuf, sizeof(errbuf)) ) + qstring qerrbuf; + if ( PyW_GetError(&qerrbuf) ) { - err = PyW_CreateIdcException(r, errbuf); + err = PyW_CreateIdcException(r, qerrbuf.c_str()); } else { diff --git a/pywraps/py_expr.py b/pywraps/py_expr.py index aefdd13..c5838bb 100644 --- a/pywraps/py_expr.py +++ b/pywraps/py_expr.py @@ -1,28 +1,3 @@ -# -------------------------------------------------------------------------- -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: @@ -34,13 +9,13 @@ try: # 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()) + call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_ida_expr.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) + _IDCFUNC_CB_T = CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) except: _IDCFUNC_CB_T = None @@ -107,7 +82,7 @@ def set_idc_func_ex(name, fp=None, args=(), flags=0): del __IDC_FUNC_CTXS[name] # Delete the context and unregister the function - return _idaapi.pyw_unregister_idc_func(f.ctxptr) + return _ida_expr.pyw_unregister_idc_func(f.ctxptr) # Registering a function that is already registered? if f is not None: @@ -118,7 +93,7 @@ def set_idc_func_ex(name, fp=None, args=(), flags=0): args = "".join([chr(x) for x in args]) # Create a context - ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) + ctxptr = _ida_expr.pyw_register_idc_func(name, args, fp) if ctxptr == 0: return False @@ -129,7 +104,7 @@ def set_idc_func_ex(name, fp=None, args=(), flags=0): __IDC_FUNC_CTXS[name] = f # Register IDC function with a callback - return _idaapi.py_set_idc_func_ex( + return _ida_expr.py_set_idc_func_ex( name, f.fp_ptr, args, diff --git a/pywraps/py_frame.hpp b/pywraps/py_frame.hpp new file mode 100644 index 0000000..ba3ad67 --- /dev/null +++ b/pywraps/py_frame.hpp @@ -0,0 +1,59 @@ +// +//------------------------------------------------------------------------- +/* +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; +} + +//------------------------------------------------------------------------- +/* +# +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)); +} +// diff --git a/pywraps/py_gdl.py b/pywraps/py_gdl.py index 32604ec..f503ef9 100755 --- a/pywraps/py_gdl.py +++ b/pywraps/py_gdl.py @@ -1,4 +1,6 @@ # +import _ida_idaapi +import types # ----------------------------------------------------------------------- class BasicBlock(object): """Basic block class. It is returned by the Flowchart class""" @@ -52,7 +54,7 @@ class FlowChart(object): raise Exception("Please specifiy either a function or start/end pair") if bounds is None: - bounds = (BADADDR, BADADDR) + bounds = (_ida_idaapi.BADADDR, _ida_idaapi.BADADDR) # Create the flowchart self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) @@ -61,7 +63,7 @@ class FlowChart(object): """Number of blocks in the flow chart""" - def refresh(): + def refresh(self): """Refreshes the flow chart""" self._q.refresh() diff --git a/pywraps/py_graph.hpp b/pywraps/py_graph.hpp index c608bc3..43d3b9b 100644 --- a/pywraps/py_graph.hpp +++ b/pywraps/py_graph.hpp @@ -7,7 +7,7 @@ class py_graph_t : public py_customidamemo_t typedef py_customidamemo_t inherited; protected: - void collect_class_callbacks_ids(callbacks_ids_t *out); + void collect_class_callbacks_ids(pycim_callbacks_ids_t *out); private: enum @@ -74,7 +74,7 @@ private: void clear(py_graph_t *pyg) { iterator e = end(); - for ( iterator it=begin(); it!=end(); ) + for ( iterator it=begin(); it != end(); ) { if ( it->second == pyg ) { @@ -104,7 +104,7 @@ private: // 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)); + QASSERT(30453, pycim_lookup_info.find_by_py_view(NULL, NULL, (py_graph_t *) obj)); PYW_GIL_GET; return ((py_graph_t *)obj)->gr_callback(code, va); } @@ -288,7 +288,7 @@ private: void show() { TForm *form; - if ( lookup_info.find_by_py_view(&form, NULL, this) ) + if ( pycim_lookup_info.find_by_py_view(&form, NULL, this) ) open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); } @@ -330,7 +330,7 @@ private: TForm *form = create_tform(title, &hwnd); if ( hwnd != NULL ) // Created new tform { - lookup_info_t::entry_t &e = lookup_info.new_entry(this); + lookup_entry_t &e = pycim_lookup_info.new_entry(this); // get a unique graph id netnode id; char grnode[MAXSTR]; @@ -345,7 +345,7 @@ private: viewer_fit_window(pview); bind(self, pview); install_custom_viewer_handlers(); - lookup_info.commit(e, form, view); + pycim_lookup_info.commit(e, form, view); } else { @@ -387,7 +387,7 @@ public: return; py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + if ( _this == NULL || !pycim_lookup_info.find_by_py_view(NULL, NULL, _this) ) return; _this->jump_to_node(nid); @@ -396,7 +396,7 @@ public: 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) ) + if ( _this == NULL || !pycim_lookup_info.find_by_py_view(NULL, NULL, _this) ) return 0; return _this->add_command(title, hotkey); @@ -406,7 +406,7 @@ public: { TForm *form; py_graph_t *_this = view_extract_this(self); - if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) + if ( _this == NULL || !pycim_lookup_info.find_by_py_view(&form, NULL, _this) ) return; close_tform(form, FORM_CLOSE_LATER); } @@ -429,7 +429,7 @@ public: 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); + pycim_lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); if ( py_graph == NULL ) { @@ -456,7 +456,7 @@ public: }; //------------------------------------------------------------------------- -void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) +void py_graph_t::collect_class_callbacks_ids(pycim_callbacks_ids_t *out) { inherited::collect_class_callbacks_ids(out); out->add(S_ON_REFRESH, 0); @@ -507,7 +507,7 @@ void py_graph_t::on_user_refresh(mutable_graph_t *g) node_cache.clear(); // Get the edges - for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) + 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)); @@ -517,7 +517,7 @@ void py_graph_t::on_user_refresh(mutable_graph_t *g) // Get and validate each of the two elements in the sequence int edge_ids[2]; int j; - for ( j=0; j -class GraphViewer(CustomIDAMemo): +import ida_idaapi +class GraphViewer(ida_idaapi.CustomIDAMemo): """This class wraps the user graphing facility provided by the graph.hpp file""" def __init__(self, title, close_open = False): """ @@ -50,7 +51,7 @@ class GraphViewer(CustomIDAMemo): Closes the graph. It is possible to call Show() again (which will recreate the graph) """ - _idaapi.pyg_close(self) + _ida_graph.pyg_close(self) def Show(self): """ @@ -59,14 +60,14 @@ class GraphViewer(CustomIDAMemo): @return: Boolean """ if self._close_open: - frm = _idaapi.find_tform(self._title) + frm = _ida_kernwin.find_tform(self._title) if frm: - _idaapi.close_tform(frm, 0) - return _idaapi.pyg_show(self) + _ida_kernwin.close_tform(frm, 0) + return _ida_graph.pyg_show(self) def Select(self, node_id): """Selects a node on the graph""" - _idaapi.pyg_select_node(self, node_id) + _ida_graph.pyg_select_node(self, node_id) def AddCommand(self, title, hotkey): """ @@ -74,7 +75,7 @@ class GraphViewer(CustomIDAMemo): - register_action() - attach_action_to_popup() """ - return _idaapi.pyg_add_command(self, title, hotkey) + return _ida_graph.pyg_add_command(self, title, hotkey) def OnRefresh(self): """ diff --git a/pywraps/py_hexrays.hpp b/pywraps/py_hexrays.hpp new file mode 100644 index 0000000..719c018 --- /dev/null +++ b/pywraps/py_hexrays.hpp @@ -0,0 +1,439 @@ +//------------------------------------------------------------------------- +// +#ifdef WITH_HEXRAYS +hexdsp_t *hexdsp = NULL; +#endif // WITH_HEXRAYS + +//------------------------------------------------------------------------- +qstring_printer_t *new_qstring_printer_t(const cfunc_t *f, bool tags) +{ + return new qstring_printer_t(f, * (new qstring()), tags); +} + +//------------------------------------------------------------------------- +void delete_qstring_printer_t(qstring_printer_t *qs) +{ + delete &(qs->s); + delete qs; +} + +//--------------------------------------------------------------------- +static int hexrays_python_call(ref_t fct, ref_t args) +{ + PYW_GIL_GET; + + newref_t resultobj(PyEval_CallObject(fct.o, args.o)); + if ( PyErr_Occurred() ) + { + PyErr_Print(); + return 0; + } + + int result; + if ( SWIG_IsOK(SWIG_AsVal_int(resultobj.o, &result)) ) + return result; + msg("IDAPython: Hex-rays python callback returned non-integer; value ignored.\n"); + return 0; +} + +//--------------------------------------------------------------------- +static bool idaapi __python_custom_viewer_popup_item_callback(void *ud) +{ + PYW_GIL_GET; + + int ret; + borref_t fct((PyObject *)ud); + newref_t nil(NULL); + ret = hexrays_python_call(fct, nil); + return ret ? true : false; +} + +//--------------------------------------------------------------------- +static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_list va) +{ + PYW_GIL_GET; + + int ret; + borref_t fct((PyObject *)ud); + switch ( event ) + { + case hxe_maturity: + ///< Ctree maturity level is being changed. + ///< cfunc_t *cfunc + ///< ctree_maturity_t new_maturity + { + cfunc_t *arg0 = va_arg(va, cfunc_t *); + ctree_maturity_t arg1 = va_argi(va, ctree_maturity_t); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); + newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_interr: + ///< Internal error has occurred. + ///< int errcode + { + int arg0 = va_argi(va, int); + newref_t args(Py_BuildValue("(ii)", event, arg0)); + ret = hexrays_python_call(fct, args); + } + break; + + case hxe_print_func: + ///< Printing ctree and generating text. + ///< cfunc_t *cfunc + ///< vc_printer_t *vp + ///< Returns: 1 if text has been generated by the plugin + { + cfunc_t *arg0 = va_arg(va, cfunc_t *); + vc_printer_t *arg1 = va_arg(va, vc_printer_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); + newref_t arg1obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, 0 )); + newref_t args(Py_BuildValue("(iOO)", event, arg0obj.o, arg1obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + + case hxe_func_printed: + ///< Function text has been generated. Plugins may + ///< modify the text in \ref sv. + ///< cfunc_t *cfunc + { + cfunc_t *arg0 = va_arg(va, cfunc_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + + // User interface related events: + case hxe_open_pseudocode: + ///< New pseudocode view has been opened. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_switch_pseudocode: + ///< Existing pseudocode view has been reloaded + ///< with a new function. Its text has not been + ///< refreshed yet, only cfunc and mba pointers are ready. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_refresh_pseudocode: + ///< Existing pseudocode text has been refreshed. + ///< vdui_t *vu + ///< See also hxe_text_ready, which happens earlier + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_close_pseudocode: + ///< Pseudocode view is being closed. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_keyboard: + ///< Keyboard has been hit. + ///< vdui_t *vu + ///< int key_code (VK_...) + ///< int shift_state + ///< Should return: 1 if the event has been handled + { + vdui_t *arg0 = va_arg(va, vdui_t *); + int arg1 = va_argi(va, int); + int arg2 = va_argi(va, int); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iOii)", event, arg0obj.o, arg1, arg2)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_right_click: + ///< Mouse right click. We can add menu items now. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_double_click: + ///< Mouse double click. + ///< vdui_t *vu + ///< int shift_state + ///< Should return: 1 if the event has been handled + { + vdui_t *arg0 = va_arg(va, vdui_t *); + int arg1 = va_argi(va, int); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_curpos: + ///< Current cursor position has been changed. + ///< (for example, by left-clicking or using keyboard) + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_create_hint: + ///< Create a hint for the current item. + ///< vdui_t *vu + ///< qstring *result_hint + ///< int *implines + ///< Possible return values: + ///< 0: the event has not been handled + ///< 1: hint has been created (should set *implines to nonzero as well) + ///< 2: hint has been created but the standard hints must be + ///< appended by the decompiler + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_text_ready: + ///< Decompiled text is ready. + ///< vdui_t *vu + ///< This event can be used to modify the output text (sv). + ///< The text uses regular color codes (see lines.hpp) + ///< COLOR_ADDR is used to store pointers to ctree elements + { + vdui_t *arg0 = va_arg(va, vdui_t *); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); + ret = hexrays_python_call(fct, args); + } + break; + case hxe_populating_popup: + ///< Populating popup menu. We can add menu items now. + ///< TForm *form + ///< TPopupMenu *popup_handle + ///< vdui_t *vu + { + TForm *form = va_arg(va, TForm *); + TPopupMenu *pp = va_arg(va, TPopupMenu*); + vdui_t *vdui = va_arg(va, vdui_t *); + newref_t py_form(SWIG_NewPointerObj(SWIG_as_voidptr(form), SWIGTYPE_p_Forms__TForm, 0)); + newref_t py_popup(SWIG_NewPointerObj(SWIG_as_voidptr(pp), SWIGTYPE_p_Menus__TPopupMenu, 0)); + newref_t py_vdui(SWIG_NewPointerObj(SWIG_as_voidptr(vdui), SWIGTYPE_p_vdui_t, 0 )); + newref_t py_args(Py_BuildValue("(iOOO)", event, py_form.o, py_popup.o, py_vdui.o)); + ret = hexrays_python_call(fct, py_args); + } + break; + default: + //~ msg("IDAPython: Unknown event `%u' occured\n", event); + ret = 0; + break; + } + + return ret; +} + +//------------------------------------------------------------------------- +// A set of cfuncptr_t objects that were created from IDAPython. +// This is necessary in order to delete those objects before the hexrays +// plugin is unloaded. Otherwise, IDAPython will still delete them, but +// the plugin's 'hexdsp' dispatcher function will point to dlclose()'d +// code. +static qvector python_cfuncptrs; +void hexrays_clear_python_cfuncptr_t_references(void) +{ + for ( size_t i = 0, n = python_cfuncptrs.size(); i < n; ++i ) + python_cfuncptrs[i]->reset(); + // NOTE: Don't clear() the array of pointers. All the python-exposed + // cfuncptr_t instances will be deleted through the python + // shutdown/ref-decrementing process anyway, and the entries will be + // properly pulled out of the vector when that happens. +} + +//------------------------------------------------------------------------- +void hexrays_register_python_cfuncptr_t_instance(cfuncptr_t *fp) +{ + QASSERT(30457, !python_cfuncptrs.has(fp)); + python_cfuncptrs.push_back(fp); +} + +//------------------------------------------------------------------------- +void hexrays_deregister_python_cfuncptr_t_instance(cfuncptr_t *fp) +{ + qvector::iterator found = python_cfuncptrs.find(fp); + if ( found != python_cfuncptrs.end() ) + { + fp->reset(); + python_cfuncptrs.erase(found); + } +} + +//------------------------------------------------------------------------- +cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf) +{ + try + { + cfuncptr_t cfunc = decompile(pfn, hf); + return cfunc; + } + catch(...) + { + error("Hex-Rays Python: decompiler threw an exception.\n"); + } + return cfuncptr_t(0); +} + +//------------------------------------------------------------------------- +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; +} + +//------------------------------------------------------------------------- +static void try_init() +{ + init_hexrays_plugin(0); + if ( hexdsp != NULL ) + msg("IDAPython Hex-Rays bindings initialized.\n"); +} + +//------------------------------------------------------------------------- +static int idaapi on_ui_notification(void *, int code, va_list va) +{ + switch ( code ) + { + case ui_plugin_loaded: + if ( hexdsp == NULL && is_hexrays_plugin(va_arg(va, plugin_info_t *)) ) + try_init(); + 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; + } + return 0; +} +// + + +// +//--------------------------------------------------------------------- +extern hexdsp_t *hexdsp; +bool py_init_hexrays_plugin(int flags=0) +{ + // Only initialize one time + if ( hexdsp == NULL ) + return init_hexrays_plugin(flags); + else + return true; +} + +//--------------------------------------------------------------------- +void py_add_custom_viewer_popup_item( + TCustomControl *custom_viewer, + const char *title, + const char *hotkey, + PyObject *custom_viewer_popup_item_callback) +{ + PYW_GIL_GET; + Py_INCREF(custom_viewer_popup_item_callback); + add_custom_viewer_popup_item(custom_viewer, title, hotkey, __python_custom_viewer_popup_item_callback, custom_viewer_popup_item_callback); +}; + +//--------------------------------------------------------------------- +bool py_install_hexrays_callback(PyObject *hx_cblist_callback) +{ + PYW_GIL_GET; + if ( install_hexrays_callback(__hexrays_python_callback, hx_cblist_callback) ) + { + Py_INCREF(hx_cblist_callback); + return true; + } + return false; +} + +//--------------------------------------------------------------------- +int py_remove_hexrays_callback(PyObject *hx_cblist_callback) +{ + PYW_GIL_GET; + int result, i; + result = remove_hexrays_callback(__hexrays_python_callback, hx_cblist_callback); + for ( i = 0; i < result; i++ ) + Py_DECREF(hx_cblist_callback); + + return result; +} + +cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf); + +//------------------------------------------------------------------------- +bool py_decompile_many(const char *outfile, PyObject *funcaddrs, int flags) +{ + eavec_t leas, *eas = NULL; + if ( funcaddrs != Py_None ) + { + if ( !PyW_PyListToEaVec(funcaddrs, leas) ) + return false; + eas = &leas; + } + return decompile_many(outfile, eas, flags); +} + +//------------------------------------------------------------------------- +// Some examples will want to use action_handler_t's whose update() method +// calls get_tform_vdui() to figure out whether the action should be enabled +// for the current form. Unfortunately, if hexrays is first unloaded before +// the widget cleanup is performed (e.g., while loading another IDB), +// the action would crash. Ideally we should wrap all toplevel calls +// with such wrappers, but it doesn't seem to be really necessary at the +// moment: only corner-cases will reveal this issue (reported by +// the idapython_hr-decompile test.) +vdui_t *py_get_tform_vdui(TForm *f) +{ + return hexdsp != NULL ? get_tform_vdui(f) : NULL; +} +// + +// +try_init(); +hook_to_notification_point(HT_UI, on_ui_notification, NULL); +// diff --git a/pywraps/py_hexrays.py b/pywraps/py_hexrays.py new file mode 100644 index 0000000..3f32205 --- /dev/null +++ b/pywraps/py_hexrays.py @@ -0,0 +1,419 @@ +# ----------------------------------------------------------------------- +# +import ida_funcs + +hexrays_failure_t.__str__ = lambda self: str(self.str) + +# --------------------------------------------------------------------- +class DecompilationFailure(Exception): + """ Raised on a decompilation error. + + The associated hexrays_failure_t object is stored in the + 'info' member of this exception. """ + + def __init__(self, info): + Exception.__init__(self, 'Decompilation failed: %s' % (str(info), )) + self.info = info + return + +# --------------------------------------------------------------------- +def decompile(ea, hf=None): + if isinstance(ea, (int, long)): + func = ida_funcs.get_func(ea) + if not func: return + elif type(ea) == ida_funcs.func_t: + func = ea + else: + raise RuntimeError('arg 1 of decompile expects either ea_t or cfunc_t argument') + + if hf is None: + hf = hexrays_failure_t() + + ptr = _decompile(func, hf) + + if ptr.__deref__() is None: + raise DecompilationFailure(hf) + + return ptr + +# --------------------------------------------------------------------- +# stringify all string types +#qtype.__str__ = qtype.c_str +#qstring.__str__ = qstring.c_str +#citem_cmt_t.__str__ = citem_cmt_t.c_str + +# --------------------------------------------------------------------- +# listify all list types +import ida_idaapi +ida_idaapi._listify_types( + cinsnptrvec_t, + ctree_items_t, + qvector_lvar_t, + qvector_carg_t, + qvector_ccase_t, + hexwarns_t, + history_t, + lvar_saved_infos_t) + +def citem_to_specific_type(self): + """ cast the citem_t object to its more specific type, either cexpr_t or cinsn_t. """ + + if self.op >= cot_empty and self.op <= cot_last: + return self.cexpr + elif self.op >= cit_empty and self.op < cit_end: + return self.cinsn + + raise RuntimeError('unknown op type %s' % (repr(self.op), )) +citem_t.to_specific_type = property(citem_to_specific_type) + +""" array used for translating cinsn_t->op type to their names. """ +cinsn_t.op_to_typename = {} +for k in dir(_ida_hexrays): + if k.startswith('cit_'): + cinsn_t.op_to_typename[getattr(_ida_hexrays, k)] = k[4:] + +""" array used for translating cexpr_t->op type to their names. """ +cexpr_t.op_to_typename = {} +for k in dir(_ida_hexrays): + if k.startswith('cot_'): + cexpr_t.op_to_typename[getattr(_ida_hexrays, k)] = k[4:] + +def property_op_to_typename(self): + return self.op_to_typename[self.op] +cinsn_t.opname = property(property_op_to_typename) +cexpr_t.opname = property(property_op_to_typename) + +def cexpr_operands(self): + """ return a dictionary with the operands of a cexpr_t. """ + + if self.op >= cot_comma and self.op <= cot_asgumod or \ + self.op >= cot_lor and self.op <= cot_fdiv or \ + self.op == cot_idx: + return {'x': self.x, 'y': self.y} + + elif self.op == cot_tern: + return {'x': self.x, 'y': self.y, 'z': self.z} + + elif self.op in [cot_fneg, cot_neg, cot_sizeof] or \ + self.op >= cot_lnot and self.op <= cot_predec: + return {'x': self.x} + + elif self.op == cot_cast: + return {'type': self.type, 'x': self.x} + + elif self.op == cot_call: + return {'x': self.x, 'a': self.a} + + elif self.op in [cot_memref, cot_memptr]: + return {'x': self.x, 'm': self.m} + + elif self.op == cot_num: + return {'n': self.n} + + elif self.op == cot_fnum: + return {'fpc': self.fpc} + + elif self.op == cot_str: + return {'string': self.string} + + elif self.op == cot_obj: + return {'obj_ea': self.obj_ea} + + elif self.op == cot_var: + return {'v': self.v} + + elif self.op == cot_helper: + return {'helper': self.helper} + + raise RuntimeError('unknown op type %s' % self.opname) +cexpr_t.operands = property(cexpr_operands) + +def cinsn_details(self): + """ return the details pointer for the cinsn_t object depending on the value of its op member. \ + this is one of the cblock_t, cif_t, etc. objects. """ + + if self.op not in self.op_to_typename: + raise RuntimeError('unknown item->op type') + + opname = self.opname + if opname == 'empty': + return self + + if opname in ['break', 'continue']: + return None + + return getattr(self, 'c' + opname) +cinsn_t.details = property(cinsn_details) + +def cblock_iter(self): + + iter = self.begin() + for i in range(self.size()): + yield iter.cur + iter.next() + + return +cblock_t.__iter__ = cblock_iter +cblock_t.__len__ = cblock_t.size + +# cblock.find(cinsn_t) -> returns the iterator positioned at the given item +def cblock_find(self, item): + + iter = self.begin() + for i in range(self.size()): + if iter.cur == item: + return iter + iter.next() + + return +cblock_t.find = cblock_find + +# cblock.index(cinsn_t) -> returns the index of the given item +def cblock_index(self, item): + + iter = self.begin() + for i in range(self.size()): + if iter.cur == item: + return i + iter.next() + + return +cblock_t.index = cblock_index + +# cblock.at(int) -> returns the item at the given index index +def cblock_at(self, index): + + iter = self.begin() + for i in range(self.size()): + if i == index: + return iter.cur + iter.next() + + return +cblock_t.at = cblock_at + +# cblock.remove(cinsn_t) +def cblock_remove(self, item): + + iter = self.find(item) + self.erase(iter) + + return +cblock_t.remove = cblock_remove + +# cblock.insert(index, cinsn_t) +def cblock_insert(self, index, item): + + pos = self.at(index) + iter = self.find(pos) + self.insert(iter, item) + + return +cblock_t.insert = cblock_insert + +cfuncptr_t.__str__ = lambda self: str(self.__deref__()) + +import ida_typeinf +def cfunc_type(self): + """ Get the function's return type tinfo_t object. """ + tif = ida_typeinf.tinfo_t() + result = self.get_func_type(tif) + if not result: + return + return tif +cfunc_t.type = property(cfunc_type) +cfuncptr_t.type = property(lambda self: self.__deref__().type) + +cfunc_t.arguments = property(lambda self: [o for o in self.lvars if o.is_arg_var]) +cfuncptr_t.arguments = property(lambda self: self.__deref__().arguments) + +cfunc_t.lvars = property(cfunc_t.get_lvars) +cfuncptr_t.lvars = property(lambda self: self.__deref__().lvars) +cfunc_t.warnings = property(cfunc_t.get_warnings) +cfuncptr_t.warnings = property(lambda self: self.__deref__().warnings) +cfunc_t.pseudocode = property(cfunc_t.get_pseudocode) +cfuncptr_t.pseudocode = property(lambda self: self.__deref__().get_pseudocode()) +cfunc_t.eamap = property(cfunc_t.get_eamap) +cfuncptr_t.eamap = property(lambda self: self.__deref__().get_eamap()) +cfunc_t.boundaries = property(cfunc_t.get_boundaries) +cfuncptr_t.boundaries = property(lambda self: self.__deref__().get_boundaries()) + +#pragma SWIG nowarn=+503 + +lvar_t.used = property(lvar_t.used) +lvar_t.typed = property(lvar_t.typed) +lvar_t.mreg_done = property(lvar_t.mreg_done) +lvar_t.has_nice_name = property(lvar_t.has_nice_name) +lvar_t.is_unknown_width = property(lvar_t.is_unknown_width) +lvar_t.has_user_info = property(lvar_t.has_user_info) +lvar_t.has_user_name = property(lvar_t.has_user_name) +lvar_t.has_user_type = property(lvar_t.has_user_type) +lvar_t.is_result_var = property(lvar_t.is_result_var) +lvar_t.is_arg_var = property(lvar_t.is_arg_var) +lvar_t.is_fake_var = property(lvar_t.is_fake_var) +lvar_t.is_overlapped_var = property(lvar_t.is_overlapped_var) +lvar_t.is_floating_var = property(lvar_t.is_floating_var) +lvar_t.is_spoiled_var = property(lvar_t.is_spoiled_var) +lvar_t.is_mapdst_var = property(lvar_t.is_mapdst_var) + +# dictify all dict-like types + +def _map___getitem__(self, key): + """ Returns the value associated with the provided key. """ + if not isinstance(key, self.keytype): + raise KeyError('type of key should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key not in self: + raise KeyError('key not found') + return self.second(self.find(key)) + +def _map___setitem__(self, key, value): + """ Returns the value associated with the provided key. """ + if not isinstance(key, self.keytype): + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if not isinstance(value, self.valuetype): + raise KeyError('type of `value` should be ' + repr(self.valuetype) + ' but got ' + type(value)) + self.insert(key, value) + return + +def _map___delitem__(self, key): + """ Removes the value associated with the provided key. """ + if not isinstance(key, self.keytype): + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key not in self: + raise KeyError('key not found') + self.erase(self.find(key)) + return + +def _map___contains__(self, key): + """ Returns true if the specified key exists in the . """ + if not isinstance(key, self.keytype): + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if self.find(key) != self.end(): + return True + return False + +def _map_clear(self): + self.clear() + return + +def _map_copy(self): + ret = {} + for k in self.iterkeys(): + ret[k] = self[k] + return ret + +def _map_get(self, key, default=None): + if key in self: + return self[key] + return default + +def _map_iterkeys(self): + iter = self.begin() + while iter != self.end(): + yield self.first(iter) + iter = self.next(iter) + return + +def _map_itervalues(self): + iter = self.begin() + while iter != self.end(): + yield self.second(iter) + iter = self.next(iter) + return + +def _map_iteritems(self): + iter = self.begin() + while iter != self.end(): + yield (self.first(iter), self.second(iter)) + iter = self.next(iter) + return + +def _map_keys(self): + return list(self.iterkeys()) + +def _map_values(self): + return list(self.itervalues()) + +def _map_items(self): + return list(self.iteritems()) + +def _map_has_key(self, key): + return key in self + +def _map_pop(self, key): + """ Sets the value associated with the provided key. """ + if not isinstance(key, self.keytype): + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key not in self: + raise KeyError('key not found') + ret = self[key] + del self[key] + return ret + +def _map_popitem(self): + """ Sets the value associated with the provided key. """ + if len(self) == 0: + raise KeyError('key not found') + key = self.keys()[0] + return (key, self.pop(key)) + +def _map_setdefault(self, key, default=None): + """ Sets the value associated with the provided key. """ + if not isinstance(key, self.keytype): + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key in self: + return self[key] + self[key] = default + return default + +def _map_as_dict(maptype, name, keytype, valuetype): + + maptype.keytype = keytype + maptype.valuetype = valuetype + + for fctname in ['begin', 'end', 'first', 'second', 'next', \ + 'find', 'insert', 'erase', 'clear', 'size']: + fct = getattr(_ida_hexrays, name + '_' + fctname) + setattr(maptype, '__' + fctname, fct) + + maptype.__len__ = maptype.size + maptype.__getitem__ = maptype.at + + maptype.begin = lambda self, *args: self.__begin(self, *args) + maptype.end = lambda self, *args: self.__end(self, *args) + maptype.first = lambda self, *args: self.__first(*args) + maptype.second = lambda self, *args: self.__second(*args) + maptype.next = lambda self, *args: self.__next(*args) + maptype.find = lambda self, *args: self.__find(self, *args) + maptype.insert = lambda self, *args: self.__insert(self, *args) + maptype.erase = lambda self, *args: self.__erase(self, *args) + maptype.clear = lambda self, *args: self.__clear(self, *args) + maptype.size = lambda self, *args: self.__size(self, *args) + maptype.__getitem__ = _map___getitem__ + maptype.__setitem__ = _map___setitem__ + maptype.__delitem__ = _map___delitem__ + maptype.__contains__ = _map___contains__ + maptype.clear = _map_clear + maptype.copy = _map_copy + maptype.get = _map_get + maptype.iterkeys = _map_iterkeys + maptype.itervalues = _map_itervalues + maptype.iteritems = _map_iteritems + maptype.keys = _map_keys + maptype.values = _map_values + maptype.items = _map_items + maptype.has_key = _map_has_key + maptype.pop = _map_pop + maptype.popitem = _map_popitem + maptype.setdefault = _map_setdefault + +#_map_as_dict(user_labels_t, 'user_labels', (int, long), qstring) +_map_as_dict(user_cmts_t, 'user_cmts', treeloc_t, citem_cmt_t) +_map_as_dict(user_numforms_t, 'user_numforms', operand_locator_t, number_format_t) +_map_as_dict(user_iflags_t, 'user_iflags', citem_locator_t, (int, long)) +import ida_pro +_map_as_dict(user_unions_t, 'user_unions', (int, long), ida_pro.intvec_t) +_map_as_dict(eamap_t, 'eamap', long, cinsnptrvec_t) +#_map_as_dict(boundaries_t, 'boundaries', cinsn_t, areaset_t) + +# diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp index f3cf3fa..6ac3971 100644 --- a/pywraps/py_idaapi.hpp +++ b/pywraps/py_idaapi.hpp @@ -2,1639 +2,122 @@ #define __PY_IDAAPI__ // - -//------------------------------------------------------------------------ -// 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 ) +#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 - 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 linked class instances -ref_t create_idaapi_linked_class_instance( - const char *clsname, - void *lnk) +//------------------------------------------------------------------------- +void pygc_refresh(PyObject *self) { - 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; + CHK_THIS(); + _this->refresh(); } //------------------------------------------------------------------------- -// 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) +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) { - 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; + CHK_THIS(); + _this->set_node_info(py_node_idx, py_node_info, py_flags); } //------------------------------------------------------------------------- -// Parses a Python object as a long or long long -bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) +void pygc_set_nodes_infos(PyObject *self, PyObject *values) { - 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 + CHK_THIS(); + _this->set_nodes_infos(values); } //------------------------------------------------------------------------- -// Checks if a given object is of sequence type -bool PyW_IsSequenceType(PyObject *obj) +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx) { - 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); + GET_THIS(); + if ( _this != NULL ) + return _this->get_node_info(py_node_idx); else - out->qclear(); - return ok; + Py_RETURN_NONE; } -//-------------------------------------------------------------------------- -// Checks if a Python error occured and fills the out parameter with the -// exception string -bool PyW_GetError(qstring *out, bool clear_err) +//------------------------------------------------------------------------- +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes) { - PYW_GIL_CHECK_LOCKED_SCOPE(); + CHK_THIS(); + _this->del_nodes_infos(py_nodes); +} - 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!"; - } +//------------------------------------------------------------------------- +PyObject *pygc_get_current_renderer_type(PyObject *self) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_current_renderer_type(); 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; + Py_RETURN_NONE; } //------------------------------------------------------------------------- -bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt) { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - qstring s; - if ( !PyW_GetError(&s, clear_err) ) - return false; - - qstrncpy(buf, s.c_str(), bufsz); - return true; + CHK_THIS(); + _this->set_current_renderer_type(py_rt); } //------------------------------------------------------------------------- -// 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) +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos) { - PYW_GIL_CHECK_LOCKED_SCOPE(); - - static qstring err_str; - if ( !PyW_GetError(&err_str) ) - return false; - - msg("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); - return true; + CHK_THIS_OR_NONE(); + return _this->create_groups(groups_infos); } -//--------------------------------------------------------------------------- -void *pyobj_get_clink(PyObject *pyobj) +//------------------------------------------------------------------------- +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current) { - 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; + CHK_THIS_OR_NONE(); + return _this->delete_groups(groups, new_current); } -//------------------------------------------------------------------------ -class pywraps_notify_when_t +//------------------------------------------------------------------------- +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current) { - 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) - { - pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; - switch ( event_id ) - { - case processor_t::newfile: - case processor_t::oldfile: - { - // This hook gets called from the kernel. Ensure we hold the GIL. - // Note that PYW_GIL_GET appears in each case of the switch, which is to - // ensure that the GIL is retrieved ONLY when we need it. If PYW_GIL_GET - // appears outside the switch, it will be executed each time this callback - // is called, which results in a huge slowdown (at least on mac). - PYW_GIL_GET; - 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: - { - PYW_GIL_GET; - _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() ) - { - notify_when_args_vec_t::iterator it, it_end; - for ( 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; + CHK_THIS_OR_NONE(); + return _this->set_groups_visibility(groups, expand, new_current); } -//------------------------------------------------------------------------ -bool pywraps_nw_notify(int slot, ...) +//------------------------------------------------------------------------- +TForm *pycim_get_tform(PyObject *self) { - 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; + CHK_THIS_OR_NULL(); + TForm *form = NULL; + if ( !pycim_lookup_info.find_by_py_view(&form, NULL, _this) ) + return NULL; + return form; } -//------------------------------------------------------------------------ -// Deinitializes the notify_when mechanism -bool pywraps_nw_term() +//------------------------------------------------------------------------- +TCustomControl *pycim_get_tcustom_control(PyObject *self) { - 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; + CHK_THIS_OR_NULL(); + TCustomControl *tcc = NULL; + if ( !pycim_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 // @@ -1694,7 +177,7 @@ def set_script_timeout(timeout): pass # */ -int set_script_timeout(int timeout); +idaman int ida_export set_script_timeout(int timeout); /* # @@ -1708,7 +191,7 @@ def disable_script_timeout(): pass # */ -void disable_script_timeout(); +idaman void ida_export disable_script_timeout(); /* # @@ -1721,8 +204,8 @@ def enable_extlang_python(enable): pass # */ -void enable_extlang_python(bool enable); -void enable_python_cli(bool enable); +idaman void ida_export enable_extlang_python(bool enable); +idaman void ida_export enable_python_cli(bool enable); /* # @@ -1737,111 +220,6 @@ def RunPythonStatement(stmt): # */ -//--------------------------------------------------------------------------- -// 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(size_t(&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; -} - //------------------------------------------------------------------------ /* # @@ -1862,10 +240,21 @@ def notify_when(when, callback): 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); + return PyCallable_Check(py_callable) && add_notify_when(when, py_callable); } + +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); // #endif diff --git a/pywraps/py_idaapi.py b/pywraps/py_idaapi.py index a500a04..789c8d3 100755 --- a/pywraps/py_idaapi.py +++ b/pywraps/py_idaapi.py @@ -5,7 +5,7 @@ try: except: pywraps_there = False -import _idaapi +import _ida_idaapi import random import operator import datetime @@ -241,82 +241,6 @@ class PyIdc_cvt_int64__(pyidc_cvt_helper__): 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""" @@ -344,7 +268,8 @@ def as_cstr(val): 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")) + import _ida_ida + return unicode(s).encode("UTF-16" + ("BE" if _ida_ida.cvar.inf.mf else "LE")) # ----------------------------------------------------------------------- def as_uint32(v): @@ -619,4 +544,127 @@ NW_TERMIDA = 0x0008 NW_REMOVE = 0x0010 """Use this flag with other flags to uninstall a notifywhen callback""" +# ----------------------------------------------------------------------- +# CustomIDAMemo +# ----------------------------------------------------------------------- +class CustomIDAMemo(object): + def Refresh(self): + """ + Refreshes the graph. This causes the OnRefresh() to be called + """ + ida_idaapi.pygc_refresh(self) + + def GetCurrentRendererType(self): + return ida_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. + """ + ida_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. + """ + ida_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. + """ + ida_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 ida_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 ida_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 ida_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 ida_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 ida_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 ida_idaapi.pycim_get_tform(self) + + def GetTCustomControl(self): + """ + Return the TCustomControl underlying this view. + + @return: The TCustomControl underlying this view, or None. + """ + return ida_idaapi.pycim_get_tcustom_control(self) + # diff --git a/pywraps/py_linput.hpp b/pywraps/py_idaapi_loader_input.hpp similarity index 98% rename from pywraps/py_linput.hpp rename to pywraps/py_idaapi_loader_input.hpp index 3144878..de0e5b0 100644 --- a/pywraps/py_linput.hpp +++ b/pywraps/py_idaapi_loader_input.hpp @@ -1,8 +1,8 @@ -#ifndef __PY_IDA_LINPUT__ -#define __PY_IDA_LINPUT__ +#ifndef __PY_IDAAPI_LOADER_INPUT__ +#define __PY_IDAAPI_LOADER_INPUT__ //-------------------------------------------------------------------------- -// +// /* # class loader_input_t(pyidc_opaque_object_t): @@ -412,6 +412,6 @@ public: return Py_BuildValue("c", ch); } }; -// +// -#endif +#endif // __PY_IDAAPI_LOADER_INPUT__ diff --git a/pywraps/py_idbhooks.hpp b/pywraps/py_idbhooks.hpp deleted file mode 100644 index 8ae422b..0000000 --- a/pywraps/py_idbhooks.hpp +++ /dev/null @@ -1,640 +0,0 @@ - - -// - -//--------------------------------------------------------------------------- -// 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); - } - - // hookgenIDB:methods -virtual int byte_patched(ea_t ea, uint32 old_value) {qnotused(ea); qnotused(old_value); return 0;} -virtual int cmt_changed(ea_t ea, bool repeatable_cmt) {qnotused(ea); qnotused(repeatable_cmt); return 0;} -virtual int ti_changed(ea_t ea, const type_t * type, const p_list * fnames) {qnotused(ea); qnotused(type); qnotused(fnames); return 0;} -virtual int op_ti_changed(ea_t ea, int n, const type_t * type, const p_list * fnames) {qnotused(ea); qnotused(n); qnotused(type); qnotused(fnames); return 0;} -virtual int op_type_changed(ea_t ea, int n) {qnotused(ea); qnotused(n); return 0;} -virtual int enum_created(enum_t id) {qnotused(id); return 0;} -virtual int enum_deleted(enum_t id) {qnotused(id); return 0;} -virtual int enum_bf_changed(enum_t id) {qnotused(id); return 0;} -virtual int enum_renamed(tid_t id) {qnotused(id); return 0;} -virtual int enum_cmt_changed(tid_t id, bool repeatable) {qnotused(id); qnotused(repeatable); return 0;} -virtual int enum_member_created(enum_t id, const_t cid) {qnotused(id); qnotused(cid); return 0;} -virtual int enum_member_deleted(enum_t id, const_t cid) {qnotused(id); qnotused(cid); return 0;} -virtual int struc_created(tid_t struc_id) {qnotused(struc_id); return 0;} -virtual int struc_deleted(tid_t struc_id) {qnotused(struc_id); return 0;} -virtual int struc_renamed(struc_t * sptr) {qnotused(sptr); return 0;} -virtual int struc_expanded(struc_t * sptr) {qnotused(sptr); return 0;} -virtual int struc_cmt_changed(tid_t struc_id, bool repeatable_cmt) {qnotused(struc_id); qnotused(repeatable_cmt); return 0;} -virtual int struc_member_created(struc_t * sptr, member_t * mptr) {qnotused(sptr); qnotused(mptr); return 0;} -virtual int struc_member_deleted(struc_t * sptr, tid_t member_id, ea_t offset) {qnotused(sptr); qnotused(member_id); qnotused(offset); return 0;} -virtual int struc_member_renamed(struc_t * sptr, member_t * mptr) {qnotused(sptr); qnotused(mptr); return 0;} -virtual int struc_member_changed(struc_t * sptr, member_t * mptr) {qnotused(sptr); qnotused(mptr); return 0;} -virtual int thunk_func_created(func_t * pfn) {qnotused(pfn); return 0;} -virtual int func_tail_appended(func_t * pfn, func_t * tail) {qnotused(pfn); qnotused(tail); return 0;} -virtual int func_tail_removed(func_t * pfn, ea_t tail_ea) {qnotused(pfn); qnotused(tail_ea); return 0;} -virtual int tail_owner_changed(func_t * tail, ea_t owner_func, ea_t old_owner) {qnotused(tail); qnotused(owner_func); qnotused(old_owner); return 0;} -virtual int func_noret_changed(func_t * pfn) {qnotused(pfn); return 0;} -virtual int segm_added(segment_t * s) {qnotused(s); return 0;} -virtual int segm_deleted(ea_t startEA, ea_t endEA) {qnotused(startEA); qnotused(endEA); return 0;} -virtual int segm_start_changed(segment_t * s, ea_t oldstart) {qnotused(s); qnotused(oldstart); return 0;} -virtual int segm_end_changed(segment_t * s, ea_t oldend) {qnotused(s); qnotused(oldend); return 0;} -virtual int segm_moved(ea_t from, ea_t to, asize_t size) {qnotused(from); qnotused(to); qnotused(size); return 0;} -virtual int area_cmt_changed(areacb_t * cb, const area_t * a, const char * cmt, bool repeatable) {qnotused(cb); qnotused(a); qnotused(cmt); qnotused(repeatable); return 0;} -virtual int changing_cmt(ea_t ea, bool repeatable_cmt, const char * newcmt) {qnotused(ea); qnotused(repeatable_cmt); qnotused(newcmt); return 0;} -virtual int changing_ti(ea_t ea, const type_t * new_type, const p_list * new_fnames) {qnotused(ea); qnotused(new_type); qnotused(new_fnames); return 0;} -virtual int changing_op_ti(ea_t ea, int n, const type_t * new_type, const p_list * new_fnames) {qnotused(ea); qnotused(n); qnotused(new_type); qnotused(new_fnames); return 0;} -virtual int changing_op_type(ea_t ea, int n) {qnotused(ea); qnotused(n); return 0;} -virtual int deleting_enum(enum_t id) {qnotused(id); return 0;} -virtual int changing_enum_bf(enum_t id, bool new_bf) {qnotused(id); qnotused(new_bf); return 0;} -virtual int renaming_enum(tid_t id, bool is_enum, const char * newname) {qnotused(id); qnotused(is_enum); qnotused(newname); return 0;} -virtual int changing_enum_cmt(tid_t id, bool repeatable, const char * newcmt) {qnotused(id); qnotused(repeatable); qnotused(newcmt); return 0;} -virtual int deleting_enum_const() {return 0;} -virtual int deleting_struc(struc_t * sptr) {qnotused(sptr); return 0;} -virtual int renaming_struc(tid_t id, const char * oldname, const char * newname) {qnotused(id); qnotused(oldname); qnotused(newname); return 0;} -virtual int expanding_struc(struc_t * sptr, ea_t offset, adiff_t delta) {qnotused(sptr); qnotused(offset); qnotused(delta); return 0;} -virtual int changing_struc_cmt(tid_t struc_id, bool repeatable, const char * newcmt) {qnotused(struc_id); qnotused(repeatable); qnotused(newcmt); return 0;} -virtual int deleting_struc_member(struc_t * sptr, member_t * mptr) {qnotused(sptr); qnotused(mptr); return 0;} -virtual int renaming_struc_member(struc_t * sptr, member_t * mptr, const char * newname) {qnotused(sptr); qnotused(mptr); qnotused(newname); return 0;} -virtual int changing_struc_member(struc_t * sptr, member_t * mptr, flags_t flag, const opinfo_t * ti, asize_t nbytes) {qnotused(sptr); qnotused(mptr); qnotused(flag); qnotused(ti); qnotused(nbytes); return 0;} -virtual int removing_func_tail(func_t * pfn, const area_t * tail) {qnotused(pfn); qnotused(tail); return 0;} -virtual int deleting_segm(ea_t startEA) {qnotused(startEA); return 0;} -virtual int changing_segm_start(segment_t * s, ea_t new_start, int segmod_flags) {qnotused(s); qnotused(new_start); qnotused(segmod_flags); return 0;} -virtual int changing_segm_end(segment_t * s, ea_t new_end, int segmod_flags) {qnotused(s); qnotused(new_end); qnotused(segmod_flags); return 0;} -virtual int changing_area_cmt(areacb_t * cb, const area_t * a, const char * cmt, bool repeatable) {qnotused(cb); qnotused(a); qnotused(cmt); qnotused(repeatable); return 0;} -virtual int changing_segm_name(segment_t * s, const char * oldname) {qnotused(s); qnotused(oldname); return 0;} -virtual int changing_segm_class(segment_t * s) {qnotused(s); return 0;} -virtual int segm_name_changed(segment_t * s, const char * name) {qnotused(s); qnotused(name); return 0;} -virtual int segm_class_changed(segment_t * s, const char * sclass) {qnotused(s); qnotused(sclass); return 0;} -virtual int destroyed_items(ea_t ea1, ea_t ea2, bool will_disable_range) {qnotused(ea1); qnotused(ea2); qnotused(will_disable_range); return 0;} -virtual int changed_stkpnts(func_t * pfn) {qnotused(pfn); return 0;} -virtual int extra_cmt_changed(ea_t ea, int line_idx, const char * cmt) {qnotused(ea); qnotused(line_idx); qnotused(cmt); return 0;} -virtual int changing_struc(struc_t * sptr) {qnotused(sptr); return 0;} -virtual int changed_struc(struc_t * sptr) {qnotused(sptr); return 0;} -virtual int local_types_changed() {return 0;} -virtual int segm_attrs_changed(segment_t * s) {qnotused(s); return 0;} -virtual int allsegs_moved(segm_move_infos_t * info) {qnotused(info); return 0;} -}; -// - - -// -//--------------------------------------------------------------------------- -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; - int ret = 0; - try - { - switch ( notification_code ) - { - // hookgenIDB:notifications -case idb_event::byte_patched: -{ - ea_t ea = va_arg(va, ea_t); - uint32 old_value = va_arg(va, uint32); - ret = proxy->byte_patched(ea, old_value); -} -break; - -case idb_event::cmt_changed: -{ - ea_t ea = va_arg(va, ea_t); - bool repeatable_cmt = bool(va_arg(va, int)); - ret = proxy->cmt_changed(ea, repeatable_cmt); -} -break; - -case idb_event::ti_changed: -{ - ea_t ea = va_arg(va, ea_t); - const type_t * type = va_arg(va, const type_t *); - const p_list * fnames = va_arg(va, const p_list *); - ret = proxy->ti_changed(ea, type, fnames); -} -break; - -case idb_event::op_ti_changed: -{ - ea_t ea = va_arg(va, ea_t); - int n = va_arg(va, int); - const type_t * type = va_arg(va, const type_t *); - const p_list * fnames = va_arg(va, const p_list *); - ret = proxy->op_ti_changed(ea, n, type, fnames); -} -break; - -case idb_event::op_type_changed: -{ - ea_t ea = va_arg(va, ea_t); - int n = va_arg(va, int); - ret = proxy->op_type_changed(ea, n); -} -break; - -case idb_event::enum_created: -{ - enum_t id = va_arg(va, enum_t); - ret = proxy->enum_created(id); -} -break; - -case idb_event::enum_deleted: -{ - enum_t id = va_arg(va, enum_t); - ret = proxy->enum_deleted(id); -} -break; - -case idb_event::enum_bf_changed: -{ - enum_t id = va_arg(va, enum_t); - ret = proxy->enum_bf_changed(id); -} -break; - -case idb_event::enum_renamed: -{ - tid_t id = va_arg(va, tid_t); - ret = proxy->enum_renamed(id); -} -break; - -case idb_event::enum_cmt_changed: -{ - tid_t id = va_arg(va, tid_t); - bool repeatable = bool(va_arg(va, int)); - ret = proxy->enum_cmt_changed(id, repeatable); -} -break; - -case idb_event::enum_const_created: -{ - enum_t id = va_arg(va, enum_t); - const_t cid = va_arg(va, const_t); - ret = proxy->enum_member_created(id, cid); -} -break; - -case idb_event::enum_const_deleted: -{ - enum_t id = va_arg(va, enum_t); - const_t cid = va_arg(va, const_t); - ret = proxy->enum_member_deleted(id, cid); -} -break; - -case idb_event::struc_created: -{ - tid_t struc_id = va_arg(va, tid_t); - ret = proxy->struc_created(struc_id); -} -break; - -case idb_event::struc_deleted: -{ - tid_t struc_id = va_arg(va, tid_t); - ret = proxy->struc_deleted(struc_id); -} -break; - -case idb_event::struc_renamed: -{ - struc_t * sptr = va_arg(va, struc_t *); - ret = proxy->struc_renamed(sptr); -} -break; - -case idb_event::struc_expanded: -{ - struc_t * sptr = va_arg(va, struc_t *); - ret = proxy->struc_expanded(sptr); -} -break; - -case idb_event::struc_cmt_changed: -{ - tid_t struc_id = va_arg(va, tid_t); - bool repeatable_cmt = bool(va_arg(va, int)); - ret = proxy->struc_cmt_changed(struc_id, repeatable_cmt); -} -break; - -case idb_event::struc_member_created: -{ - struc_t * sptr = va_arg(va, struc_t *); - member_t * mptr = va_arg(va, member_t *); - ret = proxy->struc_member_created(sptr, mptr); -} -break; - -case idb_event::struc_member_deleted: -{ - struc_t * sptr = va_arg(va, struc_t *); - tid_t member_id = va_arg(va, tid_t); - ea_t offset = va_arg(va, ea_t); - ret = proxy->struc_member_deleted(sptr, member_id, offset); -} -break; - -case idb_event::struc_member_renamed: -{ - struc_t * sptr = va_arg(va, struc_t *); - member_t * mptr = va_arg(va, member_t *); - ret = proxy->struc_member_renamed(sptr, mptr); -} -break; - -case idb_event::struc_member_changed: -{ - struc_t * sptr = va_arg(va, struc_t *); - member_t * mptr = va_arg(va, member_t *); - ret = proxy->struc_member_changed(sptr, mptr); -} -break; - -case idb_event::thunk_func_created: -{ - func_t * pfn = va_arg(va, func_t *); - ret = proxy->thunk_func_created(pfn); -} -break; - -case idb_event::func_tail_appended: -{ - func_t * pfn = va_arg(va, func_t *); - func_t * tail = va_arg(va, func_t *); - ret = proxy->func_tail_appended(pfn, tail); -} -break; - -case idb_event::func_tail_removed: -{ - func_t * pfn = va_arg(va, func_t *); - ea_t tail_ea = va_arg(va, ea_t); - ret = proxy->func_tail_removed(pfn, tail_ea); -} -break; - -case idb_event::tail_owner_changed: -{ - func_t * tail = va_arg(va, func_t *); - ea_t owner_func = va_arg(va, ea_t); - ea_t old_owner = va_arg(va, ea_t); - ret = proxy->tail_owner_changed(tail, owner_func, old_owner); -} -break; - -case idb_event::func_noret_changed: -{ - func_t * pfn = va_arg(va, func_t *); - ret = proxy->func_noret_changed(pfn); -} -break; - -case idb_event::segm_added: -{ - segment_t * s = va_arg(va, segment_t *); - ret = proxy->segm_added(s); -} -break; - -case idb_event::segm_deleted: -{ - ea_t startEA = va_arg(va, ea_t); - ea_t endEA = va_arg(va, ea_t); - ret = proxy->segm_deleted(startEA, endEA); -} -break; - -case idb_event::segm_start_changed: -{ - segment_t * s = va_arg(va, segment_t *); - ea_t oldstart = va_arg(va, ea_t); - ret = proxy->segm_start_changed(s, oldstart); -} -break; - -case idb_event::segm_end_changed: -{ - segment_t * s = va_arg(va, segment_t *); - ea_t oldend = va_arg(va, ea_t); - ret = proxy->segm_end_changed(s, oldend); -} -break; - -case idb_event::segm_moved: -{ - ea_t from = va_arg(va, ea_t); - ea_t to = va_arg(va, ea_t); - asize_t size = va_arg(va, asize_t); - ret = proxy->segm_moved(from, to, size); -} -break; - -case idb_event::area_cmt_changed: -{ - areacb_t * cb = va_arg(va, areacb_t *); - const area_t * a = va_arg(va, const area_t *); - const char * cmt = va_arg(va, const char *); - bool repeatable = bool(va_arg(va, int)); - ret = proxy->area_cmt_changed(cb, a, cmt, repeatable); -} -break; - -case idb_event::changing_cmt: -{ - ea_t ea = va_arg(va, ea_t); - bool repeatable_cmt = bool(va_arg(va, int)); - const char * newcmt = va_arg(va, const char *); - ret = proxy->changing_cmt(ea, repeatable_cmt, newcmt); -} -break; - -case idb_event::changing_ti: -{ - ea_t ea = va_arg(va, ea_t); - const type_t * new_type = va_arg(va, const type_t *); - const p_list * new_fnames = va_arg(va, const p_list *); - ret = proxy->changing_ti(ea, new_type, new_fnames); -} -break; - -case idb_event::changing_op_ti: -{ - ea_t ea = va_arg(va, ea_t); - int n = va_arg(va, int); - const type_t * new_type = va_arg(va, const type_t *); - const p_list * new_fnames = va_arg(va, const p_list *); - ret = proxy->changing_op_ti(ea, n, new_type, new_fnames); -} -break; - -case idb_event::changing_op_type: -{ - ea_t ea = va_arg(va, ea_t); - int n = va_arg(va, int); - ret = proxy->changing_op_type(ea, n); -} -break; - -case idb_event::deleting_enum: -{ - enum_t id = va_arg(va, enum_t); - ret = proxy->deleting_enum(id); -} -break; - -case idb_event::changing_enum_bf: -{ - enum_t id = va_arg(va, enum_t); - bool new_bf = bool(va_arg(va, int)); - ret = proxy->changing_enum_bf(id, new_bf); -} -break; - -case idb_event::renaming_enum: -{ - tid_t id = va_arg(va, tid_t); - bool is_enum = bool(va_arg(va, int)); - const char * newname = va_arg(va, const char *); - ret = proxy->renaming_enum(id, is_enum, newname); -} -break; - -case idb_event::changing_enum_cmt: -{ - tid_t id = va_arg(va, tid_t); - bool repeatable = bool(va_arg(va, int)); - const char * newcmt = va_arg(va, const char *); - ret = proxy->changing_enum_cmt(id, repeatable, newcmt); -} -break; - -case idb_event::deleting_enum_const: -{ - ret = proxy->deleting_enum_const(); -} -break; - -case idb_event::deleting_struc: -{ - struc_t * sptr = va_arg(va, struc_t *); - ret = proxy->deleting_struc(sptr); -} -break; - -case idb_event::renaming_struc: -{ - tid_t id = va_arg(va, tid_t); - const char * oldname = va_arg(va, const char *); - const char * newname = va_arg(va, const char *); - ret = proxy->renaming_struc(id, oldname, newname); -} -break; - -case idb_event::expanding_struc: -{ - struc_t * sptr = va_arg(va, struc_t *); - ea_t offset = va_arg(va, ea_t); - adiff_t delta = va_arg(va, adiff_t); - ret = proxy->expanding_struc(sptr, offset, delta); -} -break; - -case idb_event::changing_struc_cmt: -{ - tid_t struc_id = va_arg(va, tid_t); - bool repeatable = bool(va_arg(va, int)); - const char * newcmt = va_arg(va, const char *); - ret = proxy->changing_struc_cmt(struc_id, repeatable, newcmt); -} -break; - -case idb_event::deleting_struc_member: -{ - struc_t * sptr = va_arg(va, struc_t *); - member_t * mptr = va_arg(va, member_t *); - ret = proxy->deleting_struc_member(sptr, mptr); -} -break; - -case idb_event::renaming_struc_member: -{ - struc_t * sptr = va_arg(va, struc_t *); - member_t * mptr = va_arg(va, member_t *); - const char * newname = va_arg(va, const char *); - ret = proxy->renaming_struc_member(sptr, mptr, newname); -} -break; - -case idb_event::changing_struc_member: -{ - struc_t * sptr = va_arg(va, struc_t *); - member_t * mptr = va_arg(va, member_t *); - flags_t flag = va_arg(va, flags_t); - const opinfo_t * ti = va_arg(va, const opinfo_t *); - asize_t nbytes = va_arg(va, asize_t); - ret = proxy->changing_struc_member(sptr, mptr, flag, ti, nbytes); -} -break; - -case idb_event::removing_func_tail: -{ - func_t * pfn = va_arg(va, func_t *); - const area_t * tail = va_arg(va, const area_t *); - ret = proxy->removing_func_tail(pfn, tail); -} -break; - -case idb_event::deleting_segm: -{ - ea_t startEA = va_arg(va, ea_t); - ret = proxy->deleting_segm(startEA); -} -break; - -case idb_event::changing_segm_start: -{ - segment_t * s = va_arg(va, segment_t *); - ea_t new_start = va_arg(va, ea_t); - int segmod_flags = va_arg(va, int); - ret = proxy->changing_segm_start(s, new_start, segmod_flags); -} -break; - -case idb_event::changing_segm_end: -{ - segment_t * s = va_arg(va, segment_t *); - ea_t new_end = va_arg(va, ea_t); - int segmod_flags = va_arg(va, int); - ret = proxy->changing_segm_end(s, new_end, segmod_flags); -} -break; - -case idb_event::changing_area_cmt: -{ - areacb_t * cb = va_arg(va, areacb_t *); - const area_t * a = va_arg(va, const area_t *); - const char * cmt = va_arg(va, const char *); - bool repeatable = bool(va_arg(va, int)); - ret = proxy->changing_area_cmt(cb, a, cmt, repeatable); -} -break; - -case idb_event::changing_segm_name: -{ - segment_t * s = va_arg(va, segment_t *); - const char * oldname = va_arg(va, const char *); - ret = proxy->changing_segm_name(s, oldname); -} -break; - -case idb_event::changing_segm_class: -{ - segment_t * s = va_arg(va, segment_t *); - ret = proxy->changing_segm_class(s); -} -break; - -case idb_event::segm_name_changed: -{ - segment_t * s = va_arg(va, segment_t *); - const char * name = va_arg(va, const char *); - ret = proxy->segm_name_changed(s, name); -} -break; - -case idb_event::segm_class_changed: -{ - segment_t * s = va_arg(va, segment_t *); - const char * sclass = va_arg(va, const char *); - ret = proxy->segm_class_changed(s, sclass); -} -break; - -case idb_event::destroyed_items: -{ - ea_t ea1 = va_arg(va, ea_t); - ea_t ea2 = va_arg(va, ea_t); - bool will_disable_range = bool(va_arg(va, int)); - ret = proxy->destroyed_items(ea1, ea2, will_disable_range); -} -break; - -case idb_event::changed_stkpnts: -{ - func_t * pfn = va_arg(va, func_t *); - ret = proxy->changed_stkpnts(pfn); -} -break; - -case idb_event::extra_cmt_changed: -{ - ea_t ea = va_arg(va, ea_t); - int line_idx = va_arg(va, int); - const char * cmt = va_arg(va, const char *); - ret = proxy->extra_cmt_changed(ea, line_idx, cmt); -} -break; - -case idb_event::changing_struc: -{ - struc_t * sptr = va_arg(va, struc_t *); - ret = proxy->changing_struc(sptr); -} -break; - -case idb_event::changed_struc: -{ - struc_t * sptr = va_arg(va, struc_t *); - ret = proxy->changed_struc(sptr); -} -break; - -case idb_event::local_types_changed: -{ - ret = proxy->local_types_changed(); -} -break; - -case idb_event::segm_attrs_changed: -{ - segment_t * s = va_arg(va, segment_t *); - ret = proxy->segm_attrs_changed(s); -} -break; - -case idb_event::allsegs_moved: -{ - segm_move_infos_t * info = va_arg(va, segm_move_infos_t *); - ret = proxy->allsegs_moved(info); -} -break; - - } - } - 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; -} -// diff --git a/pywraps/py_idd.hpp b/pywraps/py_idd.hpp index 7ed17c5..0443002 100644 --- a/pywraps/py_idd.hpp +++ b/pywraps/py_idd.hpp @@ -9,31 +9,6 @@ static bool dbg_can_query() 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, @@ -56,7 +31,7 @@ PyObject *py_appcall( Py_ssize_t nargs = PyList_Size(arg_list); idc_args.resize(nargs); bool ok = true; - for ( Py_ssize_t i=0; iob_refcnt == 1); if ( (debug & IDA_DEBUG_APPCALL) != 0 ) { msg("return var:\n" @@ -207,7 +181,7 @@ static PyObject *dbg_get_registers() PyObject *py_list = PyList_New(dbg->registers_size); - for ( int i=0; iregisters_size; i++ ) + for ( int i=0; i < dbg->registers_size; i++ ) { register_info_t &ri = dbg->registers(i); PyObject *py_bits; @@ -218,7 +192,7 @@ static PyObject *dbg_get_registers() { int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; py_bits = PyList_New(nbits); - for ( int i=0; i diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp index e2cb0c2..fcb9bea 100644 --- a/pywraps/py_idp.hpp +++ b/pywraps/py_idp.hpp @@ -352,7 +352,7 @@ 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 @@ -908,944 +782,6 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) switch ( notification_code ) { // hookgenIDP:notifications -case processor_t::init: -{ - const char * idp_modname = va_arg(va, const char *); - ret = proxy->init(idp_modname); -} -break; - -case processor_t::term: -{ - ret = proxy->term(); -} -break; - -case processor_t::newprc: -{ - int pnum = va_arg(va, int); - ret = proxy->newprc(pnum); -} -break; - -case processor_t::newasm: -{ - int asmnum = va_arg(va, int); - ret = proxy->newasm(asmnum); -} -break; - -case processor_t::newfile: -{ - char * fname = va_arg(va, char *); - ret = proxy->newfile(fname); -} -break; - -case processor_t::oldfile: -{ - char * fname = va_arg(va, char *); - ret = proxy->oldfile(fname); -} -break; - -case processor_t::newbinary: -{ - char * filename = va_arg(va, char *); - uint32 fileoff = va_arg(va, uint32); - ea_t basepara = va_arg(va, ea_t); - ea_t binoff = va_arg(va, ea_t); - uint32 nbytes = va_arg(va, uint32); - ret = proxy->newbinary(filename, fileoff, basepara, binoff, nbytes); -} -break; - -case processor_t::endbinary: -{ - bool ok = bool(va_arg(va, int)); - ret = proxy->endbinary(ok); -} -break; - -case processor_t::newseg: -{ - segment_t * seg = va_arg(va, segment_t *); - ret = proxy->newseg(seg); -} -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 = bool(va_arg(va, int)); - const char * line = va_arg(va, const char *); - uchar * bin = va_arg(va, uchar *); - PyObject * _tmp = proxy->assemble(ea, cs, ip, use32, line); - ret = IDP_Hooks::handle_assemble_output(_tmp, ea, cs, ip, use32, line, bin); -} -break; - -case processor_t::obsolete_makemicro: -{ - mblock_t * block = va_arg(va, mblock_t *); - ret = proxy->obsolete_makemicro(block); -} -break; - -case processor_t::outlabel: -{ - ea_t ea = va_arg(va, ea_t); - const char * colored_name = va_arg(va, const char *); - ret = proxy->outlabel(ea, colored_name); -} -break; - -case processor_t::rename: -{ - ea_t ea = va_arg(va, ea_t); - const char * new_name = va_arg(va, const char *); - int flags = va_arg(va, int); - qnotused(flags); - ret = proxy->rename(ea, new_name); -} -break; - -case processor_t::may_show_sreg: -{ - ea_t current_ea = va_arg(va, ea_t); - ret = proxy->may_show_sreg(current_ea); -} -break; - -case processor_t::closebase: -{ - ret = proxy->closebase(); -} -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::coagulate: -{ - ea_t start_ea = va_arg(va, ea_t); - ret = proxy->coagulate(start_ea); -} -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::func_bounds: -{ - int * possible_return_code = va_arg(va, int *); - func_t * pfn = va_arg(va, func_t *); - ea_t max_func_end_ea = va_arg(va, ea_t); - proxy->func_bounds(possible_return_code, pfn, max_func_end_ea); -} -break; - -case processor_t::may_be_func: -{ - int state = va_arg(va, int); - ret = proxy->may_be_func(state); -} -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::is_jump_func: -{ - func_t * pfn = va_arg(va, func_t *); - ea_t * jump_target = va_arg(va, ea_t *); - ea_t * func_pointer = va_arg(va, ea_t *); - ret = proxy->is_jump_func(pfn, jump_target, func_pointer); -} -break; - -case processor_t::gen_regvar_def: -{ - regvar_t * v = va_arg(va, regvar_t *); - ret = proxy->gen_regvar_def(v); -} -break; - -case processor_t::setsgr: -{ - ea_t startEA = va_arg(va, ea_t); - ea_t endEA = va_arg(va, ea_t); - int regnum = va_arg(va, int); - sel_t value = va_arg(va, sel_t); - sel_t old_value = va_arg(va, sel_t); - uchar tag = uchar(va_arg(va, int)); - ret = proxy->setsgr(startEA, endEA, regnum, value, old_value, tag); -} -break; - -case processor_t::set_compiler: -{ - ret = proxy->set_compiler(); -} -break; - -case processor_t::is_basic_block_end: -{ - bool call_insn_stops_block = bool(va_arg(va, int)); - ret = proxy->is_basic_block_end(call_insn_stops_block); -} -break; - -case processor_t::reglink: -{ - ret = proxy->reglink(); -} -break; - -case processor_t::get_vxd_name: -{ - int vxdnum = va_arg(va, int); - int funcnum = va_arg(va, int); - char * outbuf = va_arg(va, char *); - proxy->get_vxd_name(vxdnum, funcnum, outbuf); -} -break; - -case processor_t::custom_ana: -{ - bool _tmp = proxy->custom_ana(); - ret = IDP_Hooks::bool_to_cmdsize(_tmp); -} -break; - -case processor_t::custom_out: -{ - bool _tmp = proxy->custom_out(); - ret = IDP_Hooks::bool_to_2or0(_tmp); -} -break; - -case processor_t::custom_emu: -{ - bool _tmp = proxy->custom_emu(); - ret = IDP_Hooks::bool_to_2or0(_tmp); -} -break; - -case processor_t::custom_outop: -{ - op_t * op = va_arg(va, op_t *); - ref_t clinked_op = create_idaapi_linked_class_instance(S_PY_OP_T_CLSNAME, op); - if ( clinked_op == NULL ) - break; - bool _tmp = proxy->custom_outop(clinked_op.o); - ret = IDP_Hooks::bool_to_2or0(_tmp); -} -break; - -case processor_t::custom_mnem: -{ - char * buf = va_arg(va, char *); - size_t bufsize = va_arg(va, size_t); - PyObject * _tmp = proxy->custom_mnem(); - ret = IDP_Hooks::handle_custom_mnem_output(_tmp, buf, bufsize); -} -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::moving_segm: -{ - segment_t * seg = va_arg(va, segment_t *); - ea_t to = va_arg(va, ea_t); - int flags = va_arg(va, int); - ret = proxy->moving_segm(seg, to, flags); -} -break; - -case processor_t::move_segm: -{ - ea_t from = va_arg(va, ea_t); - segment_t * seg = va_arg(va, segment_t *); - proxy->move_segm(from, seg); -} -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 = bool(va_arg(va, int)); - ret = proxy->is_ret_insn(ea, strict); -} -break; - -case processor_t::get_stkvar_scale_factor: -{ - ret = proxy->get_stkvar_scale_factor(); -} -break; - -case processor_t::create_flat_group: -{ - ea_t image_base = va_arg(va, ea_t); - int bitness = va_arg(va, int); - sel_t dataseg_sel = va_arg(va, sel_t); - ret = proxy->create_flat_group(image_base, bitness, dataseg_sel); -} -break; - -case processor_t::kernel_config_loaded: -{ - proxy->kernel_config_loaded(); -} -break; - -case processor_t::might_change_sp: -{ - ea_t ea = va_arg(va, ea_t); - ret = proxy->might_change_sp(ea); -} -break; - -case processor_t::is_alloca_probe: -{ - ea_t ea = va_arg(va, ea_t); - ret = proxy->is_alloca_probe(ea); -} -break; - -case processor_t::out_3byte: -{ - ea_t dataea = va_arg(va, ea_t); - uint32 value = va_arg(va, uint32); - bool analyze_only = bool(va_arg(va, int)); - ret = proxy->out_3byte(dataea, value, analyze_only); -} -break; - -case processor_t::get_reg_name: -{ - int reg = va_arg(va, int); - size_t width = va_arg(va, size_t); - char * buf = va_arg(va, char *); - size_t bufsize = va_arg(va, size_t); - int reghi = va_arg(va, int); - PyObject * _tmp = proxy->get_reg_name(reg, width, reghi); - ret = IDP_Hooks::handle_get_reg_name_output(_tmp, reg, width, buf, bufsize, reghi); -} -break; - -case processor_t::savebase: -{ - proxy->savebase(); -} -break; - -case processor_t::gen_asm_or_lst: -{ - bool starting = bool(va_arg(va, int)); - FILE * fp = va_arg(va, FILE *); - bool is_asm = bool(va_arg(va, int)); - int flags = va_arg(va, int); - gen_outline_t ** outline = va_arg(va, gen_outline_t **); - proxy->gen_asm_or_lst(starting, fp, is_asm, flags, outline); -} -break; - -case processor_t::out_src_file_lnnum: -{ - ret = proxy->out_src_file_lnnum(); -} -break; - -case processor_t::get_autocmt: -{ - char * buf = va_arg(va, char *); - size_t bufsize = va_arg(va, size_t); - ret = proxy->get_autocmt(buf, bufsize); -} -break; - -case processor_t::is_insn_table_jump: -{ - ret = proxy->is_insn_table_jump(); -} -break; - -case processor_t::auto_empty_finally: -{ - proxy->auto_empty_finally(); -} -break; - -case processor_t::loader_finished: -{ - linput_t * li = va_arg(va, linput_t *); - uint16 neflags = uint16(va_arg(va, int)); - const char * filetypename = va_arg(va, const char *); - ret = proxy->loader_finished(li, neflags, filetypename); -} -break; - -case processor_t::loader_elf_machine: -{ - linput_t * li = va_arg(va, linput_t *); - int machine_type = va_arg(va, int); - const char ** p_procname = va_arg(va, const char **); - proc_def ** p_pd = va_arg(va, proc_def **); - set_elf_reloc_t * set_reloc = va_arg(va, set_elf_reloc_t *); - ret = proxy->loader_elf_machine(li, machine_type, p_procname, p_pd, set_reloc); -} -break; - -case processor_t::is_indirect_jump: -{ - ret = proxy->is_indirect_jump(); -} -break; - -case processor_t::verify_noreturn: -{ - func_t * pfn = va_arg(va, func_t *); - ret = proxy->verify_noreturn(pfn); -} -break; - -case processor_t::verify_sp: -{ - func_t * pfn = va_arg(va, func_t *); - ret = proxy->verify_sp(pfn); -} -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 = bool(va_arg(va, int)); - proxy->renamed(ea, new_name, local_name); -} -break; - -case processor_t::add_func: -{ - func_t * pfn = va_arg(va, func_t *); - proxy->add_func(pfn); -} -break; - -case processor_t::del_func: -{ - func_t * pfn = va_arg(va, func_t *); - ret = proxy->del_func(pfn); -} -break; - -case processor_t::set_func_start: -{ - func_t * pfn = va_arg(va, func_t *); - ea_t new_start = va_arg(va, ea_t); - ret = proxy->set_func_start(pfn, new_start); -} -break; - -case processor_t::set_func_end: -{ - func_t * pfn = va_arg(va, func_t *); - ea_t new_end = va_arg(va, ea_t); - ret = proxy->set_func_end(pfn, new_end); -} -break; - -case processor_t::treat_hindering_item: -{ - ea_t hindering_item_ea = va_arg(va, ea_t); - flags_t new_item_flags = va_arg(va, flags_t); - ea_t new_item_ea = va_arg(va, ea_t); - asize_t new_item_length = va_arg(va, asize_t); - ret = proxy->treat_hindering_item(hindering_item_ea, new_item_flags, new_item_ea, new_item_length); -} -break; - -case processor_t::str2reg: -{ - const char * regname = va_arg(va, const char *); - ret = proxy->str2reg(regname); -} -break; - -case processor_t::create_switch_xrefs: -{ - ea_t jumpea = va_arg(va, ea_t); - switch_info_ex_t * si = va_arg(va, switch_info_ex_t *); - ret = proxy->create_switch_xrefs(jumpea, si); -} -break; - -case processor_t::calc_switch_cases: -{ - ea_t insn_ea = va_arg(va, ea_t); - switch_info_ex_t * si = va_arg(va, switch_info_ex_t *); - casevec_t * casevec = va_arg(va, casevec_t *); - eavec_t * targets = va_arg(va, eavec_t *); - ret = proxy->calc_switch_cases(insn_ea, si, casevec, targets); -} -break; - -case processor_t::determined_main: -{ - ea_t main = va_arg(va, ea_t); - proxy->determined_main(main); -} -break; - -case processor_t::preprocess_chart: -{ - qflow_chart_t * fc = va_arg(va, qflow_chart_t *); - proxy->preprocess_chart(fc); -} -break; - -case processor_t::get_bg_color: -{ - ea_t ea = va_arg(va, ea_t); - bgcolor_t color = va_arg(va, bgcolor_t); - ret = proxy->get_bg_color(ea, color); -} -break; - -case processor_t::validate_flirt_func: -{ - ea_t start_ea = va_arg(va, ea_t); - const char * funcname = va_arg(va, const char *); - ret = proxy->validate_flirt_func(start_ea, funcname); -} -break; - -case processor_t::get_operand_string: -{ - int opnum = va_arg(va, int); - char * buf = va_arg(va, char *); - size_t buflen = va_arg(va, size_t); - ret = proxy->get_operand_string(opnum, buf, buflen); -} -break; - -case processor_t::add_cref: -{ - ea_t from = va_arg(va, ea_t); - ea_t to = va_arg(va, ea_t); - cref_t type = cref_t(va_arg(va, int)); - ret = proxy->add_cref(from, to, type); -} -break; - -case processor_t::add_dref: -{ - ea_t from = va_arg(va, ea_t); - ea_t to = va_arg(va, ea_t); - dref_t type = dref_t(va_arg(va, int)); - ret = proxy->add_dref(from, to, type); -} -break; - -case processor_t::del_cref: -{ - ea_t from = va_arg(va, ea_t); - ea_t to = va_arg(va, ea_t); - bool expand = bool(va_arg(va, int)); - ret = proxy->del_cref(from, to, expand); -} -break; - -case processor_t::del_dref: -{ - ea_t from = va_arg(va, ea_t); - ea_t to = va_arg(va, ea_t); - ret = proxy->del_dref(from, to); -} -break; - -case processor_t::coagulate_dref: -{ - ea_t from = va_arg(va, ea_t); - ea_t to = va_arg(va, ea_t); - bool may_define = bool(va_arg(va, int)); - ea_t * code_ea = va_arg(va, ea_t *); - ret = proxy->coagulate_dref(from, to, may_define, code_ea); -} -break; - -case processor_t::register_custom_fixup: -{ - const char * name = va_arg(va, const char *); - ret = proxy->register_custom_fixup(name); -} -break; - -case processor_t::custom_refinfo: -{ - ea_t ea = va_arg(va, ea_t); - int numop = va_arg(va, int); - ea_t * opval = va_arg(va, ea_t *); - const refinfo_t* ri = va_arg(va, const refinfo_t*); - char * buf = va_arg(va, char *); - size_t bufsize = va_arg(va, size_t); - ea_t * target = va_arg(va, ea_t *); - ea_t * fullvalue = va_arg(va, ea_t *); - ea_t from = va_arg(va, ea_t); - int getn_flags = va_arg(va, int); - ret = proxy->custom_refinfo(ea, numop, opval, ri, buf, bufsize, target, fullvalue, from, getn_flags); -} -break; - -case processor_t::set_proc_options: -{ - const char * options = va_arg(va, const char *); - ret = proxy->set_proc_options(options); -} -break; - -case processor_t::adjust_libfunc_ea: -{ - const idasgn_t * sig = va_arg(va, const idasgn_t *); - const libfunc_t * libfun = va_arg(va, const libfunc_t *); - ea_t * ea = va_arg(va, ea_t *); - ret = proxy->adjust_libfunc_ea(sig, libfun, ea); -} -break; - -case processor_t::extlang_changed: -{ - int kind = va_arg(va, int); - const extlang_t * el = va_arg(va, const extlang_t *); - proxy->extlang_changed(kind, el); -} -break; - -case processor_t::delay_slot_insn: -{ - ea_t * ea = va_arg(va, ea_t *); - ret = proxy->delay_slot_insn(ea); -} -break; - -case processor_t::obsolete_get_operand_info: -{ - ret = proxy->obsolete_get_operand_info(); -} -break; - -case processor_t::get_jump_target: -{ - ea_t ea = va_arg(va, ea_t); - int tid = va_arg(va, int); - processor_t::regval_getter_t getreg = va_arg(va, processor_t::regval_getter_t); - const regval_t * regvalues = va_arg(va, const regval_t *); - ea_t * target = va_arg(va, ea_t *); - ret = proxy->get_jump_target(ea, tid, getreg, regvalues, target); -} -break; - -case processor_t::calc_step_over: -{ - ea_t ip = va_arg(va, ea_t); - ea_t * target = va_arg(va, ea_t *); - ret = proxy->calc_step_over(ip, target); -} -break; - -case processor_t::get_macro_insn_head: -{ - ea_t ip = va_arg(va, ea_t); - ea_t * head = va_arg(va, ea_t *); - ret = proxy->get_macro_insn_head(ip, head); -} -break; - -case processor_t::get_dbr_opnum: -{ - ea_t ea = va_arg(va, ea_t); - int * opnum = va_arg(va, int *); - ret = proxy->get_dbr_opnum(ea, opnum); -} -break; - -case processor_t::insn_reads_tbit: -{ - ea_t ea = va_arg(va, ea_t); - processor_t::regval_getter_t getreg = va_arg(va, processor_t::regval_getter_t); - const regval_t * regvalues = va_arg(va, const regval_t *); - ret = proxy->insn_reads_tbit(ea, getreg, regvalues); -} -break; - -case processor_t::get_operand_info: -{ - ea_t ea = va_arg(va, ea_t); - int n = va_arg(va, int); - int thread_id = va_arg(va, int); - processor_t::regval_getter_t getreg = va_arg(va, processor_t::regval_getter_t); - const regval_t * regvalues = va_arg(va, const regval_t *); - idd_opinfo_t * opinf = va_arg(va, idd_opinfo_t *); - ret = proxy->get_operand_info(ea, n, thread_id, getreg, regvalues, opinf); -} -break; - -case processor_t::calc_next_eas: -{ - bool over = bool(va_arg(va, int)); - ea_t * res = va_arg(va, ea_t *); - int * nsubcalls = va_arg(va, int *); - ret = proxy->calc_next_eas(over, res, nsubcalls); -} -break; - -case processor_t::clean_tbit: -{ - ea_t ea = va_arg(va, ea_t); - processor_t::regval_getter_t getreg = va_arg(va, processor_t::regval_getter_t); - const regval_t * regvalues = va_arg(va, const regval_t *); - ret = proxy->clean_tbit(ea, getreg, regvalues); -} -break; - -case processor_t::get_reg_info2: -{ - const char * regname = va_arg(va, const char *); - const char ** main_regname = va_arg(va, const char **); - bitrange_t * bitrange = va_arg(va, bitrange_t *); - ret = proxy->get_reg_info2(regname, main_regname, bitrange); -} -break; - -case processor_t::setup_til: -{ - proxy->setup_til(); -} -break; - -case processor_t::based_ptr: -{ - unsigned ptrt = va_arg(va, unsigned); - const char ** ptrname = va_arg(va, const char **); - ret = proxy->based_ptr(ptrt, ptrname); -} -break; - -case processor_t::max_ptr_size: -{ - ret = proxy->max_ptr_size(); -} -break; - -case processor_t::get_default_enum_size: -{ - cm_t cm = cm_t(va_arg(va, int)); - ret = proxy->get_default_enum_size(cm); -} -break; - -case processor_t::calc_cdecl_purged_bytes2: -{ - ret = proxy->calc_cdecl_purged_bytes2(); -} -break; - -case processor_t::get_stkarg_offset2: -{ - ret = proxy->get_stkarg_offset2(); -} -break; - -case processor_t::til_for_file: -{ - ret = proxy->til_for_file(); -} -break; - -case processor_t::equal_reglocs: -{ - argloc_t * a1 = va_arg(va, argloc_t *); - argloc_t * a2 = va_arg(va, argloc_t *); - ret = proxy->equal_reglocs(a1, a2); -} -break; - -case processor_t::decorate_name3: -{ - qstring * outbuf = va_arg(va, qstring *); - const char * name = va_arg(va, const char *); - bool mangle = bool(va_arg(va, int)); - cm_t cc = cm_t(va_arg(va, int)); - PyObject * _tmp = proxy->decorate_name3(name, mangle, IDP_Hooks::cm_t_to_int(cc)); - ret = IDP_Hooks::handle_decorate_name3_output(_tmp, outbuf, name, mangle, cc); -} -break; - -case processor_t::calc_retloc3: -{ - const tinfo_t * rettype = va_arg(va, const tinfo_t *); - cm_t cc = cm_t(va_arg(va, int)); - argloc_t * retloc = va_arg(va, argloc_t *); - ret = proxy->calc_retloc3(rettype, cc, retloc); -} -break; - -case processor_t::calc_varglocs3: -{ - const func_type_data_t * ftd = va_arg(va, const func_type_data_t *); - regobjs_t * regs = va_arg(va, regobjs_t *); - relobj_t * stkargs = va_arg(va, relobj_t *); - int nfixed = va_arg(va, int); - ret = proxy->calc_varglocs3(ftd, regs, stkargs, nfixed); -} -break; - -case processor_t::calc_arglocs3: -{ - func_type_data_t * fti = va_arg(va, func_type_data_t *); - ret = proxy->calc_arglocs3(fti); -} -break; - -case processor_t::use_stkarg_type3: -{ - ea_t ea = va_arg(va, ea_t); - const funcarg_t * arg = va_arg(va, const funcarg_t *); - ret = proxy->use_stkarg_type3(ea, arg); -} -break; - -case processor_t::use_regarg_type3: -{ - int * idx = va_arg(va, int *); - ea_t ea = va_arg(va, ea_t); - const funcargvec_t * rargs = va_arg(va, const funcargvec_t *); - ret = proxy->use_regarg_type3(idx, ea, rargs); -} -break; - -case processor_t::use_arg_types3: -{ - ea_t ea = va_arg(va, ea_t); - func_type_data_t * fti = va_arg(va, func_type_data_t *); - funcargvec_t * rargs = va_arg(va, funcargvec_t *); - ret = proxy->use_arg_types3(ea, fti, rargs); -} -break; - -case processor_t::calc_purged_bytes3: -{ - int * p_purged_bytes = va_arg(va, int *); - const func_type_data_t * fti = va_arg(va, const func_type_data_t *); - ret = proxy->calc_purged_bytes3(p_purged_bytes, fti); -} -break; - -case processor_t::shadow_args_size: -{ - int * shadow_args_size = va_arg(va, int *); - func_t * pfn = va_arg(va, func_t *); - ret = proxy->shadow_args_size(shadow_args_size, pfn); -} -break; - -case processor_t::get_varcall_regs3: -{ - callregs_t * regs = va_arg(va, callregs_t *); - ret = proxy->get_varcall_regs3(regs); -} -break; - -case processor_t::get_fastcall_regs3: -{ - callregs_t * regs = va_arg(va, callregs_t *); - ret = proxy->get_fastcall_regs3(regs); -} -break; - -case processor_t::get_thiscall_regs3: -{ - callregs_t * regs = va_arg(va, callregs_t *); - ret = proxy->get_thiscall_regs3(regs); -} -break; - -case processor_t::get_func_cvtarg_map: -{ - const func_type_data_t * fti = va_arg(va, const func_type_data_t *); - intvec_t * argnums = va_arg(va, intvec_t *); - ret = proxy->get_func_cvtarg_map(fti, argnums); -} -break; - -case processor_t::get_simd_types: -{ - const simd_info_t * simd_attrs = va_arg(va, const simd_info_t *); - const argloc_t * argloc = va_arg(va, const argloc_t *); - simd_info_vec_t * out = va_arg(va, simd_info_vec_t *); - bool create_tifs = bool(va_arg(va, int)); - ret = proxy->get_simd_types(simd_attrs, argloc, out, create_tifs); -} -break; - -case processor_t::loader: -{ - ret = proxy->loader(); -} -break; - } } catch (Swig::DirectorException &e) @@ -1871,7 +807,6 @@ static const regval_t *idaapi _py_getreg( static regval_t rv; return &rv; } - //------------------------------------------------------------------------- // //------------------------------------------------------------------------- diff --git a/pywraps/py_idp.py b/pywraps/py_idp.py new file mode 100644 index 0000000..0c3ea17 --- /dev/null +++ b/pywraps/py_idp.py @@ -0,0 +1,232 @@ +# + +#---------------------------------------------------------------------------- +# 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 + + +# ---------------------------------------------------------------------- +# 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. + +# +# 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) + +# ---------------------------------------------------------------------- +import ida_ua +class processor_t(ida_idaapi.pyidc_opaque_object_t): + """Base class for all processor module scripts""" + def __init__(self): + # Take a reference to 'cmd' + self.cmd = ida_ua.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 ida_ua.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/pywraps/py_idp_idbhooks.hpp b/pywraps/py_idp_idbhooks.hpp new file mode 100644 index 0000000..5f40831 --- /dev/null +++ b/pywraps/py_idp_idbhooks.hpp @@ -0,0 +1,51 @@ + +// + +//--------------------------------------------------------------------------- +// 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); + } + + // hookgenIDB:methods +}; +// + + +// +//--------------------------------------------------------------------------- +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; + int ret = 0; + try + { + switch ( notification_code ) + { + // hookgenIDB:notifications + } + } + 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; +} +// diff --git a/pywraps/py_kernwin.hpp b/pywraps/py_kernwin.hpp index 7b6db62..ba66780 100644 --- a/pywraps/py_kernwin.hpp +++ b/pywraps/py_kernwin.hpp @@ -1,6 +1,32 @@ #ifndef __PY_KERNWIN__ #define __PY_KERNWIN__ + +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ + +//--------------------------------------------------------------------------- +// 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; +}; + +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ + + //------------------------------------------------------------------------ // //------------------------------------------------------------------------ @@ -41,27 +67,20 @@ static PyObject *py_register_timer(int interval, PyObject *py_callback) // Timer has been unregistered? if ( ret == -1 ) - { - // Free the context - Py_DECREF(ctx->pycallback); - delete ctx; - } + python_timer_del(ctx); return ret; }; }; - py_timer_ctx_t *ctx = new py_timer_ctx_t(); - ctx->pycallback = py_callback; - Py_INCREF(py_callback); + py_timer_ctx_t *ctx = python_timer_new(py_callback); ctx->timer_id = register_timer( - interval, - tmr_t::callback, - ctx); + interval, + tmr_t::callback, + ctx); if ( ctx->timer_id == NULL ) { - Py_DECREF(py_callback); - delete ctx; + python_timer_del(ctx); Py_RETURN_NONE; } return PyCObject_FromVoidPtr(ctx, NULL); @@ -89,12 +108,11 @@ static PyObject *py_unregister_timer(PyObject *py_timerctx) Py_RETURN_FALSE; py_timer_ctx_t *ctx = (py_timer_ctx_t *) PyCObject_AsVoidPtr(py_timerctx); - if ( !unregister_timer(ctx->timer_id) ) + if ( ctx == NULL || !unregister_timer(ctx->timer_id) ) Py_RETURN_FALSE; - Py_DECREF(ctx->pycallback); - delete ctx; - + python_timer_del(ctx); + PyCObject_SetVoidPtr(py_timerctx, NULL); Py_RETURN_TRUE; } @@ -693,8 +711,6 @@ def execute_sync(callable, reqf): 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 @@ -823,9 +839,9 @@ static bool py_execute_ui_requests(PyObject *py_list) bool init(PyObject *py_list) { Py_ssize_t count = pyvar_walk_list( - py_list, - s_py_list_walk_cb, - this); + py_list, + s_py_list_walk_cb, + this); return count > 0; } @@ -1065,31 +1081,6 @@ public: } // hookgenUI:methods -virtual void range() {} -virtual void idcstart() {} -virtual void idcstop() {} -virtual void suspend() {} -virtual void resume() {} -virtual void saving() {} -virtual void saved() {} -virtual void term() {} -virtual int debugger_menu_change(bool enable) {qnotused(enable); return 1;} -virtual void tform_visible(TForm * form, void * HWND) {qnotused(form); qnotused(HWND); } -virtual void tform_invisible(TForm * form, void * HWND) {qnotused(form); qnotused(HWND); } -virtual PyObject * get_ea_hint(ea_t ea) {qnotused(ea); Py_RETURN_NONE;} -virtual PyObject * get_item_hint(ea_t ea, int max_lines) {qnotused(ea); qnotused(max_lines); Py_RETURN_NONE;} -virtual PyObject * get_custom_viewer_hint(TCustomControl* viewer, place_t * place) {qnotused(viewer); qnotused(place); Py_RETURN_NONE;} -virtual void database_inited(int is_new_database, const char * idc_script) {qnotused(is_new_database); qnotused(idc_script); } -virtual void ready_to_run() {} -virtual void preprocess(const char * name) {qnotused(name); } -virtual void postprocess() {} -virtual void updating_actions(action_update_ctx_t * ctx) {qnotused(ctx); } -virtual void updated_actions() {} -virtual void populating_tform_popup(TForm * form, TPopupMenu * popup_handle) {qnotused(form); qnotused(popup_handle); } -virtual void finish_populating_tform_popup(TForm * form, TPopupMenu * popup_handle) {qnotused(form); qnotused(popup_handle); } -virtual void plugin_loaded(const plugin_info_t * plugin_info) {qnotused(plugin_info); } -virtual void plugin_unloading(const plugin_info_t * plugin_info) {qnotused(plugin_info); } -virtual void current_tform_changed(TForm * form, TForm * prev_form) {qnotused(form); qnotused(prev_form); } }; //------------------------------------------------------------------------- @@ -1123,6 +1114,15 @@ bool py_unregister_action(const char *name) return ok; } +//------------------------------------------------------------------------- +PyObject *py_get_registered_actions() +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t actions; + get_registered_actions(&actions); + return qstrvec2pylist(actions); +} + //------------------------------------------------------------------------- bool py_attach_dynamic_action_to_popup( TForm *form, @@ -1277,11 +1277,12 @@ nav_colorizer_t *py_set_nav_colorizer(PyObject *new_py_colorizer) } }; - bool need_install = py_colorizer == NULL; + // Always perform the call to set_nav_colorizer(): that has side-effects + // (e.g., updating the legend.) + bool first_install = py_colorizer == NULL; py_colorizer = borref_t(new_py_colorizer); - return need_install - ? set_nav_colorizer(lambda_t::call_py_colorizer) - : NULL; + nav_colorizer_t *prev = set_nav_colorizer(lambda_t::call_py_colorizer); + return first_install ? prev : NULL; } //------------------------------------------------------------------------- @@ -1321,187 +1322,6 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) switch ( notification_code ) { // hookgenUI:notifications -case ui_range: -{ - proxy->range(); -} -break; - -case ui_idcstart: -{ - proxy->idcstart(); -} -break; - -case ui_idcstop: -{ - proxy->idcstop(); -} -break; - -case ui_suspend: -{ - proxy->suspend(); -} -break; - -case ui_resume: -{ - proxy->resume(); -} -break; - -case ui_saving: -{ - proxy->saving(); -} -break; - -case ui_saved: -{ - proxy->saved(); -} -break; - -case ui_term: -{ - proxy->term(); -} -break; - -case ui_debugger_menu_change: -{ - bool enable = bool(va_arg(va, int)); - ret = proxy->debugger_menu_change(enable); -} -break; - -case ui_tform_visible: -{ - TForm * form = va_arg(va, TForm *); - void * HWND = va_arg(va, void *); - proxy->tform_visible(form, HWND); -} -break; - -case ui_tform_invisible: -{ - TForm * form = va_arg(va, TForm *); - void * HWND = va_arg(va, void *); - proxy->tform_invisible(form, HWND); -} -break; - -case ui_get_ea_hint: -{ - ea_t ea = va_arg(va, ea_t); - char * buf = va_arg(va, char *); - size_t bufsize = va_arg(va, size_t); - PyObject * _tmp = proxy->get_ea_hint(ea); - ret = UI_Hooks::handle_get_ea_hint_output(_tmp, ea, buf, bufsize); -} -break; - -case ui_get_item_hint: -{ - ea_t ea = va_arg(va, ea_t); - int max_lines = va_arg(va, int); - int * important_lines = va_arg(va, int *); - qstring * hint = va_arg(va, qstring *); - PyObject * _tmp = proxy->get_item_hint(ea, max_lines); - ret = UI_Hooks::handle_hint_output(_tmp, ea, max_lines, important_lines, hint); -} -break; - -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 *); - PyObject * _tmp = proxy->get_custom_viewer_hint(viewer, place); - ret = UI_Hooks::handle_hint_output(_tmp, viewer, place, important_lines, hint); -} -break; - -case ui_database_inited: -{ - int is_new_database = va_arg(va, int); - const char * idc_script = va_arg(va, const char *); - proxy->database_inited(is_new_database, idc_script); -} -break; - -case ui_ready_to_run: -{ - proxy->ready_to_run(); -} -break; - -case ui_preprocess: -{ - const char * name = va_arg(va, const char *); - proxy->preprocess(name); -} -break; - -case ui_postprocess: -{ - proxy->postprocess(); -} -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_handle = va_arg(va, TPopupMenu *); - proxy->populating_tform_popup(form, popup_handle); -} -break; - -case ui_finish_populating_tform_popup: -{ - TForm * form = va_arg(va, TForm *); - TPopupMenu * popup_handle = va_arg(va, TPopupMenu *); - proxy->finish_populating_tform_popup(form, popup_handle); -} -break; - -case ui_plugin_loaded: -{ - const plugin_info_t * plugin_info = va_arg(va, const plugin_info_t *); - proxy->plugin_loaded(plugin_info); -} -break; - -case ui_plugin_unloading: -{ - const plugin_info_t * plugin_info = va_arg(va, const plugin_info_t *); - proxy->plugin_unloading(plugin_info); -} -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; - } } catch (Swig::DirectorException &e) diff --git a/pywraps/py_kernwin.py b/pywraps/py_kernwin.py index cdaa267..7fa5876 100755 --- a/pywraps/py_kernwin.py +++ b/pywraps/py_kernwin.py @@ -1,25 +1,3 @@ -# ----------------------------------------------------------------------- -# 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 @@ -50,15 +28,15 @@ def load_custom_icon(file_name=None, data=None, format=None): Use free_custom_icon() to free it """ if file_name is not None: - return _idaapi.py_load_custom_icon_fn(file_name) + return _ida_kernwin.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) + return _ida_kernwin.py_load_custom_icon_data(data, format) else: return 0 # ---------------------------------------------------------------------- def asklong(defval, format): - res, val = _idaapi._asklong(defval, format) + res, val = _ida_kernwin._asklong(defval, format) if res == 1: return val @@ -67,7 +45,7 @@ def asklong(defval, format): # ---------------------------------------------------------------------- def askaddr(defval, format): - res, ea = _idaapi._askaddr(defval, format) + res, ea = _ida_kernwin._askaddr(defval, format) if res == 1: return ea @@ -76,7 +54,7 @@ def askaddr(defval, format): # ---------------------------------------------------------------------- def askseg(defval, format): - res, sel = _idaapi._askseg(defval, format) + res, sel = _ida_kernwin._askseg(defval, format) if res == 1: return sel @@ -84,7 +62,7 @@ def askseg(defval, format): return None # ---------------------------------------------------------------------- -class action_handler_t: +class action_handler_t(object): def __init__(self): pass @@ -95,27 +73,3 @@ class action_handler_t: 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_askusingform.hpp b/pywraps/py_kernwin_askusingform.hpp similarity index 90% rename from pywraps/py_askusingform.hpp rename to pywraps/py_kernwin_askusingform.hpp index f69ea58..76262e1 100644 --- a/pywraps/py_askusingform.hpp +++ b/pywraps/py_kernwin_askusingform.hpp @@ -1,25 +1,11 @@ -#ifndef __PY_ASKUSINGFORM__ -#define __PY_ASKUSINGFORM__ +#ifndef __PY_KERNWIN_ASKUSINGFORM__ +#define __PY_KERNWIN_ASKUSINGFORM__ -// -static qvector py_compiled_form_vec; -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; //--------------------------------------------------------------------------- @@ -384,18 +370,15 @@ static size_t py_get_OpenForm() 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); + PyW_register_compiled_form(py_form); } 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); + PyW_unregister_compiled_form(py_form); } +// -// -#endif // __PY_ASKUSINGFORM__ +#endif // __PY_KERNWIN_ASKUSINGFORM__ + diff --git a/pywraps/py_askusingform.py b/pywraps/py_kernwin_askusingform.py similarity index 91% rename from pywraps/py_askusingform.py rename to pywraps/py_kernwin_askusingform.py index f4a8e04..1f7b3c5 100755 --- a/pywraps/py_askusingform.py +++ b/pywraps/py_kernwin_askusingform.py @@ -1,74 +1,8 @@ # -------------------------------------------------------------------------- -import os -import gc -import sys +# +import ida_idaapi, _ida_idaapi +import ida_pro -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 @@ -98,7 +32,7 @@ except: # ----------------------------------------------------------------------- # textctrl_info_t clinked object -class textctrl_info_t(py_clinked_object_t): +class textctrl_info_t(ida_idaapi.py_clinked_object_t): """Class representing textctrl_info_t""" # Some constants @@ -116,7 +50,7 @@ class textctrl_info_t(py_clinked_object_t): """The control uses IDA's fixed font""" def __init__(self, text="", flags=0, tabsize=0): - py_clinked_object_t.__init__(self) + ida_idaapi.py_clinked_object_t.__init__(self) if text: self.text = text if flags: @@ -125,41 +59,41 @@ class textctrl_info_t(py_clinked_object_t): self.tabsize = tabsize def _create_clink(self): - return _idaapi.textctrl_info_t_create() + return _ida_kernwin.textctrl_info_t_create() def _del_clink(self, lnk): - return _idaapi.textctrl_info_t_destroy(lnk) + return _ida_kernwin.textctrl_info_t_destroy(lnk) def _get_clink_ptr(self): - return _idaapi.textctrl_info_t_get_clink_ptr(self) + return _ida_kernwin.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) + return _ida_kernwin.textctrl_info_t_assign(self, other) def __set_text(self, s): """Sets the text value""" - return _idaapi.textctrl_info_t_set_text(self, s) + return _ida_kernwin.textctrl_info_t_set_text(self, s) def __get_text(self): """Sets the text value""" - return _idaapi.textctrl_info_t_get_text(self) + return _ida_kernwin.textctrl_info_t_get_text(self) def __set_flags__(self, flags): """Sets the flags value""" - return _idaapi.textctrl_info_t_set_flags(self, flags) + return _ida_kernwin.textctrl_info_t_set_flags(self, flags) def __get_flags__(self): """Returns the flags value""" - return _idaapi.textctrl_info_t_get_flags(self) + return _ida_kernwin.textctrl_info_t_get_flags(self) def __set_tabsize__(self, tabsize): """Sets the tabsize value""" - return _idaapi.textctrl_info_t_set_tabsize(self, tabsize) + return _ida_kernwin.textctrl_info_t_set_tabsize(self, tabsize) def __get_tabsize__(self): """Returns the tabsize value""" - return _idaapi.textctrl_info_t_get_tabsize(self) + return _ida_kernwin.textctrl_info_t_get_tabsize(self) value = property(__get_text, __set_text) """Alias for the text property""" @@ -218,6 +152,8 @@ class Form(object): """Multi text control - textctrl_info_t""" FT_DROPDOWN_LIST = 'b' """Dropdown list control - Form.DropdownControl""" + FT_HTML_LABEL = 'h' + """HTML label to display (only for GUI version, and for dynamic labels; no input)""" FT_CHKGRP = 'C' FT_CHKGRP2= 'c' @@ -726,7 +662,7 @@ class Form(object): # 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) + emb, sel = _ida_kernwin.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)) @@ -751,7 +687,7 @@ class Form(object): def AddCommand(self, caption, - flags = _idaapi.CHOOSER_POPUP_MENU, + flags = _ida_kernwin.CHOOSER_POPUP_MENU, menu_index = -1, icon = -1): """ @@ -777,7 +713,7 @@ class Form(object): Form.Control.free(self) - class DropdownListControl(InputControl, _qstrvec_t): + class DropdownListControl(InputControl, ida_pro._qstrvec_t): """ Dropdown control This control allows manipulating a dropdown control @@ -804,7 +740,7 @@ class Form(object): hlp) # Init the associated qstrvec - _qstrvec_t.__init__(self, items) + ida_pro._qstrvec_t.__init__(self, items) # Remember if readonly or not self.readonly = readonly @@ -815,7 +751,7 @@ class Form(object): val_addr = addressof(self.__selval) else: # Create an strvec with one qstring - self.__selval = _qstrvec_t([selval]) + self.__selval = ida_pro._qstrvec_t([selval]) # Get address of the first element val_addr = self.__selval.addressof(0) @@ -934,7 +870,7 @@ class Form(object): self._reset() # Unregister, so we don't try and free it again at closing-time. - _idaapi.py_unregister_compiled_form(self) + _ida_kernwin.py_unregister_compiled_form(self) def _reset(self): @@ -1180,7 +1116,7 @@ class Form(object): 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) + _ida_kernwin.py_register_compiled_form(self) return (self, self.__args) @@ -1227,7 +1163,7 @@ class Form(object): Enable or disable an input field @return: False - no such control """ - return _idaapi.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) + return _ida_kernwin.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) def ShowField(self, ctrl, show): @@ -1235,7 +1171,7 @@ class Form(object): Show or hide an input field @return: False - no such control """ - return _idaapi.formchgcbfa_show_field(self.p_fa, ctrl.id, show) + return _ida_kernwin.formchgcbfa_show_field(self.p_fa, ctrl.id, show) def MoveField(self, ctrl, x, y, w, h): @@ -1244,7 +1180,7 @@ class Form(object): @return: False - no such fiel """ - return _idaapi.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) + return _ida_kernwin.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) def GetFocusedField(self): @@ -1252,7 +1188,7 @@ class Form(object): 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) + id = _ida_kernwin.formchgcbfa_get_focused_field(self.p_fa) return self.FindControlById(id) @@ -1261,7 +1197,7 @@ class Form(object): Set currently focused input field @return: False - no such control """ - return _idaapi.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) + return _ida_kernwin.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) def RefreshField(self, ctrl): @@ -1269,7 +1205,7 @@ class Form(object): Refresh a field @return: False - no such control """ - return _idaapi.formchgcbfa_refresh_field(self.p_fa, ctrl.id) + return _ida_kernwin.formchgcbfa_refresh_field(self.p_fa, ctrl.id) def Close(self, close_normally): @@ -1280,7 +1216,7 @@ class Form(object): 0: form is closed abnormally as if the user pressed Esc @return: None """ - return _idaapi.formchgcbfa_close(self.p_fa, close_normally) + return _ida_kernwin.formchgcbfa_close(self.p_fa, close_normally) def GetControlValue(self, ctrl): @@ -1296,7 +1232,7 @@ class Form(object): - None: on failure """ tid, sz = self.ControlToFieldTypeIdAndSize(ctrl) - r = _idaapi.formchgcbfa_get_field_value( + r = _ida_kernwin.formchgcbfa_get_field_value( self.p_fa, ctrl.id, tid, @@ -1320,7 +1256,7 @@ class Form(object): @return: Boolean true on success """ tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) - return _idaapi.formchgcbfa_set_field_value( + return _ida_kernwin.formchgcbfa_set_field_value( self.p_fa, ctrl.id, tid, @@ -1346,7 +1282,7 @@ class Form(object): elif isinstance(ctrl, (Form.GroupItemControl, Form.GroupControl)): return (2, 0) elif isinstance(ctrl, Form.StringLabel): - return (3, min(_idaapi.MAXSTR, ctrl.size)) + return (3, min(_ida_kernwin.MAXSTR, ctrl.size)) elif isinstance(ctrl, Form.ColorInput): return (4, 0) elif isinstance(ctrl, Form.NumericInput): @@ -1362,9 +1298,9 @@ class Form(object): 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()) + Form.NumericArgument.DefI64 = _ida_idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL + AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_ida_kernwin.py_get_AskUsingForm()) + OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_ida_kernwin.py_get_OpenForm()) except: def AskUsingForm__(*args): warning("AskUsingForm() needs ctypes library in order to work") @@ -1379,9 +1315,9 @@ def AskUsingForm(*args): @param: Compiled Arguments obtain through the Form.Compile() function @return: 1 = ok, 0 = cancel """ - old = set_script_timeout(0) + old = _ida_idaapi.set_script_timeout(0) r = AskUsingForm__(*args) - set_script_timeout(old) + _ida_idaapi.set_script_timeout(old) return r def OpenForm(*args): @@ -1389,9 +1325,7 @@ def OpenForm(*args): Calls OpenForm() @param: Compiled Arguments obtain through the Form.Compile() function """ - old = set_script_timeout(0) + old = _ida_idaapi.set_script_timeout(0) r = OpenForm__(*args) - set_script_timeout(old) - - -# + _ida_idaapi.set_script_timeout(old) +# diff --git a/pywraps/py_choose.hpp b/pywraps/py_kernwin_choose.hpp similarity index 92% rename from pywraps/py_choose.hpp rename to pywraps/py_kernwin_choose.hpp index 369d77e..bbce7a0 100644 --- a/pywraps/py_choose.hpp +++ b/pywraps/py_kernwin_choose.hpp @@ -1,8 +1,8 @@ -#ifndef __PY_CHOOSE__ -#define __PY_CHOOSE__ +#ifndef __PY_KERNWIN_CHOOSE__ +#define __PY_KERNWIN_CHOOSE__ //--------------------------------------------------------------------------- -// +// //--------------------------------------------------------------------------- uint32 idaapi choose_sizer(void *self) { @@ -73,6 +73,6 @@ uint32 choose_choose( return r; } -// +// -#endif // __PY_CHOOSE__ +#endif // __PY_KERNWIN_CHOOSE__ diff --git a/pywraps/py_choose.py b/pywraps/py_kernwin_choose.py similarity index 86% rename from pywraps/py_choose.py rename to pywraps/py_kernwin_choose.py index 2cde81a..3924660 100644 --- a/pywraps/py_choose.py +++ b/pywraps/py_kernwin_choose.py @@ -1,6 +1,8 @@ -# -class Choose: +# +import _ida_idaapi + +class Choose(object): """ Choose - class for choose() with callbacks """ @@ -70,8 +72,8 @@ class Choose: """ choose - Display the choose dialogue """ - old = set_script_timeout(0) - n = _idaapi.choose_choose( + old = _ida_idaapi.set_script_timeout(0) + n = _ida_kernwin.choose_choose( self, self.flags, self.x0, @@ -81,6 +83,6 @@ class Choose: self.width, self.deflt, self.icon) - set_script_timeout(old) + _ida_idaapi.set_script_timeout(old) return n -# +# diff --git a/pywraps/py_choose2.hpp b/pywraps/py_kernwin_choose2.hpp similarity index 96% rename from pywraps/py_choose2.hpp rename to pywraps/py_kernwin_choose2.hpp index 6b6693b..a5733d2 100644 --- a/pywraps/py_choose2.hpp +++ b/pywraps/py_kernwin_choose2.hpp @@ -1,7 +1,7 @@ -#ifndef __PY_CHOOSE2__ -#define __PY_CHOOSE2__ +#ifndef __PY_KERNWIN_CHOOSE2__ +#define __PY_KERNWIN_CHOOSE2__ -// +// //------------------------------------------------------------------------ // Some defines @@ -100,9 +100,6 @@ private: //------------------------------------------------------------------------ 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; @@ -112,6 +109,9 @@ private: if ( obj != chooser_obj ) return 0; + // We will treat the demand. + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; 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); @@ -181,7 +181,7 @@ private: if ( popup_names == NULL ) return; - for ( int i=0; i=0; i-- ) + for ( int i=ncols-1; i >= 0; i-- ) line_arr[i][0] = '\0'; // Call Python @@ -236,7 +236,7 @@ private: return; // Go over the List returned by Python and convert to C strings - for ( int i=ncols-1; i>=0; i-- ) + for ( int i=ncols-1; i >= 0; i-- ) { borref_t item(PyList_GetItem(list.result.o, Py_ssize_t(i))); if ( item == NULL ) @@ -434,9 +434,7 @@ private: 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; @@ -564,7 +562,7 @@ public: // Get cols caption and widthes cols.qclear(); - for ( int i=0; iget_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); @@ -883,7 +881,19 @@ 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); -// + +PyObject *py_get_chooser_data(const char *chooser_caption, uint32 n) +{ + qstrvec_t data; + if ( !get_chooser_data(&data, chooser_caption, n) ) + Py_RETURN_NONE; + PyObject *py_list = PyList_New(data.size()); + for ( size_t i = 0; i < data.size(); ++i ) + PyList_SetItem(py_list, i, PyString_FromString(data[i].c_str())); + return py_list; +} + +// //--------------------------------------------------------------------------- // Testing functions. They belong to PyWraps and won't be copied to IDAPython @@ -899,4 +909,4 @@ static size_t choose2_get_test_embedded() { return (size_t)choose2_test_embedded; } -#endif // __PY_CHOOSE2__ +#endif // __PY_KERNWIN_CHOOSE2__ diff --git a/pywraps/py_choose2.py b/pywraps/py_kernwin_choose2.py similarity index 78% rename from pywraps/py_choose2.py rename to pywraps/py_kernwin_choose2.py index ceb3745..2c949f7 100755 --- a/pywraps/py_choose2.py +++ b/pywraps/py_kernwin_choose2.py @@ -1,41 +1,5 @@ # ----------------------------------------------------------------------- -# 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. @@ -105,7 +69,7 @@ class Choose2(object): Creates an embedded chooser (as opposed to Show()) @return: Returns 1 on success """ - return _idaapi.choose2_create(self, True) + return _ida_kernwin.choose2_create(self, True) def GetEmbSelection(self): @@ -116,7 +80,7 @@ class Choose2(object): - None if chooser is not embedded - A list with selection indices (0-based) """ - return _idaapi.choose2_get_embedded_selection(self) + return _ida_kernwin.choose2_get_embedded_selection(self) def Show(self, modal=False): @@ -129,9 +93,9 @@ class Choose2(object): 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) + old = _ida_idaapi.set_script_timeout(0) + n = _ida_kernwin.choose2_create(self, False) + _ida_idaapi.set_script_timeout(old) # Delete the modal chooser instance self.Close() @@ -139,27 +103,27 @@ class Choose2(object): return n else: self.flags &= ~Choose2.CH_MODAL - return _idaapi.choose2_create(self, False) + return _ida_kernwin.choose2_create(self, False) def Activate(self): """Activates a visible chooser""" - return _idaapi.choose2_activate(self) + return _ida_kernwin.choose2_activate(self) def Refresh(self): """Causes the refresh callback to trigger""" - return _idaapi.choose2_refresh(self) + return _ida_kernwin.choose2_refresh(self) def Close(self): """Closes the chooser""" - return _idaapi.choose2_close(self) + return _ida_kernwin.choose2_close(self) def AddCommand(self, caption, - flags = _idaapi.CHOOSER_POPUP_MENU, + flags = _ida_kernwin.CHOOSER_POPUP_MENU, menu_index = -1, icon = -1, emb=None): @@ -172,7 +136,7 @@ class Choose2(object): # 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) + return _ida_kernwin.choose2_add_command(self, caption, flags, menu_index, icon) # # Implement these methods in the subclass: @@ -273,4 +237,4 @@ class Choose2(object): # """ # return [0x0, CHITEM_BOLD] # -# +# diff --git a/pywraps/py_cli.hpp b/pywraps/py_kernwin_cli.hpp similarity index 98% rename from pywraps/py_cli.hpp rename to pywraps/py_kernwin_cli.hpp index 16b4650..ba78a1f 100644 --- a/pywraps/py_cli.hpp +++ b/pywraps/py_kernwin_cli.hpp @@ -1,7 +1,7 @@ #ifndef __PYWRAPS_CLI__ #define __PYWRAPS_CLI__ -// +// //-------------------------------------------------------------------------- #define MAX_PY_CLI 12 @@ -276,11 +276,11 @@ const py_cli_cbs_t py_cli_t::py_cli_cbs[MAX_PY_CLI] = 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); @@ -290,6 +290,6 @@ 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_kernwin_cli.py similarity index 80% rename from pywraps/py_cli.py rename to pywraps/py_kernwin_cli.py index 085874c..205490c 100755 --- a/pywraps/py_cli.py +++ b/pywraps/py_kernwin_cli.py @@ -1,28 +1,8 @@ # ----------------------------------------------------------------------- -# Standalone and testing code -import sys -try: - import pywraps - pywraps_there = True - print "Using pywraps" -except: - pywraps_there = False - print "Using IDAPython" +# +import ida_idaapi -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): +class cli_t(ida_idaapi.pyidc_opaque_object_t): """ cli_t wrapper class. @@ -55,7 +35,7 @@ class cli_t(pyidc_opaque_object_t): if hint is not None: self.hint = hint # Register - self.__cli_idx = _idaapi.install_command_interpreter(self) + self.__cli_idx = _ida_kernwin.install_command_interpreter(self) return False if self.__cli_idx < 0 else True @@ -66,7 +46,7 @@ class cli_t(pyidc_opaque_object_t): if self.__cli_idx < 0: return False - _idaapi.remove_command_interpreter(self.__cli_idx) + _ida_kernwin.remove_command_interpreter(self.__cli_idx) self.__cli_idx = -1 return True @@ -125,4 +105,4 @@ class cli_t(pyidc_opaque_object_t): # return None # -# +# diff --git a/pywraps/py_custview.hpp b/pywraps/py_kernwin_custview.hpp similarity index 99% rename from pywraps/py_custview.hpp rename to pywraps/py_kernwin_custview.hpp index 0a7b83c..0de6ea2 100644 --- a/pywraps/py_custview.hpp +++ b/pywraps/py_kernwin_custview.hpp @@ -1,6 +1,6 @@ #ifndef __PYWRAPS_CUSTVIEWER__ #define __PYWRAPS_CUSTVIEWER__ -// +// //--------------------------------------------------------------------------- // Base class for all custviewer place_t providers class custviewer_data_t @@ -826,7 +826,7 @@ public: }; PYW_GIL_CHECK_LOCKED_SCOPE(); - for ( size_t i=0; i +// //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- -// +// // // Pywraps Simple Custom Viewer functions // @@ -1128,6 +1128,6 @@ TCustomControl *pyscv_get_tcustom_control(PyObject *py_this) #undef DECL_THIS -// +// //--------------------------------------------------------------------------- #endif // __PYWRAPS_CUSTVIEWER__ diff --git a/pywraps/py_custview.py b/pywraps/py_kernwin_custview.py similarity index 67% rename from pywraps/py_custview.py rename to pywraps/py_kernwin_custview.py index c97c70f..35c18ea 100755 --- a/pywraps/py_custview.py +++ b/pywraps/py_kernwin_custview.py @@ -1,46 +1,5 @@ # ----------------------------------------------------------------------- -# 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): @@ -49,7 +8,7 @@ class simplecustviewer_t(object): def __del__(self): """Destructor. It also frees the associated C++ object""" try: - _idaapi.pyscv_delete(self.__this) + _ida_kernwin.pyscv_delete(self.__this) except: pass @@ -66,7 +25,7 @@ class simplecustviewer_t(object): In this case better close existing windows """ self.title = title - self.__this = _idaapi.pyscv_init(self, title) + self.__this = _ida_kernwin.pyscv_init(self, title) return True if self.__this else False def Close(self): @@ -76,25 +35,25 @@ class simplecustviewer_t(object): Show() can be called and it will call Create() internally. @return: Boolean """ - return _idaapi.pyscv_close(self.__this) + return _ida_kernwin.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) + return _ida_kernwin.pyscv_show(self.__this) def Refresh(self): - return _idaapi.pyscv_refresh(self.__this) + return _ida_kernwin.pyscv_refresh(self.__this) def RefreshCurrent(self): """Refreshes the current line only""" - return _idaapi.pyscv_refresh_current(self.__this) + return _ida_kernwin.pyscv_refresh_current(self.__this) def Count(self): """Returns the number of lines in the view""" - return _idaapi.pyscv_count(self.__this) + return _ida_kernwin.pyscv_count(self.__this) def GetSelection(self): """ @@ -103,43 +62,43 @@ class simplecustviewer_t(object): - tuple(x1, y1, x2, y2) - None if no selection """ - return _idaapi.pyscv_get_selection(self.__this) + return _ida_kernwin.pyscv_get_selection(self.__this) def ClearLines(self): """Clears all the lines""" - _idaapi.pyscv_clear_lines(self.__this) + _ida_kernwin.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)) + return _ida_kernwin.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)) + return _ida_kernwin.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)) + return _ida_kernwin.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) + return _ida_kernwin.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) + return _ida_kernwin.pyscv_del_line(self.__this, lineno) def GetLine(self, lineno): """ @@ -148,7 +107,7 @@ class simplecustviewer_t(object): @return: Returns a tuple (colored_line, fgcolor, bgcolor) or None """ - return _idaapi.pyscv_get_line(self.__this, lineno) + return _ida_kernwin.pyscv_get_line(self.__this, lineno) def GetCurrentWord(self, mouse = 0): """ @@ -156,7 +115,7 @@ class simplecustviewer_t(object): @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) + return _ida_kernwin.pyscv_get_current_word(self.__this, mouse) def GetCurrentLine(self, mouse = 0, notags = 0): """ @@ -165,7 +124,7 @@ class simplecustviewer_t(object): @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) + return _ida_kernwin.pyscv_get_current_line(self.__this, mouse, notags) def GetPos(self, mouse = 0): """ @@ -173,7 +132,7 @@ class simplecustviewer_t(object): @param mouse: return mouse position @return: Returns a tuple (lineno, x, y) """ - return _idaapi.pyscv_get_pos(self.__this, mouse) + return _ida_kernwin.pyscv_get_pos(self.__this, mouse) def GetLineNo(self, mouse = 0): """Calls GetPos() and returns the current line number or -1 on failure""" @@ -181,16 +140,16 @@ class simplecustviewer_t(object): 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) + return _ida_kernwin.pyscv_jumpto(self.__this, lineno, x, y) def AddPopupMenu(self, title, hotkey=""): """ - Adds a popup menu item + Adds a popup menu item, and get its ID (for use in the OnPopupMenu() handler) @param title: The name of the menu item @param hotkey: Hotkey of the item or just empty - @return: Returns the + @return: Returns the id of the registered popup menu item """ - return _idaapi.pyscv_add_popup_menu(self.__this, title, hotkey) + return _ida_kernwin.pyscv_add_popup_menu(self.__this, title, hotkey) def ClearPopupMenu(self): """ @@ -198,11 +157,11 @@ class simplecustviewer_t(object): 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) + _ida_kernwin.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) + return _ida_kernwin.pyscv_is_focused(self.__this) def GetTForm(self): """ @@ -210,7 +169,7 @@ class simplecustviewer_t(object): @return: The TForm that hosts this view, or None. """ - return _idaapi.pyscv_get_tform(self.__this) + return _ida_kernwin.pyscv_get_tform(self.__this) def GetTCustomControl(self): """ @@ -218,7 +177,7 @@ class simplecustviewer_t(object): @return: The TCustomControl underlying this view, or None. """ - return _idaapi.pyscv_get_tcustom_control(self.__this) + return _ida_kernwin.pyscv_get_tcustom_control(self.__this) @@ -292,4 +251,4 @@ class simplecustviewer_t(object): # print "OnPopupMenu, menu_id=" % menu_id # return True # -# +# diff --git a/pywraps/py_kernwin_idaview.hpp b/pywraps/py_kernwin_idaview.hpp new file mode 100644 index 0000000..b2f5d8b --- /dev/null +++ b/pywraps/py_kernwin_idaview.hpp @@ -0,0 +1,92 @@ + +// +//------------------------------------------------------------------------- +// py_idaview_t +//------------------------------------------------------------------------- +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 ( pycim_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_entry_t &e = pycim_lookup_info.new_entry(py_view); + pycim_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); +} +// + +// +bool pyidag_bind(PyObject *self); +bool pyidag_unbind(PyObject *self); +// diff --git a/pywraps/py_kernwin_idaview.py b/pywraps/py_kernwin_idaview.py new file mode 100644 index 0000000..b009f57 --- /dev/null +++ b/pywraps/py_kernwin_idaview.py @@ -0,0 +1,25 @@ + +# +#------------------------------------------------------------------------- +# IDAViewWrapper +#------------------------------------------------------------------------- +import ida_idaapi +import _ida_kernwin +class IDAViewWrapper(ida_idaapi.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 _ida_kernwin.pyidag_bind(self) + + def Unbind(self): + return _ida_kernwin.pyidag_unbind(self) + +# diff --git a/pywraps/py_plgform.hpp b/pywraps/py_kernwin_plgform.hpp similarity index 93% rename from pywraps/py_plgform.hpp rename to pywraps/py_kernwin_plgform.hpp index 7538511..30bf74b 100644 --- a/pywraps/py_plgform.hpp +++ b/pywraps/py_kernwin_plgform.hpp @@ -1,7 +1,7 @@ -#ifndef __PY_PLGFORM__ -#define __PY_PLGFORM__ +#ifndef __PY_KERNWIN_PLGFORM__ +#define __PY_KERNWIN_PLGFORM__ -// +// //--------------------------------------------------------------------------- class plgform_t { @@ -124,9 +124,9 @@ public: 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() @@ -152,6 +152,6 @@ static void plgform_close( plgform->close(options); } #undef DECL_PLGFORM -// +// -#endif // __PY_PLGFORM__ +#endif // __PY_KERNWIN_PLGFORM__ diff --git a/pywraps/py_plgform.py b/pywraps/py_kernwin_plgform.py similarity index 93% rename from pywraps/py_plgform.py rename to pywraps/py_kernwin_plgform.py index fe47a1a..a3d070b 100755 --- a/pywraps/py_plgform.py +++ b/pywraps/py_kernwin_plgform.py @@ -1,6 +1,5 @@ -import _idaapi - -# +# +import sys class PluginForm(object): """ PluginForm class. @@ -27,7 +26,7 @@ class PluginForm(object): def __init__(self): """ """ - self.__clink__ = _idaapi.plgform_new() + self.__clink__ = _ida_kernwin.plgform_new() @@ -39,7 +38,7 @@ class PluginForm(object): @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) + return _ida_kernwin.plgform_show(self.__clink__, self, caption, options) @staticmethod @@ -112,7 +111,7 @@ class PluginForm(object): @return: None """ - return _idaapi.plgform_close(self.__clink__, options) + return _ida_kernwin.plgform_close(self.__clink__, options) FORM_SAVE = 0x1 """Save state in desktop config""" @@ -125,7 +124,7 @@ class PluginForm(object): 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_lines.hpp b/pywraps/py_lines.hpp index 2661a34..21ae2a1 100644 --- a/pywraps/py_lines.hpp +++ b/pywraps/py_lines.hpp @@ -114,23 +114,31 @@ 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 ) + // Can't call tag_remove() with a size of 0; debug builds will INTERR 1270 + if ( sz > 0 ) { - Py_INCREF(Py_None); - res = Py_None; + 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; } else { - res = PyString_FromString(buf); + return PyString_FromString(""); } - delete [] buf; - return res; } //------------------------------------------------------------------------- @@ -194,7 +202,7 @@ PyObject *py_generate_disassembly( int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); newref_t py_tuple(PyTuple_New(nlines)); - for ( int i=0; i +import _ida_idaapi # ---------------- Color escape sequence defitions ------------------------- -COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 +COLOR_ADDR_SIZE = 16 if _ida_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 @@ -12,7 +13,7 @@ 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) +PALETTE_SIZE = (cvar.COLOR_FG_MAX+_ida_lines.COLOR_BG_MAX) def requires_color_esc(c): """ diff --git a/pywraps/py_nalt.hpp b/pywraps/py_nalt.hpp index 15be50f..28744be 100644 --- a/pywraps/py_nalt.hpp +++ b/pywraps/py_nalt.hpp @@ -39,18 +39,6 @@ static int idaapi py_import_enum_cb( 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; -} // // @@ -94,7 +82,7 @@ PyObject *py_get_switch_info_ex(ea_t ea) 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 ) + || (py_obj = create_linked_class_instance(S_IDA_NALT_MODNAME, S_PY_SWIEX_CLSNAME, ex)) == NULL ) { delete ex; Py_RETURN_NONE; @@ -103,114 +91,6 @@ PyObject *py_get_switch_info_ex(ea_t ea) 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; -} - //------------------------------------------------------------------------- /* # diff --git a/pywraps/py_nalt.py b/pywraps/py_nalt.py index a27c5a9..08e323b 100755 --- a/pywraps/py_nalt.py +++ b/pywraps/py_nalt.py @@ -1,4 +1,6 @@ # +import _ida_idaapi + SWI_SPARSE = 0x1 """sparse switch ( value table present ) otherwise lowcase present""" SWI_V32 = 0x2 @@ -42,18 +44,18 @@ SWI2_SUBTRACT = 0x0002 """table values are subtracted from the elbase instead of being addded""" # -------------------------------------------------------------------------- -class switch_info_ex_t(py_clinked_object_t): +class switch_info_ex_t(ida_idaapi.py_clinked_object_t): def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) + ida_idaapi.py_clinked_object_t.__init__(self, lnk) def _create_clink(self): - return _idaapi.switch_info_ex_t_create() + return _ida_nalt.switch_info_ex_t_create() def _del_clink(self, lnk): - return _idaapi.switch_info_ex_t_destroy(lnk) + return _ida_nalt.switch_info_ex_t_destroy(lnk) def assign(self, other): - return _idaapi.switch_info_ex_t_assign(self, other) + return _ida_nalt.switch_info_ex_t_assign(self, other) def is_indirect(self): return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_INDIRECT) != 0 @@ -112,57 +114,57 @@ class switch_info_ex_t(py_clinked_object_t): # Autogenerated # def __get_regdtyp__(self): - return _idaapi.switch_info_ex_t_get_regdtyp(self) + return _ida_nalt.switch_info_ex_t_get_regdtyp(self) def __set_regdtyp__(self, v): - _idaapi.switch_info_ex_t_set_regdtyp(self, v) + _ida_nalt.switch_info_ex_t_set_regdtyp(self, v) def __get_flags2__(self): - return _idaapi.switch_info_ex_t_get_flags2(self) + return _ida_nalt.switch_info_ex_t_get_flags2(self) def __set_flags2__(self, v): - _idaapi.switch_info_ex_t_set_flags2(self, v) + _ida_nalt.switch_info_ex_t_set_flags2(self, v) def __get_jcases__(self): - return _idaapi.switch_info_ex_t_get_jcases(self) + return _ida_nalt.switch_info_ex_t_get_jcases(self) def __set_jcases__(self, v): - _idaapi.switch_info_ex_t_set_jcases(self, v) + _ida_nalt.switch_info_ex_t_set_jcases(self, v) def __get_regnum__(self): - return _idaapi.switch_info_ex_t_get_regnum(self) + return _ida_nalt.switch_info_ex_t_get_regnum(self) def __set_regnum__(self, v): - _idaapi.switch_info_ex_t_set_regnum(self, v) + _ida_nalt.switch_info_ex_t_set_regnum(self, v) def __get_flags__(self): - return _idaapi.switch_info_ex_t_get_flags(self) + return _ida_nalt.switch_info_ex_t_get_flags(self) def __set_flags__(self, v): - _idaapi.switch_info_ex_t_set_flags(self, v) + _ida_nalt.switch_info_ex_t_set_flags(self, v) def __get_ncases__(self): - return _idaapi.switch_info_ex_t_get_ncases(self) + return _ida_nalt.switch_info_ex_t_get_ncases(self) def __set_ncases__(self, v): - _idaapi.switch_info_ex_t_set_ncases(self, v) + _ida_nalt.switch_info_ex_t_set_ncases(self, v) def __get_defjump__(self): - return _idaapi.switch_info_ex_t_get_defjump(self) + return _ida_nalt.switch_info_ex_t_get_defjump(self) def __set_defjump__(self, v): - _idaapi.switch_info_ex_t_set_defjump(self, v) + _ida_nalt.switch_info_ex_t_set_defjump(self, v) def __get_jumps__(self): - return _idaapi.switch_info_ex_t_get_jumps(self) + return _ida_nalt.switch_info_ex_t_get_jumps(self) def __set_jumps__(self, v): - _idaapi.switch_info_ex_t_set_jumps(self, v) + _ida_nalt.switch_info_ex_t_set_jumps(self, v) def __get_elbase__(self): - return _idaapi.switch_info_ex_t_get_elbase(self) + return _ida_nalt.switch_info_ex_t_get_elbase(self) def __set_elbase__(self, v): - _idaapi.switch_info_ex_t_set_elbase(self, v) + _ida_nalt.switch_info_ex_t_set_elbase(self, v) def __get_startea__(self): - return _idaapi.switch_info_ex_t_get_startea(self) + return _ida_nalt.switch_info_ex_t_get_startea(self) def __set_startea__(self, v): - _idaapi.switch_info_ex_t_set_startea(self, v) + _ida_nalt.switch_info_ex_t_set_startea(self, v) def __get_custom__(self): - return _idaapi.switch_info_ex_t_get_custom(self) + return _ida_nalt.switch_info_ex_t_get_custom(self) def __set_custom__(self, v): - _idaapi.switch_info_ex_t_set_custom(self, v) + _ida_nalt.switch_info_ex_t_set_custom(self, v) def __get_ind_lowcase__(self): - return _idaapi.switch_info_ex_t_get_ind_lowcase(self) + return _ida_nalt.switch_info_ex_t_get_ind_lowcase(self) def __set_ind_lowcase__(self, v): - _idaapi.switch_info_ex_t_set_ind_lowcase(self, v) + _ida_nalt.switch_info_ex_t_set_ind_lowcase(self, v) def __get_values_lowcase__(self): - return _idaapi.switch_info_ex_t_get_values_lowcase(self) + return _ida_nalt.switch_info_ex_t_get_values_lowcase(self) def __set_values_lowcase__(self, v): - _idaapi.switch_info_ex_t_set_values_lowcase(self, v) + _ida_nalt.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__) @@ -188,4 +190,4 @@ class switch_info_ex_t(py_clinked_object_t): 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 c2cdda5..4da0238 100644 --- a/pywraps/py_name.hpp +++ b/pywraps/py_name.hpp @@ -16,7 +16,7 @@ PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) PyObject *dict = Py_BuildValue("{}"); if ( dict != NULL ) { - for ( ea_name_vec_t::iterator it=names.begin(); it!=names.end(); ++it ) + for ( ea_name_vec_t::iterator it=names.begin(); it != names.end(); ++it ) { PyDict_SetItem(dict, Py_BuildValue(PY_FMT64, it->ea), diff --git a/pywraps/py_name.py b/pywraps/py_name.py index 0cf43cb..9fbbd8c 100644 --- a/pywraps/py_name.py +++ b/pywraps/py_name.py @@ -2,7 +2,7 @@ import bisect # -class NearestName: +class NearestName(object): """ Utility class to help find the nearest name in a given ea/name dictionary """ diff --git a/pywraps/py_pro.hpp b/pywraps/py_pro.hpp new file mode 100644 index 0000000..f67a448 --- /dev/null +++ b/pywraps/py_pro.hpp @@ -0,0 +1,106 @@ +// +//--------------------------------------------------------------------------- +// 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(size_t(&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; +} +// diff --git a/pywraps/py_pro.py b/pywraps/py_pro.py new file mode 100644 index 0000000..ffc870e --- /dev/null +++ b/pywraps/py_pro.py @@ -0,0 +1,87 @@ + +# +import ida_idaapi +ida_idaapi._listify_types( + uvalvec_t, + intvec_t, + int64vec_t, + boolvec_t, + strvec_t) + +# ----------------------------------------------------------------------- +# qstrvec_t clinked object +class _qstrvec_t(ida_idaapi.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): + ida_idaapi.py_clinked_object_t.__init__(self) + # Populate the list if needed + if items: + self.from_list(items) + + def _create_clink(self): + return _ida_pro.qstrvec_t_create() + + def _del_clink(self, lnk): + return _ida_pro.qstrvec_t_destroy(lnk) + + def _get_clink_ptr(self): + return _ida_pro.qstrvec_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _ida_pro.qstrvec_t_assign(self, other) + + def __setitem__(self, idx, s): + """Sets string at the given index""" + return _ida_pro.qstrvec_t_set(self, idx, s) + + def __getitem__(self, idx): + """Gets the string at the given index""" + return _ida_pro.qstrvec_t_get(self, idx) + + def __get_size(self): + return _ida_pro.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 _ida_pro.qstrvec_t_addressof(self, idx) + + def add(self, s): + """Add a string to the vector""" + return _ida_pro.qstrvec_t_add(self, s) + + def from_list(self, lst): + """Populates the vector from a Python string list""" + return _ida_pro.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 _ida_pro.qstrvec_t_clear(self, qclear) + + def insert(self, idx, s): + """Insert a string into the vector""" + return _ida_pro.qstrvec_t_insert(self, idx, s) + + def remove(self, idx): + """Removes a string from the vector""" + return _ida_pro.qstrvec_t_remove(self, idx) + +# diff --git a/pywraps/py_typeinf.hpp b/pywraps/py_typeinf.hpp index 9441b2f..61788de 100644 --- a/pywraps/py_typeinf.hpp +++ b/pywraps/py_typeinf.hpp @@ -669,82 +669,88 @@ til_t *load_til_header_wrap(const char *tildir, const char *name) PyErr_SetString(PyExc_RuntimeError, errbuf); return res; } + +//------------------------------------------------------------------------- +/* +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; + } +} // // //------------------------------------------------------------------------- -// 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) +inline const p_list * PyW_Fields(PyObject *tp) { - // 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 + return tp == Py_None ? NULL : (const p_list *) PyString_AsString(tp); } -#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 +//------------------------------------------------------------------------- +// tuple(type_str, fields_str, field_cmts) on success +static PyObject *py_tinfo_t_serialize( + const tinfo_t *tif, + int sudt_flags) +{ + qtype type, fields, fldcmts; + if ( !tif->serialize(&type, &fields, &fldcmts, sudt_flags) ) + Py_RETURN_NONE; + PyObject *tuple = PyTuple_New(3); + int ctr = 0; +#define ADD(Thing) \ + do \ + { \ + PyObject *o = Py_None; \ + if ( (Thing).empty() ) \ + Py_INCREF(Py_None); \ + else \ + o = PyString_FromString((const char *) (Thing).begin()); \ + PyTuple_SetItem(tuple, ctr, o); \ + ++ctr; \ + } while ( false ) + ADD(type); + ADD(fields); + ADD(fldcmts); +#undef ADD + return tuple; +} // diff --git a/pywraps/py_typeinf.py b/pywraps/py_typeinf.py index c8bcc68..9bd7676 100644 --- a/pywraps/py_typeinf.py +++ b/pywraps/py_typeinf.py @@ -12,4 +12,8 @@ def get_type_size0(ti, tp): """ return calc_type_size(ti, tp) +import ida_idaapi +ida_idaapi._listify_types( + reginfovec_t) + # diff --git a/pywraps/py_ua.hpp b/pywraps/py_ua.hpp index 556121d..566fa3e 100644 --- a/pywraps/py_ua.hpp +++ b/pywraps/py_ua.hpp @@ -3,23 +3,10 @@ //------------------------------------------------------------------------- // -//------------------------------------------------------------------------- -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): @@ -101,109 +88,6 @@ flags_t py_OutValue(PyObject *x, int outflags=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; - } -} - //------------------------------------------------------------------------- /* # diff --git a/pywraps/py_ua.py b/pywraps/py_ua.py index ccee1f3..d9c8ead 100644 --- a/pywraps/py_ua.py +++ b/pywraps/py_ua.py @@ -1,20 +1,21 @@ # +import ida_idaapi # ----------------------------------------------------------------------- -class op_t(py_clinked_object_t): +class op_t(ida_idaapi.py_clinked_object_t): """Class representing operands""" def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) + ida_idaapi.py_clinked_object_t.__init__(self, lnk) def _create_clink(self): - return _idaapi.op_t_create() + return _ida_ua.op_t_create() def _del_clink(self, lnk): - return _idaapi.op_t_destroy(lnk) + return _ida_ua.op_t_destroy(lnk) def assign(self, other): """Copies the contents of 'other' to 'self'""" - return _idaapi.op_t_assign(self, other) + return _ida_ua.op_t_assign(self, other) def __eq__(self, other): """Checks if two register operands are equal by checking the register number and its dtype""" @@ -32,61 +33,61 @@ class op_t(py_clinked_object_t): # Autogenerated # def __get_n__(self): - return _idaapi.op_t_get_n(self) + return _ida_ua.op_t_get_n(self) def __set_n__(self, v): - _idaapi.op_t_set_n(self, v) + _ida_ua.op_t_set_n(self, v) def __get_type__(self): - return _idaapi.op_t_get_type(self) + return _ida_ua.op_t_get_type(self) def __set_type__(self, v): - _idaapi.op_t_set_type(self, v) + _ida_ua.op_t_set_type(self, v) def __get_offb__(self): - return _idaapi.op_t_get_offb(self) + return _ida_ua.op_t_get_offb(self) def __set_offb__(self, v): - _idaapi.op_t_set_offb(self, v) + _ida_ua.op_t_set_offb(self, v) def __get_offo__(self): - return _idaapi.op_t_get_offo(self) + return _ida_ua.op_t_get_offo(self) def __set_offo__(self, v): - _idaapi.op_t_set_offo(self, v) + _ida_ua.op_t_set_offo(self, v) def __get_flags__(self): - return _idaapi.op_t_get_flags(self) + return _ida_ua.op_t_get_flags(self) def __set_flags__(self, v): - _idaapi.op_t_set_flags(self, v) + _ida_ua.op_t_set_flags(self, v) def __get_dtyp__(self): - return _idaapi.op_t_get_dtyp(self) + return _ida_ua.op_t_get_dtyp(self) def __set_dtyp__(self, v): - _idaapi.op_t_set_dtyp(self, v) + _ida_ua.op_t_set_dtyp(self, v) def __get_reg_phrase__(self): - return _idaapi.op_t_get_reg_phrase(self) + return _ida_ua.op_t_get_reg_phrase(self) def __set_reg_phrase__(self, v): - _idaapi.op_t_set_reg_phrase(self, v) + _ida_ua.op_t_set_reg_phrase(self, v) def __get_value__(self): - return _idaapi.op_t_get_value(self) + return _ida_ua.op_t_get_value(self) def __set_value__(self, v): - _idaapi.op_t_set_value(self, v) + _ida_ua.op_t_set_value(self, v) def __get_addr__(self): - return _idaapi.op_t_get_addr(self) + return _ida_ua.op_t_get_addr(self) def __set_addr__(self, v): - _idaapi.op_t_set_addr(self, v) + _ida_ua.op_t_set_addr(self, v) def __get_specval__(self): - return _idaapi.op_t_get_specval(self) + return _ida_ua.op_t_get_specval(self) def __set_specval__(self, v): - _idaapi.op_t_set_specval(self, v) + _ida_ua.op_t_set_specval(self, v) def __get_specflag1__(self): - return _idaapi.op_t_get_specflag1(self) + return _ida_ua.op_t_get_specflag1(self) def __set_specflag1__(self, v): - _idaapi.op_t_set_specflag1(self, v) + _ida_ua.op_t_set_specflag1(self, v) def __get_specflag2__(self): - return _idaapi.op_t_get_specflag2(self) + return _ida_ua.op_t_get_specflag2(self) def __set_specflag2__(self, v): - _idaapi.op_t_set_specflag2(self, v) + _ida_ua.op_t_set_specflag2(self, v) def __get_specflag3__(self): - return _idaapi.op_t_get_specflag3(self) + return _ida_ua.op_t_get_specflag3(self) def __set_specflag3__(self, v): - _idaapi.op_t_set_specflag3(self, v) + _ida_ua.op_t_set_specflag3(self, v) def __get_specflag4__(self): - return _idaapi.op_t_get_specflag4(self) + return _ida_ua.op_t_get_specflag4(self) def __set_specflag4__(self, v): - _idaapi.op_t_set_specflag4(self, v) + _ida_ua.op_t_set_specflag4(self, v) n = property(__get_n__, __set_n__) type = property(__get_type__, __set_type__) @@ -104,11 +105,18 @@ class op_t(py_clinked_object_t): specflag3 = property(__get_specflag3__, __set_specflag3__) specflag4 = property(__get_specflag4__, __set_specflag4__) +# ---------------------------------------------------------------------- +# +# Misc constants +# +UA_MAXOP = 6 +"""The maximum number of operands in the insn_t structure""" + # ----------------------------------------------------------------------- -class insn_t(py_clinked_object_t): +class insn_t(ida_idaapi.py_clinked_object_t): """Class representing instructions""" def __init__(self, lnk = None): - py_clinked_object_t.__init__(self, lnk) + ida_idaapi.py_clinked_object_t.__init__(self, lnk) # Create linked operands self.Operands = [] @@ -125,7 +133,7 @@ class insn_t(py_clinked_object_t): def assign(self, other): """Copies the contents of 'other' to 'self'""" - return _idaapi.insn_t_assign(self, other) + return _ida_ua.insn_t_assign(self, other) # # def copy(self): @@ -134,11 +142,11 @@ class insn_t(py_clinked_object_t): # def _create_clink(self): - return _idaapi.insn_t_create() + return _ida_ua.insn_t_create() def _del_clink(self, lnk): - return _idaapi.insn_t_destroy(lnk) + return _ida_ua.insn_t_destroy(lnk) def __iter__(self): @@ -159,53 +167,53 @@ class insn_t(py_clinked_object_t): return self.flags & INSN_MACRO != 0 def is_canon_insn(self): - return _idaapi.insn_t_is_canon_insn(self.itype) + return _ida_ua.insn_t_is_canon_insn(self.itype) def get_canon_feature(self): - return _idaapi.insn_t_get_canon_feature(self.itype) + return _ida_ua.insn_t_get_canon_feature(self.itype) def get_canon_mnem(self): - return _idaapi.insn_t_get_canon_mnem(self.itype) + return _ida_ua.insn_t_get_canon_mnem(self.itype) # # Autogenerated # def __get_cs__(self): - return _idaapi.insn_t_get_cs(self) + return _ida_ua.insn_t_get_cs(self) def __set_cs__(self, v): - _idaapi.insn_t_set_cs(self, v) + _ida_ua.insn_t_set_cs(self, v) def __get_ip__(self): - return _idaapi.insn_t_get_ip(self) + return _ida_ua.insn_t_get_ip(self) def __set_ip__(self, v): - _idaapi.insn_t_set_ip(self, v) + _ida_ua.insn_t_set_ip(self, v) def __get_ea__(self): - return _idaapi.insn_t_get_ea(self) + return _ida_ua.insn_t_get_ea(self) def __set_ea__(self, v): - _idaapi.insn_t_set_ea(self, v) + _ida_ua.insn_t_set_ea(self, v) def __get_itype__(self): - return _idaapi.insn_t_get_itype(self) + return _ida_ua.insn_t_get_itype(self) def __set_itype__(self, v): - _idaapi.insn_t_set_itype(self, v) + _ida_ua.insn_t_set_itype(self, v) def __get_size__(self): - return _idaapi.insn_t_get_size(self) + return _ida_ua.insn_t_get_size(self) def __set_size__(self, v): - _idaapi.insn_t_set_size(self, v) + _ida_ua.insn_t_set_size(self, v) def __get_auxpref__(self): - return _idaapi.insn_t_get_auxpref(self) + return _ida_ua.insn_t_get_auxpref(self) def __set_auxpref__(self, v): - _idaapi.insn_t_set_auxpref(self, v) + _ida_ua.insn_t_set_auxpref(self, v) def __get_segpref__(self): - return _idaapi.insn_t_get_segpref(self) + return _ida_ua.insn_t_get_segpref(self) def __set_segpref__(self, v): - _idaapi.insn_t_set_segpref(self, v) + _ida_ua.insn_t_set_segpref(self, v) def __get_insnpref__(self): - return _idaapi.insn_t_get_insnpref(self) + return _ida_ua.insn_t_get_insnpref(self) def __set_insnpref__(self, v): - _idaapi.insn_t_set_insnpref(self, v) + _ida_ua.insn_t_set_insnpref(self, v) def __get_flags__(self): - return _idaapi.insn_t_get_flags(self) + return _ida_ua.insn_t_get_flags(self) def __set_flags__(self, v): - _idaapi.insn_t_set_flags(self, v) + _ida_ua.insn_t_set_flags(self, v) cs = property(__get_cs__, __set_cs__) ip = property(__get_ip__, __set_ip__) @@ -218,186 +226,11 @@ class insn_t(py_clinked_object_t): 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 = insn_t(_ida_ua.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 @@ -458,64 +291,4 @@ OF_SHOW = 0x08 # should the operand be displayed? if clear, the operand is hidd 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/pywraps/py_view.hpp b/pywraps/py_view.hpp deleted file mode 100644 index 4d78738..0000000 --- a/pywraps/py_view.hpp +++ /dev/null @@ -1,1052 +0,0 @@ - -// -//------------------------------------------------------------------------- -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; - - 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); -} - -//------------------------------------------------------------------------- -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); - } -} - -//------------------------------------------------------------------------- -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); - } -} - -//------------------------------------------------------------------------- -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; - -//------------------------------------------------------------------------- -// py_idaview_t -//------------------------------------------------------------------------- -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); -} -// - -// -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); - -//------------------------------------------------------------------------- -// py_idaview_t -//------------------------------------------------------------------------- -bool pyidag_bind(PyObject *self); -bool pyidag_unbind(PyObject *self); -// diff --git a/pywraps/py_view.py b/pywraps/py_view.py deleted file mode 100644 index b2d22d5..0000000 --- a/pywraps/py_view.py +++ /dev/null @@ -1,144 +0,0 @@ - -# -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) - - -#------------------------------------------------------------------------- -# IDAViewWrapper -#------------------------------------------------------------------------- -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/pywraps/py_xref.hpp b/pywraps/py_xref.hpp new file mode 100644 index 0000000..c5fcdbd --- /dev/null +++ b/pywraps/py_xref.hpp @@ -0,0 +1,118 @@ + +//------------------------------------------------------------------------- +// + +// important for SWiG to generate properly-wrapped vector classes +typedef qvector svalvec_t; +typedef qvector casevec_t; +typedef qvector eavec_t; +// + +/* +# +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; +} +// diff --git a/pywraps/py_xref.py b/pywraps/py_xref.py new file mode 100644 index 0000000..fb91ec4 --- /dev/null +++ b/pywraps/py_xref.py @@ -0,0 +1,8 @@ + +# + +import ida_idaapi +ida_idaapi._listify_types( + casevec_t) + +# diff --git a/pywraps/pywraps.vcxproj b/pywraps/pywraps.vcxproj deleted file mode 100644 index f14921d..0000000 --- a/pywraps/pywraps.vcxproj +++ /dev/null @@ -1,663 +0,0 @@ - - - - - Debug - Win32 - - - Rel64 - Win32 - - - Release64 - Win32 - - - Release - Win32 - - - SemiDebugx64 - Win32 - - - SemiDebug - Win32 - - - - {F43D6BB8-B7D6-486A-82E5-BABBA9848525} - pywraps - - - - DynamicLibrary - false - MultiByte - - - DynamicLibrary - false - MultiByte - - - DynamicLibrary - false - MultiByte - - - DynamicLibrary - false - MultiByte - - - DynamicLibrary - false - MultiByte - - - DynamicLibrary - false - MultiByte - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - .\Debug\ - .\Debug\ - true - false - .\Release\ - .\Release\ - false - $(Configuration)\ - $(Configuration)\ - false - $(Configuration)\ - $(Configuration)\ - false - $(Configuration)\ - $(Configuration)\ - false - $(Configuration)\ - $(Configuration)\ - false - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/pywraps.tlb - - - - - Disabled - ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) - _DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;__PYWRAPS__;_PYWRAPS_DEBUG;NO_OBSOLETE_FUNCS;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\Debug/pywraps.pch - .\Debug/ - .\Debug/ - .\Debug/ - Level3 - true - EditAndContinue - Cdecl - 4005;4800;4018;%(DisableSpecificWarnings) - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;python26_d.lib;%(AdditionalDependencies) - ../../../bin/x86_win_vc/plugins/pywraps.plw - true - ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) - true - .\Debug/pywraps.pdb - false - - - .\Debug/pywraps.lib - MachineX86 - - - true - .\Debug/pywraps.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/pywraps.tlb - - - - - Disabled - OnlyExplicitInline - ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) - NO_OBSOLETE_FUNCS;NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/pywraps.pch - .\Release/ - .\Release/ - .\Release/ - Level4 - true - ProgramDatabase - Cdecl - - - NDEBUG;%(PreprocessorDefinitions) - 0x0419 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;python26.lib;%(AdditionalDependencies) - ../../../bin/x86_win_vc/plugins/pywraps.plw - true - ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) - true - .\Release/pywraps.pdb - - - - - .\Release/pywraps.lib - MachineX86 - - - true - .\Release/pywraps.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/pywraps.tlb - - - - - Disabled - OnlyExplicitInline - ..\..\..\include;c:\Python26\include;%(AdditionalIncludeDirectories) - NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;_PYWRAPS_DEBUG;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/pywraps.pch - .\Release/ - .\Release/ - .\Release/ - Level3 - true - ProgramDatabase - Cdecl - 4005;4800;4018;%(DisableSpecificWarnings) - - - NDEBUG;%(PreprocessorDefinitions) - 0x0419 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;python26.lib;%(AdditionalDependencies) - ../../../bin/x86_win_vc/plugins/pywraps.plw - true - ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) - true - .\Release/pywraps.pdb - - - - - .\Release/pywraps.lib - MachineX86 - - - true - .\Release/pywraps.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/pywraps.tlb - - - - - Disabled - OnlyExplicitInline - ..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) - NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;_PYWRAPS_DEBUG;__EA64__;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/pywraps.pch - .\Release/ - .\Release/ - .\Release/ - Level3 - true - ProgramDatabase - Cdecl - 4005;4800;4018;%(DisableSpecificWarnings) - - - NDEBUG;%(PreprocessorDefinitions) - 0x0419 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;python26.lib;%(AdditionalDependencies) - ../../../bin/x86_win_vc/plugins/pywraps.p64 - true - ..\..\..\lib\vc.w64;c:\python26\libs;%(AdditionalLibraryDirectories) - true - .\Release/pywraps.pdb - false - - - true - .\Release/pywraps.lib - MachineX86 - - - true - .\Release/pywraps.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/pywraps.tlb - - - - - Disabled - OnlyExplicitInline - ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) - NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;__EA64__;_PYWRAPS_DEBUG;__PYWRAPS__;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/pywraps.pch - .\Release/ - .\Release/ - .\Release/ - Level4 - true - ProgramDatabase - Cdecl - - - NDEBUG;%(PreprocessorDefinitions) - 0x0419 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;python26.lib;%(AdditionalDependencies) - ../../../bin/x86_win_vc/plugins/pywraps.p64 - true - ..\..\..\lib\vc.w64;c:\python26\libs;%(AdditionalLibraryDirectories) - true - .\Release/pywraps.pdb - - - - - .\Release/pywraps.lib - MachineX86 - - - true - .\Release/pywraps.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/pywraps.tlb - - - - - Disabled - OnlyExplicitInline - ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) - NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/pywraps.pch - .\Release/ - .\Release/ - .\Release/ - Level4 - true - ProgramDatabase - Cdecl - - - NDEBUG;%(PreprocessorDefinitions) - 0x0419 - - - /export:PLUGIN %(AdditionalOptions) - ida.lib;python26.lib;%(AdditionalDependencies) - ../../../bin/x86_win_vc/plugins/pywraps.plw - true - ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) - true - .\Release/pywraps.pdb - - - - - .\Release/pywraps.lib - MachineX86 - - - true - .\Release/pywraps.bsc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - true - true - true - true - - - true - true - true - true - true - - - true - true - true - true - true - - - - true - true - true - true - true - - - true - true - true - true - true - - - true - true - true - - - true - true - true - true - true - true - - - true - true - true - true - true - true - - - true - true - true - true - true - true - - - true - true - true - true - true - true - - - true - true - true - true - true - true - - - true - true - true - - - true - true - true - true - true - true - - - true - true - true - true - true - true - - - - - - - - - - - - - - - - - - true - true - true - true - true - true - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pywraps/pywraps.vcxproj.filters b/pywraps/pywraps.vcxproj.filters deleted file mode 100644 index 5b14810..0000000 --- a/pywraps/pywraps.vcxproj.filters +++ /dev/null @@ -1,518 +0,0 @@ - - - - - {9fd646ab-4c34-4457-a88f-0b9efa521a7c} - cpp;c;cxx;rc;def;r;odl;idl;hpj;bat - - - {c7c2445a-144b-4574-a5a7-4547bf731f79} - - - {a817be2b-11e4-441c-9e1e-ff3825b03d03} - - - {bfbff292-8186-40a9-a90f-09a530e2e5d2} - - - {2ebfadc0-402b-4ef7-b2bf-ab9fb8c5279b} - - - {0ad19987-f808-44a4-865b-985e7f8dca02} - - - {1016cbe2-fa4e-49b5-9e8f-b8fc4a0656d3} - - - {e57b232e-0cdb-4cc6-ad63-80ecfeaa25b9} - - - {cfda7913-ffad-425d-b9ce-295bb647d8e5} - - - {ec48a898-4c2f-443d-9236-c8b58c036050} - - - {12e97676-9f57-4e1f-af42-6bf0cd178ae6} - - - {62308260-bfab-49eb-9b11-7522f7b671d8} - - - {f33949eb-15bb-4926-94e3-1648a4c3bca0} - - - {79a2a07e-7db7-43ac-8c41-fe08777e802c} - - - {714c10e9-2a6f-4b86-a594-b53b28f9fc7e} - - - {22f57328-eb90-4341-bb06-464f6c7b2bd6} - - - {ef594bc3-6670-4e55-a77f-e3bff3193d94} - - - {7092618c-44fe-497b-a229-360bab592f3d} - - - {c5616ba8-8b0d-41a0-bf78-583fdcfb3898} - - - {0cefa3d8-f4b6-4085-b2a2-6b2d97167252} - - - {b21f3b63-396e-4725-83cc-2df9f5b6bb46} - - - {c4a9b8be-70fa-4287-bc47-73db56202dd6} - - - {a103675f-abc2-4f84-a0b8-9ac7403f04db} - - - {ac94c213-e485-48ac-b554-3868e350d671} - - - {833c766e-6e8d-4c00-947f-71a7b34378a6} - - - {8dc47ef4-3da0-4bf8-9ce2-5a9f8896b1b3} - - - {8e65a873-5b8f-4052-a929-e13e9e0f81ac} - - - {b7a7d68d-a3b0-487c-b4d0-bd716dd06280} - - - {959b59d2-acb2-4c89-a828-eece77e502f4} - - - - - py_dbgidd - - - py_dbgidd - - - py_dbgidd - - - py_graph - - - py_graph - - - py_diskio - - - py_diskio - - - py_diskio - - - py_bytes - - - py_bytes - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - deploy\examples - - - py_idaapi - - - py_idaapi - - - py_typeinf - - - 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 - - - swig.i - - - swig.i - - - swig.i - - - swig.i - - - swig.i - - - py_gdl - - - py_gdl - - - py_ua - - - py_ua - - - IDAPython - - - IDAPython - - - IDAPython - - - IDAPython - - - py_notifywhen - - - py_lines - - - py_lines - - - py_nalt - - - py_nalt - - - py_loader - - - py_idp - - - py_kernwin - - - py_kernwin - - - py_kernwin\py_custview - - - py_kernwin\py_choose2 - - - py_kernwin\py_cli - - - py_kernwin\py_plgform - - - py_kernwin\py_askusingform - - - py_kernwin\py_askusingform - - - py_kernwin - - - py_kernwin\py_plgform - - - py_kernwin\py_plgform - - - py_idp - - - py_kernwin - - - py_expr - - - - - py_dbgidd - - - py_graph - - - pywraps - - - py_diskio - - - py_bytes - - - py_bytes - - - IDAPython - - - IDAPython - - - py_notifywhen - - - py_nalt - - - py_kernwin - - - py_kernwin\py_custview - - - py_kernwin\py_choose2 - - - py_kernwin\py_cli - - - swig_stub - - - py_expr - - - - - py_dbgidd - - - py_graph - - - pywraps - - - py_diskio - - - py_diskio - - - py_diskio - - - py_bytes - - - py_bytes - - - py_idaapi - - - py_typeinf - - - py_ua - - - py_notifywhen - - - py_lines - - - py_nalt - - - py_loader - - - py_idp - - - py_utils - - - py_kernwin - - - py_kernwin\py_custview - - - py_kernwin\py_choose2 - - - py_kernwin\py_cli - - - py_kernwin\py_plgform - - - py_kernwin\py_askusingform - - - py_kernwin\py_choose - - - swig_stub - - - py_expr - - - - - IDAPython - - - \ No newline at end of file diff --git a/pywraps/sidaapi.py b/pywraps/sidaapi.py deleted file mode 100755 index f3dbf47..0000000 --- a/pywraps/sidaapi.py +++ /dev/null @@ -1,228 +0,0 @@ -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/swig_stub.cpp b/pywraps/swig_stub.cpp new file mode 100644 index 0000000..e44b86b --- /dev/null +++ b/pywraps/swig_stub.cpp @@ -0,0 +1,6 @@ +#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 new file mode 100644 index 0000000..4752aeb --- /dev/null +++ b/pywraps/swig_stub.h @@ -0,0 +1,25 @@ +#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"; + } + }; +} + +// Some fake SWIG types +#define SWIGTYPE_p_member_t NULL + +#endif diff --git a/swig/allins.i b/swig/allins.i index 161878f..759533b 100644 --- a/swig/allins.i +++ b/swig/allins.i @@ -1,3 +1,7 @@ +%{ +#include +%} + // Ignore the unnedded externals %ignore Instructions; diff --git a/swig/area.i b/swig/area.i index 591bc51..adf220c 100644 --- a/swig/area.i +++ b/swig/area.i @@ -2,9 +2,6 @@ %ignore ANODE2; %ignore AREA_LONG_COMMENT_TAG; %ignore area_visitor_t; -%ignore areacb_t_link_dont_load; -%ignore add_area_from_cache; -%ignore areacb_t_valid_push_back; // Ignore the private members in areacb_t %ignore areacb_t::areasCode; @@ -37,4 +34,23 @@ %ignore areaset_t::move_chunk; %ignore areaset_t::check_move_args; +%inline %{ +// +// +%} + %include "area.hpp" + +%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; + } +} diff --git a/swig/askusingform.i b/swig/askusingform.i deleted file mode 100644 index 17f24e3..0000000 --- a/swig/askusingform.i +++ /dev/null @@ -1,17 +0,0 @@ - -// relies on kernwin.i to include kernwin.hpp - -%{ -// -// -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/auto.i b/swig/auto.i index 225683e..65ce048 100644 --- a/swig/auto.i +++ b/swig/auto.i @@ -1,17 +1,11 @@ -%ignore queue_weak_code; -%ignore auto_process_all; +%{ +#include +%} + %ignore autoPlanned; %ignore nextPlanned; -%ignore is_planned_ea; -%ignore get_next_planned_ea; -%ignore autoDelCode; %ignore autoPeek; %ignore autoProcess; -%ignore auto_init; -%ignore auto_save; -%ignore auto_term; -%ignore ea_without_xrefs; -%ignore postpone_lastinsn_analysis; %include "auto.hpp" diff --git a/swig/bytes.i b/swig/bytes.i index 0c3018d..1fdd7a6 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -1,13 +1,8 @@ -// Make get_any_cmt() work -%apply unsigned char *OUTPUT { color_t *cmttype }; +%{ +#include +%} -// 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 }; +%import "area.i" // Unexported and kernel-only declarations %ignore FlagsEnable; @@ -22,7 +17,6 @@ %ignore is_first_visea; %ignore is_last_visea; %ignore is_visible_finally; -%ignore invalidate_visea_cache; %ignore fluFlags; %ignore setFlbits; %ignore clrFlbits; @@ -36,44 +30,11 @@ %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_stroff_path; -%ignore del_stroff_path; -%ignore set_enum_id; -%ignore del_enum_id; -%ignore enum_encode; -%ignore enum_decode; -%ignore mark_dirty_tof; -%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 max_cache_count; -%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; @@ -126,3 +87,18 @@ # # %} + +%{ +// +// +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} diff --git a/swig/choose.i b/swig/choose.i deleted file mode 100644 index 08486fe..0000000 --- a/swig/choose.i +++ /dev/null @@ -1,12 +0,0 @@ - -// relies on kernwin.i to include kernwin.hpp - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/choose2.i b/swig/choose2.i deleted file mode 100644 index 199c87d..0000000 --- a/swig/choose2.i +++ /dev/null @@ -1,17 +0,0 @@ - -// relies on kernwin.i to include kernwin.hpp - -%{ -// -// -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/cli.i b/swig/cli.i deleted file mode 100644 index abef900..0000000 --- a/swig/cli.i +++ /dev/null @@ -1,17 +0,0 @@ - -// relies on kernwin.i to include kernwin.hpp - -%{ -// -// -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/custdata.i b/swig/custdata.i deleted file mode 100644 index 38de684..0000000 --- a/swig/custdata.i +++ /dev/null @@ -1,15 +0,0 @@ - -%{ -// -// -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/custview.i b/swig/custview.i deleted file mode 100644 index 778fa46..0000000 --- a/swig/custview.i +++ /dev/null @@ -1,17 +0,0 @@ - -// relies on kernwin.i to include kernwin.hpp - -%{ -// -// -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/dbg.i b/swig/dbg.i index 2929e96..58abf3a 100644 --- a/swig/dbg.i +++ b/swig/dbg.i @@ -1,16 +1,14 @@ -// 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; +%{ +#include +#include +%} + +%import "idd.i" %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; @@ -28,8 +26,8 @@ typedef struct %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; +// TODO: This could be fixed (if needed) +%ignore set_dbgmem_source; %rename (list_bptgrps) py_list_bptgrps; %apply qstring *result { qstring *grp_name }; @@ -41,10 +39,19 @@ typedef struct %ignore qvector::add_unique; %template(bpt_vec_t) qvector; +%ignore qvector::operator==; +%ignore qvector::operator!=; +%ignore qvector::find; +%ignore qvector::has; +%ignore qvector::del; +%ignore qvector::add_unique; +%ignore memreg_info_t::bytes; +%rename (bytes) memreg_info_t_py_bytes; +%template(memreg_infos_t) qvector; + %ignore internal_get_sreg_base; %rename (internal_get_sreg_base) py_internal_get_sreg_base; -%rename (get_tev_reg_mem) py_get_tev_reg_mem; // We want ALL wrappers around what is declared in dbg.hpp // to release the GIL when calling into the IDA api: those @@ -61,11 +68,15 @@ typedef struct // %} +//------------------------------------------------------------------------- +// bpt_t +//------------------------------------------------------------------------- %extend bpt_t { PyObject *condition; PyObject *elang; } + %{ PyObject *bpt_t_condition_get(bpt_t *bpt) { @@ -100,7 +111,28 @@ void bpt_t_elang_set(bpt_t *bpt, PyObject *val) } %} +//------------------------------------------------------------------------- +// memreg_info_t +//------------------------------------------------------------------------- +%extend memreg_info_t +{ + PyObject *get_bytes() const + { + return PyString_FromStringAndSize( + (const char *) $self->bytes.begin(), + $self->bytes.size()); + } + %pythoncode %{ + bytes = property(get_bytes) + %} +} + %inline %{ // // %} + +%pythoncode %{ +# +# +%} diff --git a/swig/diskio.i b/swig/diskio.i index 82cacf5..d675a63 100644 --- a/swig/diskio.i +++ b/swig/diskio.i @@ -1,8 +1,14 @@ +%{ +#include +#include +#include +#include "../../../pywraps.hpp" +%} + // 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; @@ -29,9 +35,6 @@ %ignore qlfile; %ignore make_linput; %ignore unmake_linput; -%ignore create_remote_linput; -%ignore make_filehandle_linput; -%ignore get_default_userdir; // FIXME: These should be wrapped for completeness %ignore eread; diff --git a/swig/entry.i b/swig/entry.i index bebc381..2878980 100644 --- a/swig/entry.i +++ b/swig/entry.i @@ -1,6 +1,5 @@ -%ignore init_entries; -%ignore term_entries; -%ignore move_entries; -%ignore set_entry_name; +%{ +#include +%} %include "entry.hpp" diff --git a/swig/enum.i b/swig/enum.i index 8d357f5..6da8938 100644 --- a/swig/enum.i +++ b/swig/enum.i @@ -1,17 +1,16 @@ +%{ +#include +%} // Kernel only & unexported symbols %ignore enums; %ignore init_enums; %ignore save_enums; %ignore term_enums; %ignore set_enum_flag; -%ignore sync_from_enum; %ignore get_enum_name(tid_t); -%ignore del_all_enum_members; %ignore get_selected_enum; %ignore add_selected_enum; %ignore unmark_selected_enums; -%ignore is_good_bmask; -%ignore get_bmask_enum; %ignore ENUM_REVERSE; %ignore ENUM_SELMEMS; %ignore ENUM_QTY_IDX; diff --git a/swig/expr.i b/swig/expr.i index 7407bf4..cb701e5 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -12,7 +12,6 @@ %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; @@ -35,25 +34,10 @@ %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; %ignore syntax_highlighter_t; +%ignore get_idptype_and_data; %cstring_output_maxstr_none(char *errbuf, size_t errbufsize); %ignore CompileEx; diff --git a/swig/fixup.i b/swig/fixup.i index 2d31d56..a06ba42 100644 --- a/swig/fixup.i +++ b/swig/fixup.i @@ -1,6 +1,8 @@ +%{ +#include +%} + %ignore apply_fixup; -%ignore convert_fixups; -%ignore move_fixups; %ignore custom_fixup_handler_t; %ignore custom_fixup_handlers_t; %ignore register_custom_fixup; diff --git a/swig/frame.i b/swig/frame.i index 47d9fc5..b76e32c 100644 --- a/swig/frame.i +++ b/swig/frame.i @@ -1,24 +1,15 @@ +%{ +#include +%} + +%import "area.i" + %ignore add_frame_spec_member; %ignore del_stkvars; %ignore calc_frame_offset; -%ignore read_regvars; -%ignore write_regvars; -%ignore del_regvars; -%ignore free_regvar; -%ignore gen_regvar_defs; %ignore set_llabel; %ignore get_llabel_ea; %ignore get_llabel; -%ignore read_llabels; -%ignore write_llabels; -%ignore del_llabels; -%ignore free_llabel; -%ignore read_stkpnts; -%ignore write_stkpnts; -%ignore del_stkpnts; -%ignore rename_frame; -%ignore _set_frame_size; -%ignore add_empty_frame; %ignore get_stkvar; %rename (get_stkvar) py_get_stkvar; @@ -31,4 +22,9 @@ %template(xreflist_t) qvector; +%inline %{ +// +// +%} + %include "frame.hpp" diff --git a/swig/funcs.i b/swig/funcs.i index 505fdd9..0678b01 100644 --- a/swig/funcs.i +++ b/swig/funcs.i @@ -1,3 +1,6 @@ + +%import "area.i" + %cstring_bounded_output_none(char *buf, MAXSTR); %cstring_bounded_output_none(char *optlibs, MAXSTR); @@ -6,27 +9,10 @@ %ignore get_idasgn_header_by_short_name; // Kernel-only & unexported symbols -%ignore del_regargs; -%ignore write_regargs; -%ignore find_regarg; -%ignore free_regarg; %ignore determine_rtl; -%ignore init_signatures; %ignore save_signatures; -%ignore term_signatures; -%ignore init_funcs; -%ignore save_funcs; -%ignore term_funcs; -%ignore move_funcs; -%ignore copy_noret_info; -%ignore recalc_func_noret_flag; -%ignore plan_for_noret_analysis; %ignore invalidate_sp_analysis; -%ignore create_func_eas_array; -%ignore auto_add_func_tails; -%ignore read_tails; - %ignore get_idasgn_desc; %rename (get_idasgn_desc) py_get_idasgn_desc; %rename (get_idasgn_desc_with_matches) py_get_idasgn_desc_with_matches; diff --git a/swig/gdl.i b/swig/gdl.i index bb975b4..3d27551 100644 --- a/swig/gdl.i +++ b/swig/gdl.i @@ -1,3 +1,9 @@ +%{ +#include +%} + +%import "area.i" + %ignore cancellable_graph_t; %ignore gdl_graph_t; diff --git a/swig/graph.i b/swig/graph.i index 6f59647..5cf8440 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -1,3 +1,6 @@ +%{ +#include +%} %ignore mutable_graph_t; %ignore graph_visitor_t; %ignore abstract_graph_t; diff --git a/swig/hexrays.i b/swig/hexrays.i index cc39bc5..e4aa107 100644 --- a/swig/hexrays.i +++ b/swig/hexrays.i @@ -1,3 +1,23 @@ +%{ +#include +%} + +%import "typeinf.i" + +// KLUDGE: I have no idea how to force SWiG to declare a type for a module, +// unless that type is indeed used. That's why this wrapper exists.. +%{ +static void _kludge_use_TPopupMenu(TPopupMenu *) {} +%} +%inline %{ +namespace Menus +{ + class TPopupMenu; +} +using Menus::TPopupMenu; +static void _kludge_use_TPopupMenu(TPopupMenu *m); +%} + //--------------------------------------------------------------------- // SWIG bindings for Hexray Decompiler's hexrays.hpp // @@ -143,6 +163,8 @@ public: CITEM_MEMBER_REF(return); CITEM_MEMBER_REF(goto); CITEM_MEMBER_REF(asm); + + bool is_epilog() const { return $self == INS_EPILOG; } }; #undef CITEM_MEMBER_REF @@ -320,346 +342,27 @@ void qswap(cinsn_t &a, cinsn_t &b); } %{ - -//------------------------------------------------------------------------- -qstring_printer_t *new_qstring_printer_t(const cfunc_t *f, bool tags) -{ - return new qstring_printer_t(f, * (new qstring()), tags); -} - -//------------------------------------------------------------------------- -void delete_qstring_printer_t(qstring_printer_t *qs) -{ - delete &(qs->s); - delete qs; -} - -//--------------------------------------------------------------------- -static int hexrays_python_call(ref_t fct, ref_t args) -{ - PYW_GIL_GET; - - newref_t resultobj(PyEval_CallObject(fct.o, args.o)); - if (PyErr_Occurred()) - { - PyErr_Print(); - return 0; - } - - int result; - if ( SWIG_IsOK(SWIG_AsVal_int(resultobj.o, &result)) ) - return result; - msg("IDAPython: Hex-rays python callback returned non-integer; value ignored.\n"); - return 0; -} - -//--------------------------------------------------------------------- -static bool idaapi __python_custom_viewer_popup_item_callback(void *ud) -{ - PYW_GIL_GET; - - int ret; - borref_t fct((PyObject *)ud); - newref_t nil(NULL); - ret = hexrays_python_call(fct, nil); - return ret ? true : false; -} - -//--------------------------------------------------------------------- -static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_list va) -{ - PYW_GIL_GET; - - int ret; - borref_t fct((PyObject *)ud); - switch(event) - { - case hxe_maturity: - ///< Ctree maturity level is being changed. - ///< cfunc_t *cfunc - ///< ctree_maturity_t new_maturity - { - cfunc_t *arg0 = va_arg(va, cfunc_t *); - ctree_maturity_t arg1 = va_argi(va, ctree_maturity_t); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); - newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_interr: - ///< Internal error has occurred. - ///< int errcode - { - int arg0 = va_argi(va, int); - newref_t args(Py_BuildValue("(ii)", event, arg0)); - ret = hexrays_python_call(fct, args); - } - break; - - case hxe_print_func: - ///< Printing ctree and generating text. - ///< cfunc_t *cfunc - ///< vc_printer_t *vp - ///< Returns: 1 if text has been generated by the plugin - { - cfunc_t *arg0 = va_arg(va, cfunc_t *); - vc_printer_t *arg1 = va_arg(va, vc_printer_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); - newref_t arg1obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, 0 )); - newref_t args(Py_BuildValue("(iOO)", event, arg0obj.o, arg1obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - - // User interface related events: - case hxe_open_pseudocode: - ///< New pseudocode view has been opened. - ///< vdui_t *vu - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_switch_pseudocode: - ///< Existing pseudocode view has been reloaded - ///< with a new function. Its text has not been - ///< refreshed yet, only cfunc and mba pointers are ready. - ///< vdui_t *vu - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_refresh_pseudocode: - ///< Existing pseudocode text has been refreshed. - ///< vdui_t *vu - ///< See also hxe_text_ready, which happens earlier - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_close_pseudocode: - ///< Pseudocode view is being closed. - ///< vdui_t *vu - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_keyboard: - ///< Keyboard has been hit. - ///< vdui_t *vu - ///< int key_code (VK_...) - ///< int shift_state - ///< Should return: 1 if the event has been handled - { - vdui_t *arg0 = va_arg(va, vdui_t *); - int arg1 = va_argi(va, int); - int arg2 = va_argi(va, int); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iOii)", event, arg0obj.o, arg1, arg2)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_right_click: - ///< Mouse right click. We can add menu items now. - ///< vdui_t *vu - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_double_click: - ///< Mouse double click. - ///< vdui_t *vu - ///< int shift_state - ///< Should return: 1 if the event has been handled - { - vdui_t *arg0 = va_arg(va, vdui_t *); - int arg1 = va_argi(va, int); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_curpos: - ///< Current cursor position has been changed. - ///< (for example, by left-clicking or using keyboard) - ///< vdui_t *vu - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_create_hint: - ///< Create a hint for the current item. - ///< vdui_t *vu - ///< qstring *result_hint - ///< int *implines - ///< Possible return values: - ///< 0: the event has not been handled - ///< 1: hint has been created (should set *implines to nonzero as well) - ///< 2: hint has been created but the standard hints must be - ///< appended by the decompiler - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_text_ready: - ///< Decompiled text is ready. - ///< vdui_t *vu - ///< This event can be used to modify the output text (sv). - ///< The text uses regular color codes (see lines.hpp) - ///< COLOR_ADDR is used to store pointers to ctree elements - { - vdui_t *arg0 = va_arg(va, vdui_t *); - newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); - newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); - ret = hexrays_python_call(fct, args); - } - break; - case hxe_populating_popup: - ///< Populating popup menu. We can add menu items now. - ///< TForm *form - ///< TPopupMenu *popup_handle - ///< vdui_t *vu - { - TForm *form = va_arg(va, TForm *); - TPopupMenu *pp = va_arg(va, TPopupMenu*); - vdui_t *vdui = va_arg(va, vdui_t *); - newref_t py_form(SWIG_NewPointerObj(SWIG_as_voidptr(form), SWIGTYPE_p_Forms__TForm, 0)); - newref_t py_popup(SWIG_NewPointerObj(SWIG_as_voidptr(pp), SWIGTYPE_p_Menus__TPopupMenu, 0)); - newref_t py_vdui(SWIG_NewPointerObj(SWIG_as_voidptr(vdui), SWIGTYPE_p_vdui_t, 0 )); - newref_t py_args(Py_BuildValue("(iOOO)", event, py_form.o, py_popup.o, py_vdui.o)); - ret = hexrays_python_call(fct, py_args); - } - break; - default: - //~ msg("IDAPython: Unknown event `%u' occured\n", event); - ret = 0; - break; - } - - return ret; -} - +// +// %} %ignore init_hexrays_plugin; -%rename(init_hexrays_plugin) __init_hexrays_plugin; +%rename(init_hexrays_plugin) py_init_hexrays_plugin; %ignore add_custom_viewer_popup_item; -%rename(add_custom_viewer_popup_item) __add_custom_viewer_popup_item; +%rename(add_custom_viewer_popup_item) py_add_custom_viewer_popup_item; %ignore install_hexrays_callback; -%rename(install_hexrays_callback) __install_hexrays_callback; +%rename(install_hexrays_callback) py_install_hexrays_callback; %ignore remove_hexrays_callback; -%rename(remove_hexrays_callback) __remove_hexrays_callback; +%rename(remove_hexrays_callback) py_remove_hexrays_callback; -%inline %{ +%ignore decompile_many; +%rename (decompile_many) py_decompile_many; -//--------------------------------------------------------------------- -extern hexdsp_t *hexdsp; -bool __init_hexrays_plugin(int flags=0) -{ - // Only initialize one time - if (hexdsp == NULL) - return init_hexrays_plugin(flags); - else - return true; -} - -//--------------------------------------------------------------------- -void __add_custom_viewer_popup_item( - TCustomControl *custom_viewer, - const char *title, - const char *hotkey, - PyObject *custom_viewer_popup_item_callback) -{ - PYW_GIL_GET; - Py_INCREF(custom_viewer_popup_item_callback); - add_custom_viewer_popup_item(custom_viewer, title, hotkey, __python_custom_viewer_popup_item_callback, custom_viewer_popup_item_callback); -}; - -//--------------------------------------------------------------------- -bool __install_hexrays_callback(PyObject *hx_cblist_callback) -{ - PYW_GIL_GET; - if (install_hexrays_callback(__hexrays_python_callback, hx_cblist_callback)) - { - Py_INCREF(hx_cblist_callback); - return true; - } - return false; -} - -//--------------------------------------------------------------------- -int __remove_hexrays_callback(PyObject *hx_cblist_callback) -{ - PYW_GIL_GET; - int result, i; - result = remove_hexrays_callback(__hexrays_python_callback, hx_cblist_callback); - for (i=0;i python_cfuncptrs; -void hexrays_clear_python_cfuncptr_t_references(void) -{ - for ( size_t i = 0, n = python_cfuncptrs.size(); i < n; ++i ) - python_cfuncptrs[i]->reset(); - // NOTE: Don't clear() the array of pointers. All the python-exposed - // cfuncptr_t instances will be deleted through the python - // shutdown/ref-decrementing process anyway, and the entries will be - // properly pulled out of the vector when that happens. -} - -void hexrays_register_python_cfuncptr_t_instance(cfuncptr_t *fp) -{ - QASSERT(30457, !python_cfuncptrs.has(fp)); - python_cfuncptrs.push_back(fp); -} - -void hexrays_deregister_python_cfuncptr_t_instance(cfuncptr_t *fp) -{ - qvector::iterator found = python_cfuncptrs.find(fp); - if ( found != python_cfuncptrs.end() ) - { - fp->reset(); - python_cfuncptrs.erase(found); - } -} - -%} +%ignore get_tform_vdui; +%rename (get_tform_vdui) py_get_tform_vdui; //------------------------------------------------------------------------- #if SWIG_VERSION == 0x20012 @@ -685,23 +388,6 @@ void hexrays_deregister_python_cfuncptr_t_instance(cfuncptr_t *fp) #error Ensure cfuncptr_t wrapping is compatible with this version of SWIG #endif -%{ -cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf) -{ - try - { - cfuncptr_t cfunc = decompile(pfn, hf); - return cfunc; - } - catch(...) - { - error("Hex-Rays Python: decompiler threw an exception.\n"); - } - return cfuncptr_t(0); -} -%} - -cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf); %ignore decompile; //--------------------------------------------------------------------- @@ -737,422 +423,23 @@ cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf); %enddef %possible_director_exc(ctree_visitor_t::apply_to) %possible_director_exc(ctree_visitor_t::apply_to_exprs) + +%inline %{ +// +// +%} + %include "hexrays.hpp" %exception; // Delete & restore handlers %exception_set_default_handlers(); %pythoncode %{ - -import idaapi - -hexrays_failure_t.__str__ = lambda self: str(self.str) - -# --------------------------------------------------------------------- -class DecompilationFailure(Exception): - """ Raised on a decompilation error. - - The associated hexrays_failure_t object is stored in the - 'info' member of this exception. """ - - def __init__(self, info): - Exception.__init__(self, 'Decompilation failed: %s' % (str(info), )) - self.info = info - return - -# --------------------------------------------------------------------- -def decompile(ea, hf=None): - if isinstance(ea, (int, long)): - func = idaapi.get_func(ea) - if not func: return - elif type(ea) == idaapi.func_t: - func = ea - else: - raise RuntimeError('arg 1 of decompile expects either ea_t or cfunc_t argument') - - if hf is None: - hf = hexrays_failure_t() - - ptr = _decompile(func, hf) - - if ptr.__deref__() is None: - raise DecompilationFailure(hf) - - return ptr - -# --------------------------------------------------------------------- -# stringify all string types -#qtype.__str__ = qtype.c_str -#qstring.__str__ = qstring.c_str -#citem_cmt_t.__str__ = citem_cmt_t.c_str - -# --------------------------------------------------------------------- -# listify all list types -_listify_types(cinsnptrvec_t, - ctree_items_t, - qvector_lvar_t, - qvector_carg_t, - qvector_ccase_t, - hexwarns_t, - history_t, - lvar_saved_infos_t) - -def citem_to_specific_type(self): - """ cast the citem_t object to its more specific type, either cexpr_t or cinsn_t. """ - - if self.op >= cot_empty and self.op <= cot_last: - return self.cexpr - elif self.op >= cit_empty and self.op < cit_end: - return self.cinsn - - raise RuntimeError('unknown op type %s' % (repr(self.op), )) -citem_t.to_specific_type = property(citem_to_specific_type) - -""" array used for translating cinsn_t->op type to their names. """ -cinsn_t.op_to_typename = {} -for k in dir(_idaapi): - if k.startswith('cit_'): - cinsn_t.op_to_typename[getattr(_idaapi, k)] = k[4:] - -""" array used for translating cexpr_t->op type to their names. """ -cexpr_t.op_to_typename = {} -for k in dir(_idaapi): - if k.startswith('cot_'): - cexpr_t.op_to_typename[getattr(_idaapi, k)] = k[4:] - -def property_op_to_typename(self): - return self.op_to_typename[self.op] -cinsn_t.opname = property(property_op_to_typename) -cexpr_t.opname = property(property_op_to_typename) - -def cexpr_operands(self): - """ return a dictionary with the operands of a cexpr_t. """ - - if self.op >= cot_comma and self.op <= cot_asgumod or \ - self.op >= cot_lor and self.op <= cot_fdiv or \ - self.op == cot_idx: - return {'x': self.x, 'y': self.y} - - elif self.op == cot_tern: - return {'x': self.x, 'y': self.y, 'z': self.z} - - elif self.op in [cot_fneg, cot_neg, cot_sizeof] or \ - self.op >= cot_lnot and self.op <= cot_predec: - return {'x': self.x} - - elif self.op == cot_cast: - return {'type': self.type, 'x': self.x} - - elif self.op == cot_call: - return {'x': self.x, 'a': self.a} - - elif self.op in [cot_memref, cot_memptr]: - return {'x': self.x, 'm': self.m} - - elif self.op == cot_num: - return {'n': self.n} - - elif self.op == cot_fnum: - return {'fpc': self.fpc} - - elif self.op == cot_str: - return {'string': self.string} - - elif self.op == cot_obj: - return {'obj_ea': self.obj_ea} - - elif self.op == cot_var: - return {'v': self.v} - - elif self.op == cot_helper: - return {'helper': self.helper} - - raise RuntimeError('unknown op type %s' % self.opname) -cexpr_t.operands = property(cexpr_operands) - -def cinsn_details(self): - """ return the details pointer for the cinsn_t object depending on the value of its op member. \ - this is one of the cblock_t, cif_t, etc. objects. """ - - if self.op not in self.op_to_typename: - raise RuntimeError('unknown item->op type') - - opname = self.opname - if opname == 'empty': - return self - - if opname in ['break', 'continue']: - return None - - return getattr(self, 'c' + opname) -cinsn_t.details = property(cinsn_details) - -def cblock_iter(self): - - iter = self.begin() - for i in range(self.size()): - yield iter.cur - iter.next() - - return -cblock_t.__iter__ = cblock_iter -cblock_t.__len__ = cblock_t.size - -# cblock.find(cinsn_t) -> returns the iterator positioned at the given item -def cblock_find(self, item): - - iter = self.begin() - for i in range(self.size()): - if iter.cur == item: - return iter - iter.next() - - return -cblock_t.find = cblock_find - -# cblock.index(cinsn_t) -> returns the index of the given item -def cblock_index(self, item): - - iter = self.begin() - for i in range(self.size()): - if iter.cur == item: - return i - iter.next() - - return -cblock_t.index = cblock_index - -# cblock.at(int) -> returns the item at the given index index -def cblock_at(self, index): - - iter = self.begin() - for i in range(self.size()): - if i == index: - return iter.cur - iter.next() - - return -cblock_t.at = cblock_at - -# cblock.remove(cinsn_t) -def cblock_remove(self, item): - - iter = self.find(item) - self.erase(iter) - - return -cblock_t.remove = cblock_remove - -# cblock.insert(index, cinsn_t) -def cblock_insert(self, index, item): - - pos = self.at(index) - iter = self.find(pos) - self.insert(iter, item) - - return -cblock_t.insert = cblock_insert - -cfuncptr_t.__str__ = lambda self: str(self.__deref__()) - -def cfunc_type(self): - """ Get the function's return type tinfo_t object. """ - tif = tinfo_t() - result = self.get_func_type(tif) - if not result: - return - return tif -cfunc_t.type = property(cfunc_type) -cfuncptr_t.type = property(lambda self: self.__deref__().type) - -cfunc_t.arguments = property(lambda self: [o for o in self.lvars if o.is_arg_var]) -cfuncptr_t.arguments = property(lambda self: self.__deref__().arguments) - -cfunc_t.lvars = property(cfunc_t.get_lvars) -cfuncptr_t.lvars = property(lambda self: self.__deref__().lvars) -cfunc_t.warnings = property(cfunc_t.get_warnings) -cfuncptr_t.warnings = property(lambda self: self.__deref__().warnings) -cfunc_t.pseudocode = property(cfunc_t.get_pseudocode) -cfuncptr_t.pseudocode = property(lambda self: self.__deref__().get_pseudocode()) -cfunc_t.eamap = property(cfunc_t.get_eamap) -cfuncptr_t.eamap = property(lambda self: self.__deref__().get_eamap()) -cfunc_t.boundaries = property(cfunc_t.get_boundaries) -cfuncptr_t.boundaries = property(lambda self: self.__deref__().get_boundaries()) - -#pragma SWIG nowarn=+503 - -lvar_t.used = property(lvar_t.used) -lvar_t.typed = property(lvar_t.typed) -lvar_t.mreg_done = property(lvar_t.mreg_done) -lvar_t.has_nice_name = property(lvar_t.has_nice_name) -lvar_t.is_unknown_width = property(lvar_t.is_unknown_width) -lvar_t.has_user_info = property(lvar_t.has_user_info) -lvar_t.has_user_name = property(lvar_t.has_user_name) -lvar_t.has_user_type = property(lvar_t.has_user_type) -lvar_t.is_result_var = property(lvar_t.is_result_var) -lvar_t.is_arg_var = property(lvar_t.is_arg_var) -lvar_t.is_fake_var = property(lvar_t.is_fake_var) -lvar_t.is_overlapped_var = property(lvar_t.is_overlapped_var) -lvar_t.is_floating_var = property(lvar_t.is_floating_var) -lvar_t.is_spoiled_var = property(lvar_t.is_spoiled_var) -lvar_t.is_mapdst_var = property(lvar_t.is_mapdst_var) - -# dictify all dict-like types - -def _map___getitem__(self, key): - """ Returns the value associated with the provided key. """ - if not isinstance(key, self.keytype): - raise KeyError('type of key should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) - if key not in self: - raise KeyError('key not found') - return self.second(self.find(key)) - -def _map___setitem__(self, key, value): - """ Returns the value associated with the provided key. """ - if not isinstance(key, self.keytype): - raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) - if not isinstance(value, self.valuetype): - raise KeyError('type of `value` should be ' + repr(self.valuetype) + ' but got ' + type(value)) - self.insert(key, value) - return - -def _map___delitem__(self, key): - """ Removes the value associated with the provided key. """ - if not isinstance(key, self.keytype): - raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) - if key not in self: - raise KeyError('key not found') - self.erase(self.find(key)) - return - -def _map___contains__(self, key): - """ Returns true if the specified key exists in the . """ - if not isinstance(key, self.keytype): - raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) - if self.find(key) != self.end(): - return True - return False - -def _map_clear(self): - self.clear() - return - -def _map_copy(self): - ret = {} - for k in self.iterkeys(): - ret[k] = self[k] - return ret - -def _map_get(self, key, default=None): - if key in self: - return self[key] - return default - -def _map_iterkeys(self): - iter = self.begin() - while iter != self.end(): - yield self.first(iter) - iter = self.next(iter) - return - -def _map_itervalues(self): - iter = self.begin() - while iter != self.end(): - yield self.second(iter) - iter = self.next(iter) - return - -def _map_iteritems(self): - iter = self.begin() - while iter != self.end(): - yield (self.first(iter), self.second(iter)) - iter = self.next(iter) - return - -def _map_keys(self): - return list(self.iterkeys()) - -def _map_values(self): - return list(self.itervalues()) - -def _map_items(self): - return list(self.iteritems()) - -def _map_has_key(self, key): - return key in self - -def _map_pop(self, key): - """ Sets the value associated with the provided key. """ - if not isinstance(key, self.keytype): - raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) - if key not in self: - raise KeyError('key not found') - ret = self[key] - del self[key] - return ret - -def _map_popitem(self): - """ Sets the value associated with the provided key. """ - if len(self) == 0: - raise KeyError('key not found') - key = self.keys()[0] - return (key, self.pop(key)) - -def _map_setdefault(self, key, default=None): - """ Sets the value associated with the provided key. """ - if not isinstance(key, self.keytype): - raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) - if key in self: - return self[key] - self[key] = default - return default - -def _map_as_dict(maptype, name, keytype, valuetype): - - maptype.keytype = keytype - maptype.valuetype = valuetype - - for fctname in ['begin', 'end', 'first', 'second', 'next', \ - 'find', 'insert', 'erase', 'clear', 'size']: - fct = getattr(_idaapi, name + '_' + fctname) - setattr(maptype, '__' + fctname, fct) - - maptype.__len__ = maptype.size - maptype.__getitem__ = maptype.at - - maptype.begin = lambda self, *args: self.__begin(self, *args) - maptype.end = lambda self, *args: self.__end(self, *args) - maptype.first = lambda self, *args: self.__first(*args) - maptype.second = lambda self, *args: self.__second(*args) - maptype.next = lambda self, *args: self.__next(*args) - maptype.find = lambda self, *args: self.__find(self, *args) - maptype.insert = lambda self, *args: self.__insert(self, *args) - maptype.erase = lambda self, *args: self.__erase(self, *args) - maptype.clear = lambda self, *args: self.__clear(self, *args) - maptype.size = lambda self, *args: self.__size(self, *args) - maptype.__getitem__ = _map___getitem__ - maptype.__setitem__ = _map___setitem__ - maptype.__delitem__ = _map___delitem__ - maptype.__contains__ = _map___contains__ - maptype.clear = _map_clear - maptype.copy = _map_copy - maptype.get = _map_get - maptype.iterkeys = _map_iterkeys - maptype.itervalues = _map_itervalues - maptype.iteritems = _map_iteritems - maptype.keys = _map_keys - maptype.values = _map_values - maptype.items = _map_items - maptype.has_key = _map_has_key - maptype.pop = _map_pop - maptype.popitem = _map_popitem - maptype.setdefault = _map_setdefault - -#_map_as_dict(user_labels_t, 'user_labels', (int, long), qstring) -_map_as_dict(user_cmts_t, 'user_cmts', treeloc_t, citem_cmt_t) -_map_as_dict(user_numforms_t, 'user_numforms', operand_locator_t, number_format_t) -_map_as_dict(user_iflags_t, 'user_iflags', citem_locator_t, (int, long)) -_map_as_dict(user_unions_t, 'user_unions', (int, long), intvec_t) -_map_as_dict(eamap_t, 'eamap', long, cinsnptrvec_t) -#_map_as_dict(boundaries_t, 'boundaries', cinsn_t, areaset_t) - +# +# +%} + +//------------------------------------------------------------------------- +%init %{ +// +// %} diff --git a/swig/ida.i b/swig/ida.i index 0752a56..ab1af4b 100644 --- a/swig/ida.i +++ b/swig/ida.i @@ -1,16 +1,5 @@ // Ignore kernel-only symbols %ignore dual_text_options_t; -%ignore idainfo::init; -%ignore idainfo::init_netnode; -%ignore idainfo::precheck_idb_version; -%ignore idainfo::retrieve; -%ignore idainfo::read; -%ignore idainfo::write; -%ignore idainfo::convert_va_format; -%ignore idainfo::upgrade_approved; -%ignore idainfo::will_upgrade; -%ignore idainfo::approve_upgrade; -%ignore idainfo::show_progress; %ignore idainfo::align_short_demnames; %ignore idainfo::align_strtype; %ignore idainfo::align_long_demnames; @@ -21,6 +10,7 @@ %ignore setflag(uint32 &where,uint32 bit,int value); // Make idainfo::get_proc_name() work +%include "cstring.i" %cstring_bounded_output(char *buf, 8); %ignore BADADDR; diff --git a/swig/idaapi.i b/swig/idaapi.i index ef47c5f..5cfa0c4 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -1,163 +1,8 @@ -%module(docstring="IDA Plugin SDK API wrapper",directors="1",threads="1") idaapi -// generate directors for all classes that have virtual methods -%feature("director"); -// exceptions -%feature("nodirector") const_visitor_t; -%feature("nodirector") const_vloc_visitor_t; -%feature("nodirector") enumplace_t; -%feature("nodirector") generic_linput64_t; -%feature("nodirector") generic_linput_t; -%feature("nodirector") place_t; -%feature("nodirector") idaplace_t; -%feature("nodirector") qrefcnt_obj_t; -%feature("nodirector") qstring_printer_t; -%feature("nodirector") simpleline_place_t; -%feature("nodirector") structplace_t; -%feature("nodirector") type_mapper_t; -%feature("nodirector") type_visitor_t; -%feature("nodirector") vc_printer_t; -%feature("nodirector") vd_printer_t; -%feature("nodirector") vloc_visitor_t; -%feature("nodirector") qflow_chart_t; -%feature("nodirector") lowertype_helper_t; -%feature("nodirector") ida_lowertype_helper_t; -%warnfilter(473) user_lvar_visitor_t::get_info_mapping_for_saving; // Returning a pointer or reference in a director method is not recommended -// * 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 -#pragma SWIG nowarn=514 // Director base class 'x' has no virtual destructor. - -%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); - } -} - -void raise_python_swig_director_exception(const Swig::DirectorException &e) -{ - Py_INCREF(PyExc_RuntimeError); - PyErr_SetString(PyExc_RuntimeError, e.getMessage()); -} - -void raise_python_out_of_range_exception(const std::out_of_range &e) -{ - Py_INCREF(PyExc_RuntimeError); - PyErr_SetString(PyExc_IndexError, e.what()); -} - +#include +#include %} -%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::out_of_range &e ) { raise_python_out_of_range_exception(e); SWIG_fail; } - catch ( const std::exception &e ) { raise_python_stl_exception(e); SWIG_fail; } - catch ( const Swig::DirectorException &e ) { raise_python_swig_director_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( - PTR2U64(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 @@ -165,75 +10,14 @@ static PyObject *type##_get_clink_ptr(PyObject *self) #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" - // // %} -// 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; +%constant size_t SIZE_MAX = size_t(-1); +/* %constant nodeidx_t BADNODE = nodeidx_t(-1); */ %include "typemaps.i" @@ -241,108 +25,19 @@ typedef int error_t; %include "carrays.i" %include "cpointer.i" -%include "typeconv.i" - %pythoncode %{ # # %} -%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 "idbhooks.i" -%include "netnode.i" -%include "nalt.i" - -%include "allins.i" -%include "area.i" -%include "auto.i" -%include "bytes.i" -%include "custdata.i" -%include "dbg.i" -%include "diskio.i" -%include "linput.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 %{ // // %} -%include "gdl.i" -%include "ints.i" -%include "kernwin.i" -%include "cli.i" -%include "choose.i" -%include "choose2.i" -%include "plgform.i" -%include "custview.i" -%include "askusingform.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" +//------------------------------------------------------------------------- +%inline %{ +// +// +%} diff --git a/swig/idbhooks.i b/swig/idbhooks.i index 40c8f84..83abeb0 100644 --- a/swig/idbhooks.i +++ b/swig/idbhooks.i @@ -1,3 +1,9 @@ +%{ +#include +#include +%} + +%ignore IDB_Callback; %inline %{ // diff --git a/swig/idd.i b/swig/idd.i index 9436f10..e8942e5 100644 --- a/swig/idd.i +++ b/swig/idd.i @@ -1,3 +1,9 @@ +%{ +#include +#include +#include +%} + %ignore debugger_t; %ignore memory_info_t; %ignore lowcnd_t; @@ -20,6 +26,13 @@ %include "idd.hpp" +// 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; + %clear(char dtyp); %rename (appcall) py_appcall; diff --git a/swig/idp.i b/swig/idp.i index dfcc795..8cd5f3b 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -1,3 +1,10 @@ +%{ +#include +#include +#include +#include +#include +%} // Ignore the following symbols %ignore WorkReg; %ignore AbstractRegister; @@ -20,15 +27,9 @@ %ignore read_config; %ignore read_config_file; %ignore read_config_string; -%ignore read_kernel_config_file; -%ignore split_path_envvar; -%ignore get_user_idadirs; -%ignore find_cfg_files; %ignore cfgopt_t; %ignore cfgopt_t__apply; %ignore parse_config_value; -%ignore get_idptype_and_data; - %ignore s_preline; %ignore ca_operation_t; @@ -42,37 +43,11 @@ %ignore instruc_t; %ignore processor_t; %ignore ph; -%ignore IDB_Callback; %ignore IDP_Callback; %ignore _py_getreg; -%ignore free_processor_module; -%ignore cfg_compiler_changed; - -%ignore gen_idb_event; -%ignore print_spec_entry; %include "idp.hpp" -%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; - } -} - -%ignore IDP_Hooks::bool_to_cmdsize; -%ignore IDP_Hooks::bool_to_2or0; -%ignore IDP_Hooks::cm_t_to_int; -%ignore IDP_Hooks::handle_custom_mnem_output; -%ignore IDP_Hooks::handle_assemble_output; - %inline %{ // // @@ -82,3 +57,28 @@ // // %} + +%pythoncode %{ +# +# +%} + + +//------------------------------------------------------------------------- +// IDB_Hooks +//------------------------------------------------------------------------- +%{ +#include +%} + +%ignore IDB_Callback; + +%inline %{ +// +// +%} + +%{ +// +// +%} diff --git a/swig/ints.i b/swig/ints.i index 5a57a31..ae0a682 100644 --- a/swig/ints.i +++ b/swig/ints.i @@ -1,6 +1,2 @@ -// Kernel-only symbols -%ignore init_predefs; -%ignore term_predefs; - %include "ints.i" diff --git a/swig/kernwin.i b/swig/kernwin.i index 0d1e2d1..caccd5a 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -1,3 +1,15 @@ +%{ +#include +%} + +%{ +#ifdef __NT__ +idaman __declspec(dllimport) plugin_t PLUGIN; +#else +extern plugin_t PLUGIN; +#endif +%} + // Ignore the va_list functions %ignore AskUsingForm_cv; %ignore AskUsingForm_c; @@ -17,6 +29,9 @@ %ignore choose_idasgn; %rename (choose_idasgn) py_choose_idasgn; +%ignore get_chooser_data; +%rename (get_chooser_data) py_get_chooser_data; + %rename (del_hotkey) py_del_hotkey; %rename (add_hotkey) py_add_hotkey; @@ -26,7 +41,6 @@ %ignore umsg; %rename (umsg) py_umsg; -%ignore textctrl_info_t; %ignore vinfo; %ignore UI_Callback; %ignore vnomem; @@ -38,13 +52,6 @@ %ignore askyn_v; %ignore add_custom_viewer_popup_item; %ignore create_custom_viewer; -%extend qvector< snapshot_t *> { - snapshot_t *at(size_t n) { return self->at(n); } -}; -%ignore qvector< snapshot_t *>::at(size_t) const; -%ignore qvector< snapshot_t *>::at(size_t); -%ignore qvector< snapshot_t *>::grow; -%template(qvector_snapshotvec_t) qvector; %ignore take_database_snapshot; %rename (take_database_snapshot) py_take_database_snapshot; %ignore restore_database_snapshot; @@ -79,6 +86,8 @@ %rename (unregister_action) py_unregister_action; %ignore attach_dynamic_action_to_popup; %rename (attach_dynamic_action_to_popup) py_attach_dynamic_action_to_popup; +%ignore get_registered_actions; +%rename (get_registered_actions) py_get_registered_actions; %include "typemaps.i" @@ -130,19 +139,8 @@ %ignore place_t__deserialize; %ignore register_place_class; -%ignore register_enumplace; -%ignore register_idaplace; -%ignore register_simpleline_place; -%ignore register_structplace; -%ignore unregister_place_class; -%ignore unregister_place_classes_with_owner; -%ignore unregister_enumplace; -%ignore unregister_idaplace; -%ignore unregister_simpleline_place; -%ignore unregister_structplace; %ignore register_loc_converter; %ignore lookup_loc_converter; -%ignore unregister_loc_converters_for_class; %feature("director") UI_Hooks; @@ -211,12 +209,18 @@ void refresh_lists(void) # This is for get_cursor() %apply int *OUTPUT {int *x, int *y}; +%ignore textctrl_info_t; SWIG_DECLARE_PY_CLINKED_OBJECT(textctrl_info_t) %{ static void _py_unregister_compiled_form(PyObject *py_form, bool shutdown); %} +%{ +// +// +%} + %inline %{ // // @@ -313,3 +317,133 @@ static void _py_unregister_compiled_form(PyObject *py_form, bool shutdown); # # %} + +//------------------------------------------------------------------------- +// choose +//------------------------------------------------------------------------- +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} + +//------------------------------------------------------------------------- +// choose2 +//------------------------------------------------------------------------- +%{ +// +// +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} + +//------------------------------------------------------------------------- +// askusingform +//------------------------------------------------------------------------- +%{ +// +// +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} + + +//------------------------------------------------------------------------- +// cli_t +//------------------------------------------------------------------------- +%{ +// +// +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} + +//------------------------------------------------------------------------- +%init %{ +// +// +%} + + +//------------------------------------------------------------------------- +// IDAView +//------------------------------------------------------------------------- +%{ +// +// + +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} + +//------------------------------------------------------------------------- +// simplecustviewer_t +//------------------------------------------------------------------------- +%{ +// +// +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} + +//------------------------------------------------------------------------- +// PluginForm +//------------------------------------------------------------------------- +%{ +// +// +%} + +%inline %{ +// +// +%} + +%pythoncode %{ +# +# +%} diff --git a/swig/lines.i b/swig/lines.i index 56e74c7..47e5bb5 100644 --- a/swig/lines.i +++ b/swig/lines.i @@ -12,11 +12,7 @@ %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; @@ -29,29 +25,13 @@ %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; diff --git a/swig/loader.i b/swig/loader.i index 19f4ff8..8ea78a8 100644 --- a/swig/loader.i +++ b/swig/loader.i @@ -11,7 +11,6 @@ // TODO: These could be wrapped if needed %ignore load_info_t; -%ignore add_plugin_option; %ignore get_plugins_paths; %ignore build_loaders_list; %ignore free_loaders_list; @@ -40,9 +39,6 @@ %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; @@ -74,43 +70,11 @@ %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; @@ -121,6 +85,14 @@ %ignore run_plugin; %rename (run_plugin) py_run_plugin; +%extend qvector< snapshot_t *> { + snapshot_t *at(size_t n) { return self->at(n); } +}; +%ignore qvector< snapshot_t *>::at(size_t) const; +%ignore qvector< snapshot_t *>::at(size_t); +%ignore qvector< snapshot_t *>::grow; +%template(qvector_snapshotvec_t) qvector; + %include "loader.hpp" %inline %{ diff --git a/swig/moves.i b/swig/moves.i index 3413a0c..91078b4 100644 --- a/swig/moves.i +++ b/swig/moves.i @@ -1,17 +1,12 @@ - +%{ +#include +%} // Ignore kernel only symbols -%ignore init_marks; -%ignore term_marks; -%ignore change_jumps_stack_format; %ignore move_marks; %ignore curloc_after_segments_moved; %ignore curloc::rebase_stack; -%ignore loc_gtag; -%ignore write_gli; -%ignore read_gli; %ignore DEFINE_CURLOC_HELPERS; %ignore DEFINE_LOCATION_HELPERS; -%ignore histories_after_segments_moved; %ignore lochist_t::rebase_stack; %ignore location_t::location_t(bool); %ignore lochist_t::is_hexrays68_compat; diff --git a/swig/nalt.i b/swig/nalt.i index 3b945de..afe8bdd 100644 --- a/swig/nalt.i +++ b/swig/nalt.i @@ -1,6 +1,11 @@ -%ignore nmSerEA; -%ignore nmSerN; -%ignore maxSerialName; +%{ +#include +#include +#include +#include +#include "../../../pywraps.hpp" +%} + %ignore get_import_module_name; %rename (get_import_module_name) py_get_import_module_name; %ignore NALT_EA; @@ -14,8 +19,6 @@ %ignore get_strid; %ignore _set_strid; %ignore _del_strid; -%ignore set_strid; -%ignore del_strid; %ignore xrefpos_t; %ignore get_xrefpos; %ignore set_xrefpos; @@ -142,9 +145,6 @@ %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 %{ // diff --git a/swig/name.i b/swig/name.i index d7bce0a..da18a37 100644 --- a/swig/name.i +++ b/swig/name.i @@ -1,5 +1,4 @@ -%cstring_output_maxstr_none(char *buf, int bufsize); - +%include "cstring.i" %cstring_bounded_output(char *dstname, MAXSTR); %cstring_bounded_output(char *buf, MAXSTR); @@ -9,18 +8,8 @@ // 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; @@ -29,10 +18,6 @@ %ignore get_ea_name; %rename (get_ea_name) py_get_ea_name; -%ignore get_name_value2; -%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); diff --git a/swig/netnode.i b/swig/netnode.i index d7a99de..4cc4661 100644 --- a/swig/netnode.i +++ b/swig/netnode.i @@ -1,11 +1,4 @@ -// Ignore kernel only & unexported symbols -%ignore netlink; - %ignore RootNode; -%ignore for_all_supvals; -%ignore netErrorHandler; -%ignore netNoDiskSpaceHandler; -%ignore netnode_key_count; %ignore netnode_check; %ignore netnode_kill; @@ -91,6 +84,8 @@ %ignore netnode::operator nodeidx_t; %ignore netnode::validate_names; +%constant nodeidx_t BADNODE = nodeidx_t(-1); + // Renaming one version of hashset() otherwise SWIG will not be able to activate the other one %rename (hashset_idx) netnode::hashset(const char *idx, nodeidx_t value, char tag=htag); diff --git a/swig/offset.i b/swig/offset.i index fb277df..b854afa 100644 --- a/swig/offset.i +++ b/swig/offset.i @@ -1,3 +1,4 @@ -%ignore calc_probable_base; - +%{ +#include +%} %include "offset.hpp" diff --git a/swig/plgform.i b/swig/plgform.i deleted file mode 100644 index 931aa15..0000000 --- a/swig/plgform.i +++ /dev/null @@ -1,17 +0,0 @@ - -// relies on kernwin.i to include kernwin.hpp - -%{ -// -// -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/pro.i b/swig/pro.i index 2f7e05a..5b36439 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -1,183 +1,41 @@ -//--------------------------------------------------------------------- -%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; - -void qvector::grow(const long long &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 { - 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) { - if (i >= $self->size() || i < 0) - throw std::out_of_range("out of bounds access"); - $self->at(i) = v; - } - - inline const T&at(size_t i) { - return __getitem__(i); - } - - %pythoncode { - __iter__ = _bounded_getitem_iterator - } -} +// we must include those manually here +%import "ida.hpp" +%import "xref.hpp" +%import "typeinf.hpp" +%import "enum.hpp" +%import "netnode.hpp" +// //--------------------------------------------------------------------- %template(uvalvec_t) qvector; // unsigned values %template(intvec_t) qvector; %template(int64vec_t) qvector; // for EA64 svalvec_t objects %template(boolvec_t) qvector; -%template(casevec_t) qvector >; // signed values %template(strvec_t) qvector; -%pythoncode %{ -_listify_types(uvalvec_t, - intvec_t, - int64vec_t, - boolvec_t, - casevec_t, - strvec_t) +SWIG_DECLARE_PY_CLINKED_OBJECT(qstrvec_t) + +%inline %{ +// +// +%} + +%include "carrays.i" +%include "cpointer.i" +%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); + +%pythoncode %{ +# +# %} diff --git a/swig/queue.i b/swig/queue.i index 8e401c9..04fcfeb 100644 --- a/swig/queue.i +++ b/swig/queue.i @@ -1,16 +1,12 @@ +%{ +#include +%} // TODO: This could be wrapped. %ignore QueueGet; // Kernel-only & unexported symbols %ignore QueueDel(ea_t); -%ignore init_queue; -%ignore save_queue; -%ignore term_queue; -%ignore move_problems; -%ignore queue_del; -%ignore mark_rollback; -%ignore get_rollback_type; %ignore mark_ida_decision; %ignore unmark_ida_decision; diff --git a/swig/registry.i b/swig/registry.i index 45b150d..d137b90 100644 --- a/swig/registry.i +++ b/swig/registry.i @@ -1,4 +1,6 @@ - +%{ +#include +%} %ignore reg_bin_op; %ignore reg_str_op; %ignore reg_int_op; diff --git a/swig/search.i b/swig/search.i index a2abf23..65d1501 100644 --- a/swig/search.i +++ b/swig/search.i @@ -1,3 +1,6 @@ +%{ +#include +%} %apply int * OUTPUT { int *opnum }; %ignore search; diff --git a/swig/segment.i b/swig/segment.i index beb54fc..1d259af 100644 --- a/swig/segment.i +++ b/swig/segment.i @@ -1,4 +1,7 @@ // Ignore functions with callbacks + +%import "area.i" + %ignore enumerate_selectors; %ignore enumerate_segments_with_selector; @@ -8,13 +11,6 @@ %ignore term_groups; %ignore vset_segm_name; %ignore get_segm_expr; -%ignore get_based_segm_expr; -%ignore createSegmentation; -%ignore initSegment; -%ignore save_segments; -%ignore termSegment; -%ignore DeleteAllSegments; -%ignore delete_debug_segments; %ignore is_debugger_segm; %ignore is_ephemeral_segm; %ignore correct_address; diff --git a/swig/srarea.i b/swig/srarea.i index 309e888..60f2a36 100644 --- a/swig/srarea.i +++ b/swig/srarea.i @@ -1,42 +1,15 @@ +%{ +#include +%} + +%import "area.i" + // Ignore kernel-only symbols %ignore repairSRarea; -%ignore init_srarea; -%ignore term_srarea; -%ignore reset_srarea; -%ignore add_srarea_from_cache; -%ignore srareas_got_loaded; -%ignore save_srarea; -%ignore create_segment_registers_area; -%ignore set_segment_register_start; -%ignore set_segment_register_end; -%ignore kill_srareras; -%ignore create_srarea; -%ignore del_srareas; -%ignore move_srareas; %ignore delete_v660_segreg_t; %ignore v660_segreg_t; -%ignore SRareas_get_area; -%ignore SRareas_get_area_qty; -%ignore SRareas_getn_area; -%ignore SRareas_update; -%ignore SRareas_get_area_num; -%ignore SRareas_get_next_area; -%ignore SRareas_get_prev_area; -%ignore SRareas_next_area_ptr; -%ignore SRareas_prev_area_ptr; -%ignore SRareas_first_area_ptr; -%ignore SRareas_choose_area2; -%ignore SRareas_may_start_at; -%ignore SRareas_may_end_at; -%ignore SRareas_set_start; -%ignore SRareas_set_end; -%ignore SRareas_prepare_to_create; -%ignore SRareas_create_area; -%ignore SRareas_for_all_areas2; -%ignore SRareas_del_area; - %ignore segreg_t::tag(int n); %ignore segreg_t::reg(int n); diff --git a/swig/strlist.i b/swig/strlist.i index f58f6c1..92cb7b1 100644 --- a/swig/strlist.i +++ b/swig/strlist.i @@ -1,11 +1,9 @@ -// Ignore kernel-only symbol +%{ +#include +%} %ignore strwinsetup_t::setup_strings_window; %ignore strwinsetup_t::save_config; %ignore strwinsetup_t::restore_config; -%ignore move_strings; - - - %include "strlist.hpp" diff --git a/swig/struct.i b/swig/struct.i index 0e822f9..c0e60ba 100644 --- a/swig/struct.i +++ b/swig/struct.i @@ -1,10 +1,11 @@ +%{ +#include +%} // Kernel-only symbols -%ignore init_structs; +%ignore del_ti; %ignore save_structs; -%ignore term_structs; %ignore get_struc_name(tid_t); %ignore get_member_name2(tid_t); -%ignore sync_from_struc; %ignore get_member_by_id(tid_t, struc_t **); // allow version w/ qstring* only //------------------------------------------------------------------------- diff --git a/swig/typeconv.i b/swig/typeconv.i index a9d71bf..ffd5743 100644 --- a/swig/typeconv.i +++ b/swig/typeconv.i @@ -366,7 +366,7 @@ struct wrapped_array_t { } %pythoncode { - __iter__ = _bounded_getitem_iterator + __iter__ = ida_idaapi._bounded_getitem_iterator } } @@ -410,3 +410,55 @@ struct wrapped_array_t { #else #error Ensure tinfo_t wrapping is compatible with this version of SWIG #endif + +// 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); + + +%{ +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; +} +%} + +//--------------------------------------------------------------------- +%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; +} diff --git a/swig/typeinf.i b/swig/typeinf.i index 779cc69..20da251 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -1,3 +1,10 @@ +%{ +#include +#include +%} + +%import "idp.i" + // Most of these could be wrapped if needed %ignore get_cc; %ignore get_cc_type_size; @@ -68,6 +75,7 @@ %ignore perform_funcarg_conversion; %ignore get_argloc_info; %ignore argloc_t::dstr; +%ignore argpart_t::copy_from; %ignore extract_pstr; %ignore skipName; @@ -90,12 +98,20 @@ %rename (get_named_type64) py_get_named_type64; %rename ("%s") tinfo_t::get_named_type; -%ignore udt_type_data_t::is_last_baseclass; - %rename (print_decls) py_print_decls; %ignore print_decls; -%ignore set_named_type; +// let's get rid of this one, without prohibiting tinfo_t::set_named_type() +%ignore set_named_type( + til_t *, + const char *, + int, + const type_t *, + const p_list *, + const char *, + const p_list *, + const sclass_t *, + const uint32 *); %ignore get_named_type_size; %ignore decorate_name; @@ -173,19 +189,14 @@ // 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::serialize_enum; -%ignore enum_type_data_t::deserialize_enum; %ignore valstr_deprecated_t; %ignore valinfo_deprecated_t; %ignore valstr_deprecated2_t; %ignore valinfo_deprecated2_t; +%ignore tinfo_t::serialize(qtype *, qtype *, qtype *, int) const; %ignore custloc_desc_t; %ignore install_custom_argloc; @@ -207,6 +218,12 @@ %extend tinfo_t { + PyObject *serialize( + int sudt_flags=SUDT_FAST|SUDT_TRUNC) const + { + return py_tinfo_t_serialize($self, sudt_flags); + } + bool deserialize( const til_t *til, const type_t *type, @@ -274,6 +291,7 @@ %template(funcargvec_t) qvector; %template(udtmembervec_t) qvector; +%template(reginfovec_t) qvector; %include "typeinf.hpp" diff --git a/swig/ua.i b/swig/ua.i index b3c6915..c585e0d 100644 --- a/swig/ua.i +++ b/swig/ua.i @@ -1,7 +1,11 @@ +%{ +#include +#include +%} + %ignore insn_t; %ignore op_t; %ignore cmd; -%ignore ua_out; %ignore showAsChar; %ignore out_real; %ignore init_output_buffer; @@ -19,12 +23,7 @@ %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; @@ -44,16 +43,16 @@ %rename (ua_add_off_drefs2) py_ua_add_off_drefs2; %rename (decode_preceding_insn) py_decode_preceding_insn; -%inline %{ -// -// -%} - %{ // // %} +%inline %{ +// +// +%} + %pythoncode %{ # # diff --git a/swig/view.i b/swig/view.i deleted file mode 100644 index 7ea2f11..0000000 --- a/swig/view.i +++ /dev/null @@ -1,16 +0,0 @@ - -%{ -// -// - -%} - -%inline %{ -// -// -%} - -%pythoncode %{ -# -# -%} diff --git a/swig/xref.i b/swig/xref.i index 54ebe21..c034baf 100644 --- a/swig/xref.i +++ b/swig/xref.i @@ -1,16 +1,13 @@ // Ignore kernel-only functions and variables %ignore create_xrefs_from; -%ignore create_xrefs_from_data; %ignore delete_all_xrefs_from; -%ignore delete_data_xrefs_from; -%ignore delete_code_xrefs_from; %ignore destroy_if_align; %ignore lastXR; -%ignore has_jump_or_flow_xref; -%ignore has_call_xref; -%ignore destroy_switch_info; %ignore create_switch_xrefs; +%rename (create_switch_xrefs) py_create_switch_xrefs; %ignore create_switch_table; +%rename (create_switch_table) py_create_switch_table; +%ignore calc_switch_cases; %rename (calc_switch_cases) py_calc_switch_cases; // These functions should not be called directly (according to docs) @@ -22,4 +19,16 @@ // 'from' is a reserved Python keyword %rename (frm) from; +%inline %{ +// +// +%} + %include "xref.hpp" + +%template(casevec_t) qvector >; // signed values + +%pythoncode %{ +# +# +%} diff --git a/tools/chkapi.py b/tools/chkapi.py new file mode 100644 index 0000000..ceaa77f --- /dev/null +++ b/tools/chkapi.py @@ -0,0 +1,450 @@ +from __future__ import with_statement + +import sys, optparse, re, pprint + +ignorable_api_contents = [ + "qwait" + ] + +class TextStream: + def __init__(self, text): + self.text = text + self.point = 0 + self.maxpoint = len(self.text) + self.line_nr = 0 + self.char_nr = 0 + self.curline = None + + def line(self): + pt = self.point + self.advance_to_newline() + return self.text[pt : self.point] + + def char(self): + c, self.point = self.text[self.point], self.point + 1 + if c == '\n': + self.line_nr += 1 + self.char_nr = 0 + return c + + def advance_to_newline(self): + p = self.point + while self.text[p] != '\n': + p += 1 + p += 1 + self.line_nr += 1 + self.char_nr = 0 + self.point = p + + def empty(self): + return self.point >= self.maxpoint + + +def check_cpp(opts): + + api_contents = { + "functions" : [] + } + + functions_coherence_base = { + # + # qstrvec_t: Specialized, internal, clink'ed object. + # + "_wrap_qstrvec_t_assign" : { + "mustcall" : "qstrvec_t_assign" + }, + "_wrap_qstrvec_t_addressof" : { + "mustcall" : "qstrvec_t_addressof" + }, + "_wrap_qstrvec_t_set" : { + "mustcall" : "qstrvec_t_set" + }, + "_wrap_qstrvec_t_from_list" : { + "mustcall" : "qstrvec_t_from_list" + }, + "_wrap_qstrvec_t_size" : { + "mustcall" : "qstrvec_t_size" + }, + "_wrap_qstrvec_t_get" : { + "mustcall" : "qstrvec_t_get" + }, + "_wrap_qstrvec_t_add" : { + "mustcall" : "qstrvec_t_add" + }, + "_wrap_qstrvec_t_clear" : { + "mustcall" : "qstrvec_t_clear" + }, + "_wrap_qstrvec_t_insert" : { + "mustcall" : "qstrvec_t_insert" + }, + "_wrap_qstrvec_t_remove" : { + "mustcall" : "qstrvec_t_remove" + }, + + # + # Misc. + # + "_wrap_areacb_t_get_type" : { + "mustcall" : "get_type", + }, + "_wrap_tinfo_t_deserialize__SWIG_2" : { + "mustcall" : "tinfo_t_deserialize__SWIG_2", + }, + "_wrap_get_bpt_group" : { + "mustcall" : "PyString_FromStringAndSize", + }, + "_wrap_get_jumptable_info" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_get_array_parameters" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_read_dbg_memory" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_write_dbg_memory" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_get_grp_bpts" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_generic_linput_t_read" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_generic_linput64_t_read64" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_linput_buffer_t_read" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_get_ptr_object_size" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_tag_strlen" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_get_next_member_idx" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_get_prev_member_idx" : { + "string" : "resultobj = PyLong_FromLongLong(result)", + }, + "_wrap_guess_tinfo2" : { + "mustcall" : "PyW_GetNumber", + }, + "_wrap_IDP_Hooks_adjust_refinfo" : { + "string" : "fixup_data_t", + }, + # char[ANY] out typemap + "_wrap_idainfo_tag_get" : { + "nostring" : " --size;", + }, + # char[ANY] varout typemap + "Swig_var_database_idb_get" : { + "nostring" : " --size;", + }, + + # no autoEnabled present + "SWIG_init" : { + "nostring" : "autoEnabled", + }, + } + + functions_coherence_hexrays = { + "_wrap_cfuncptr_t___str__" : { + "mustcall" : ["cfunc_t___str__", "PyString_FromStringAndSize"], + }, + "_wrap_cfunc_t___str__" : { + "mustcall" : ["cfunc_t___str__", "PyString_FromStringAndSize"], + }, + "_wrap_hexrays_failure_t_desc" : { + "mustcall" : "PyString_FromStringAndSize", + }, + "_wrap_vd_failure_t_desc" : { + "mustcall" : "PyString_FromStringAndSize", + }, + "_wrap_create_field_name" : { + "mustcall" : "PyString_FromStringAndSize", + }, + "delete_qrefcnt_t_Sl_cfunc_t_Sg_" : { + "mustcall" : "hexrays_deregister_python_cfuncptr_t_instance", + }, + "_wrap__decompile" : { + "mustcall" : "hexrays_register_python_cfuncptr_t_instance", + }, + "_wrap_vdui_t_cfunc_get" : { + "mustcall" : "hexrays_register_python_cfuncptr_t_instance", + }, + # + # qvector + # + "_wrap_strvec_t___len__" : { + "mustcall" : "qvector_Sl_simpleline_t_Sg____len__", + }, + "_wrap_strvec_t___setitem__" : { + "mustcall" : "qvector_Sl_simpleline_t_Sg____setitem__", + }, + "_wrap_strvec_t___getitem__" : { + "mustcall" : "qvector_Sl_simpleline_t_Sg____getitem__", + }, + # + # vdui_t::cfunc + # + "_wrap_vdui_t_cfunc_get" : { + "string" : "SWIGTYPE_p_qrefcnt_tT_cfunc_t_t", # proper typemap must be used + }, + } + + functions_coherence = functions_coherence_base.copy() + if opts.with_hexrays: + functions_coherence.update(functions_coherence_hexrays) + + # Mark all functions as not-spotted. + for fname in functions_coherence.keys(): + chk = functions_coherence[fname] + chk["spotted"] = False + + def dbg(msg): + if opts.verbose: + print("DEBUG: %s" % msg) + + def is_fundecl(line): + if len(line) <= 2: + return False + if line[0:1].isspace(): + return False + if line[len(line)-1:] != "{": + return False + if line.find("(") == -1 or line.find(")") == -1: + return False + return True + + def collect_funbody_lines(ts): + pt = ts.point + braces_cnt = 1 + while True: + c = ts.char() + if c == "{": + braces_cnt = braces_cnt + 1 + elif c == "}": + braces_cnt = braces_cnt - 1 + if braces_cnt == 0: + break; + # TODO: Skip strings! + return ts.text[pt : ts.point].split("\n") + + api_fname_regex = re.compile(".*PyObject \\*_wrap_([^\\(]*)\\(.*\\).*") + + KICKME_functions_lines = {} + + # Read and go through lines + for one_file in opts.files.split(","): + with open(one_file, "r") as f: + ts = TextStream(f.read()) + + # Process lines + STATE_UNKNOWN = 0 + STATE_IN_FUN = 1 + state = STATE_UNKNOWN + while not ts.empty(): + line = ts.line().rstrip() + # dbg("Line: '%s'" % line) + if is_fundecl(line): + # dbg("Entering function (from line %d: '%s')" % (ts.line_nr, line)) + state = STATE_IN_FUN + funstart = line + match = api_fname_regex.match(funstart) + if match: + fname = match.group(1) + if fname not in ignorable_api_contents: + api_contents["functions"].append(fname) + funlines = collect_funbody_lines(ts) + KICKME_functions_lines[fname] = funlines + # Do we care about this function? + check_for = None + for fname in functions_coherence.keys(): + if funstart.find(fname + "(") > -1: + check_for = fname + break + if check_for: + dbg("Checking function at line %d: '%s'" % (ts.line_nr, check_for)) + chk = functions_coherence[check_for] + chk["spotted"] = True + def look_for_stuff(stuff, isFuncall, lookForPresence=True): + if isinstance(stuff, str): + stuff = [stuff] + for bit in stuff: + found = False + bit_pat = ("%s(" % bit) if isFuncall else bit + dbg("Thing to look for: '%s'" % bit_pat) + for funline in funlines: + # dbg("Testing line: '%s'" % funline) + if funline.find(bit_pat) > -1: + found = True + break + if lookForPresence: + if not found: + raise Exception("Couldn't find '%s', from function '%s' (lines: %s)" % + (bit_pat, check_for, "\n".join([""] + funlines))) + else: + if found: + raise Exception("Did find unwanted '%s', from function '%s' (lines: %s)" % + (bit_pat, check_for, "\n".join([""] + funlines))) + + if "mustcall" in chk: + look_for_stuff(chk["mustcall"], True) + if "string" in chk: + look_for_stuff(chk["string"], False) + if "nostring" in chk: + look_for_stuff(chk["nostring"], False, lookForPresence=False) + + # Ensure all functions were spotted. + for fname in functions_coherence.keys(): + chk = functions_coherence[fname] + if not chk["spotted"]: + raise Exception("Couldn't spot function '%s'" % fname) + + # Report contents + if opts.report_contents: + # NB: we use "wb" here so that the endlines + # are written as-is, in Unix format + # and so 'diff' does not report bogus changes + # against the file from repository + with open(opts.report_contents, "wb") as f: + api_contents["functions"].sort() + f.write(pprint.pformat(api_contents)) + + # import pickle + # with open("/tmp/funlines.last", "wb") as fo: + # pickle.dump(KICKME_functions_lines, fo) + + +def check_python(opts): + types_coherence_base = { + "func_t" : { "mustinherit" : "ida_area.area_t" }, + "hidden_area_t" : { "mustinherit" : "ida_area.area_t" }, + "qbasic_block_t" : { "mustinherit" : "ida_area.area_t" }, + "regvar_t" : { "mustinherit" : "ida_area.area_t" }, + "segment_t" : { "mustinherit" : "ida_area.area_t" }, + "segreg_area_t" : { "mustinherit" : "ida_area.area_t" }, + "segreg_t" : { "mustinherit" : "ida_area.area_t" }, + + "GraphViewer" : { "mustinherit" : "ida_idaapi.CustomIDAMemo" }, + "IDAViewWrapper" : { "mustinherit" : "ida_idaapi.CustomIDAMemo" }, + "PyIdc_cvt_int64__" : { "mustinherit" : "pyidc_cvt_helper__" }, + "PyIdc_cvt_refclass__" : { "mustinherit" : "pyidc_cvt_helper__" }, + "_qstrvec_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" }, + "action_activation_ctx_t" : { "mustinherit" : "action_ctx_base_t" }, + "action_update_ctx_t" : { "mustinherit" : "action_ctx_base_t" }, + "argpart_t" : { "mustinherit" : "argloc_t" }, + "cli_t" : { "mustinherit" : "ida_idaapi.pyidc_opaque_object_t" }, + "enumplace_t" : { "mustinherit" : "place_t" }, + "func_type_data_t" : { "mustinherit" : "funcargvec_t" }, + "ida_lowertype_helper_t" : { "mustinherit" : "lowertype_helper_t" }, + "idaplace_t" : { "mustinherit" : "place_t" }, + "insn_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" }, + "location_t" : { "mustinherit" : "curloc" }, + "op_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" }, + "plugin_t" : { "mustinherit" : "pyidc_opaque_object_t" }, + "processor_t" : { "mustinherit" : "ida_idaapi.pyidc_opaque_object_t" }, + "py_clinked_object_t" : { "mustinherit" : "pyidc_opaque_object_t" }, + "segm_move_infos_t" : { "mustinherit" : "segm_move_info_vec_t" }, + "simpleline_place_t" : { "mustinherit" : "place_t" }, + "structplace_t" : { "mustinherit" : "place_t" }, + "switch_info_ex_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" }, + "textctrl_info_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" }, + "udt_type_data_t" : { "mustinherit" : "udtmembervec_t" }, + "varpart_t" : { "mustinherit" : "varloc_t" }, + + # Just look for the presence of those things + "BADNODE" : {}, + } + + types_coherence_hexrays = { + "DecompilationFailure" : { "mustinherit" : "Exception" }, + "carg_t" : { "mustinherit" : "cexpr_t" }, + "carglist_t" : { "mustinherit" : "qvector_carg_t" }, + "cblock_t" : { "mustinherit" : "qlist_cinsn_t" }, + "ccase_t" : { "mustinherit" : "cinsn_t" }, + "ccases_t" : { "mustinherit" : "qvector_ccase_t" }, + "cdo_t" : { "mustinherit" : "cloop_t" }, + "cexpr_t" : { "mustinherit" : "citem_t" }, + "cfor_t" : { "mustinherit" : "cloop_t" }, + "cfunc_parentee_t" : { "mustinherit" : "ctree_parentee_t" }, + "cif_t" : { "mustinherit" : "ceinsn_t" }, + "cinsn_t" : { "mustinherit" : "citem_t" }, + "cloop_t" : { "mustinherit" : "ceinsn_t" }, + "creturn_t" : { "mustinherit" : "ceinsn_t" }, + "cswitch_t" : { "mustinherit" : "ceinsn_t" }, + "ctree_parentee_t" : { "mustinherit" : "ctree_visitor_t" }, + "cwhile_t" : { "mustinherit" : "cloop_t" }, + "history_item_t" : { "mustinherit" : "ctext_position_t" }, + "history_t" : { "mustinherit" : "qvector_history_t" }, + "lvar_t" : { "mustinherit" : "lvar_locator_t" }, + "lvars_t" : { "mustinherit" : "qvector_lvar_t" }, + "qstring_printer_t" : { "mustinherit" : "vc_printer_t" }, + "vc_printer_t" : { "mustinherit" : "vd_printer_t" }, + "vd_interr_t" : { "mustinherit" : "vd_failure_t" }, + } + + types_coherence = types_coherence_base.copy() + if opts.with_hexrays: + types_coherence.update(types_coherence_hexrays) + + # Mark all types as not-spotted. + for tname in types_coherence.keys(): + chk = types_coherence[tname] + chk["spotted"] = False + + class_re = re.compile("^class ([a-zA-Z0-9_]*)\\(([a-zA-Z0-9_\\.]*)\\):") + var_re = re.compile("^([a-zA-Z0-9_]*) = .*") + for one_file in opts.python_files.split(","): + with open(one_file, "r") as f: + ts = TextStream(f.read()) + + while not ts.empty(): + line = ts.line().rstrip() + match = class_re.match(line) + if match: + tname = match.group(1) + parent = match.group(2) + if tname in types_coherence.keys(): + tc = types_coherence[tname] + tc["spotted"] = True + if "mustinherit" in tc: + if tc["mustinherit"] != parent: + raise Exception("Type '%s' should inherit from '%s' (and not '%s')" % + (tname, tc["mustinherit"], parent)) + else: + match = var_re.match(line) + if match: + vname = match.group(1) + if vname in types_coherence.keys(): + tc = types_coherence[vname] + tc["spotted"] = True + + + # Ensure all types were spotted. + for tname in types_coherence.keys(): + chk = types_coherence[tname] + if not chk["spotted"]: + raise Exception("Couldn't spot type '%s'" % tname) + + + +if __name__ == "__main__": + + p = optparse.OptionParser(description='Check the generated idaapi_include.cpp file') + p.add_option('-v', "--verbose", dest="verbose", action="store_true") + p.add_option('-i', "--input", dest="files", type="string") + p.add_option('-p', "--python-input", dest="python_files", type="string") + p.add_option('-x', "--with-hexrays", dest="with_hexrays", action="store_true") + p.add_option('-r', "--report-contents", dest="report_contents", type="string") + opts, _ = p.parse_args(sys.argv[1:]) + + if not opts.files or not opts.python_files: + p.print_help() + sys.exit(1) + + check_cpp(opts) + check_python(opts) diff --git a/tools/deploy.py b/tools/deploy.py index 2acd26b..8c6cc6c 100644 --- a/tools/deploy.py +++ b/tools/deploy.py @@ -4,55 +4,37 @@ Deploy code snips into swig interface files (c) Hex-Rays """ -import sys -import re -import os +import sys, re, os, glob major, minor, micro, _, _ = sys.version_info -if major < 2 or minor < 7: - raise Exception("Expected Python version 2.7.x, but got %s.%s.%s (from %s)" % (major, minor, micro, sys.executable)) -from argparse import ArgumentParser +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + parser = ArgumentParser() parser.add_argument("-t", "--template", required=True) parser.add_argument("-o", "--output", required=True) parser.add_argument("-m", "--module", required=True) parser.add_argument("-w", "--pywraps", required=True) +parser.add_argument("-d", "--interface-dependencies", type=str, required=True) parser.add_argument("-v", "--verbose", default=False, action="store_true") args = parser.parse_args() # creates a regular expression def make_re(tag, module, prefix): - module = "py_%s" % module s = '%(p)s<%(tag)s\(%(m)s\)>(.+?)%(p)s' % {'m': module, '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(module, template, output, pywraps, verbose): - template = convert_path(template) - output = convert_path(output) - # create regular expressions - tags = ( - ('pycode', make_re('pycode', module, '#')), - ('code', make_re('code', module, '//')), - ('inline', make_re('inline', module, '//')) - ) - # read dest file - template_str = "".join(file(template, "r").readlines()) - - # read input file(s) - input_parts = [] - for fname in ["py_%s.%s" % (module, ext) for ext in ["hpp", "py"]]: - path = os.path.join(pywraps, fname) - if os.path.isfile(path): - input_parts.append("".join(file(path, "r").readlines())) - input_str = "".join(input_parts) - - pcount = 0 +def apply_tags(template_str, input_str, tags, verbose): for desc, (expr_str, expr) in tags: # find source pattern matches = expr.findall(input_str) @@ -64,7 +46,8 @@ def deploy(module, template, output, pywraps, verbose): # find pattern in destination dest = expr.search(template_str) if not dest: - raise Exception("Found <%s> for module '%s' in input, but failed to match in destination" % (desc, module)) + raise Exception("Found <%s> for module '%s' in input, but failed to match in destination" % ( + desc, expr)) # accumulate all the strings to be replaced replaces = [] @@ -72,9 +55,54 @@ def deploy(module, template, output, pywraps, verbose): replaces.append(src) template_str = template_str[:dest.start(1)] + "\n".join(replaces) + template_str[dest.end(1):] - pcount += 1 + return template_str + +def deploy(module, template, output, pywraps, iface_deps, verbose): + template = convert_path(template) + output = convert_path(output) + + # read template file + template_str = "".join(file(template, "r").readlines()) + + # read input file(s) + all_files = glob.glob(os.path.join(pywraps, "py_%s.*" % module)) + \ + glob.glob(os.path.join(pywraps, "py_%s_*.*" % module)) + for path in all_files: + fname = os.path.basename(path) + tagname, _ = os.path.splitext(fname) + if verbose: + print("Considering file: '%s' (tagname: '%s')" % (path, tagname)) + + # create regular expressions + tags = ( + ('pycode', make_re('pycode', tagname, '#')), + ('code', make_re('code', tagname, '//')), + ('inline', make_re('inline', tagname, '//')), + ('decls', make_re('decls', tagname, '//')), + ('init', make_re('init', tagname, '//')), + ) + + input_str = "".join(file(path, "r").readlines()) + template_str = apply_tags(template_str, input_str, tags, verbose) + + # write output file with open(output, 'w') as f: + # f.write("""%module(docstring="IDA Plugin SDK API wrapper: {0}",directors="1",threads="1") {1}\n""".format( + # module, + # module if module == "idaapi" else "_ida_%s" % module)) + f.write("""%module(docstring="IDA Plugin SDK API wrapper: {0}",directors="1",threads="1") {1}\n""".format( + module, "ida_%s" % module)) + f.write("#ifndef IDA_MODULE_DEFINED\n") + f.write(""" #define IDA_MODULE_%s\n""" % module.upper()) + f.write("#define IDA_MODULE_DEFINED\n") + f.write("#endif // IDA_MODULE_DEFINED\n") + for dep in [module] + iface_deps.split(","): + if len(dep): + f.write("#ifndef HAS_DEP_ON_INTERFACE_%s\n" % dep.upper()) + f.write(" #define HAS_DEP_ON_INTERFACE_%s\n" % dep.upper()) + f.write("#endif\n") + f.write("%include \"header.i\"\n") f.write(template_str) -deploy(args.module, args.template, args.output, args.pywraps, args.verbose) +deploy(args.module, args.template, args.output, args.pywraps, args.interface_dependencies, args.verbose) diff --git a/tools/deploy/header.i.in b/tools/deploy/header.i.in new file mode 100644 index 0000000..dab5f8b --- /dev/null +++ b/tools/deploy/header.i.in @@ -0,0 +1,830 @@ +%{ +#ifndef USE_DANGEROUS_FUNCTIONS + #define USE_DANGEROUS_FUNCTIONS 1 +#endif +#include +%} + +// Auto-inserted header +// generate directors for all classes that have virtual methods +%feature("director"); +// exceptions +%feature("nodirector") const_visitor_t; +%feature("nodirector") const_vloc_visitor_t; +%feature("nodirector") enumplace_t; +%feature("nodirector") generic_linput64_t; +%feature("nodirector") generic_linput_t; +%feature("nodirector") place_t; +%feature("nodirector") idaplace_t; +%feature("nodirector") qrefcnt_obj_t; +%feature("nodirector") qstring_printer_t; +%feature("nodirector") simpleline_place_t; +%feature("nodirector") structplace_t; +%feature("nodirector") type_mapper_t; +%feature("nodirector") type_visitor_t; +%feature("nodirector") vc_printer_t; +%feature("nodirector") vd_printer_t; +%feature("nodirector") vloc_visitor_t; +%feature("nodirector") qflow_chart_t; +%feature("nodirector") lowertype_helper_t; +%feature("nodirector") ida_lowertype_helper_t; +%warnfilter(473) user_lvar_visitor_t::get_info_mapping_for_saving; // Returning a pointer or reference in a director method is not recommended +// * 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 +#pragma SWIG nowarn=514 // Director base class 'x' has no virtual destructor. + +// Do not create separate wrappers for default arguments +%feature("compactdefaultargs"); + +void qvector::grow(const unsigned int &x=0); +%ignore qvector::grow; + +void qvector::grow(const long long &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; + +%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; + +// Do not move this. We need to override the define from pro.h +#define CASSERT(type) + +// If the module is 'pro', don't import pro.h, or the %include +// at the beginning of pro.i won't have any effect. Same for all +// modules. +${ALL_IMPORTS} + +%pythoncode { +import ida_idaapi +} + +//--------------------------------------------------------------------- +%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 { + 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) { + if (i >= $self->size() || i < 0) + throw std::out_of_range("out of bounds access"); + $self->at(i) = v; + } + + inline const T&at(size_t i) { + return __getitem__(i); + } + + %pythoncode { + __iter__ = ida_idaapi._bounded_getitem_iterator + } +} + +%{ +static void __raise_ba(const std::bad_alloc &ba) +{ + Py_INCREF(PyExc_MemoryError); + PyErr_SetString(PyExc_MemoryError, "Out of memory (bad_alloc)"); +} + +static void __raise_u() +{ + Py_INCREF(PyExc_RuntimeError); + PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); +} + +static void __raise_e(const std::exception &e) +{ + const char *what = e.what(); + if ( what == NULL || what[0] == '\0' ) + { + __raise_u(); + } + else + { + Py_INCREF(PyExc_RuntimeError); + PyErr_SetString(PyExc_RuntimeError, what); + } +} + +static void __raise_de(const Swig::DirectorException &e) +{ + Py_INCREF(PyExc_RuntimeError); + PyErr_SetString(PyExc_RuntimeError, e.getMessage()); +} + +static void __raise_oor(const std::out_of_range &e) +{ + Py_INCREF(PyExc_RuntimeError); + PyErr_SetString(PyExc_IndexError, e.what()); +} + +%} + +%define %exception_set_default_handlers() +%exception { + try + { + $action + } + catch ( const std::bad_alloc &ba ) { __raise_ba(ba); SWIG_fail; } + catch ( const std::out_of_range &e ) { __raise_oor(e); SWIG_fail; } + catch ( const std::exception &e ) { __raise_e(e); SWIG_fail; } + catch ( const Swig::DirectorException &e ) { __raise_de(e); SWIG_fail; } + catch ( ... ) { __raise_u(); 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( + PTR2U64(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);" + +//------------------------------------------------------------------------- +// 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++) + { + newref_t item(PySequence_GetItem($input,i)); + if (!PyLong_Check(item.o)) + { + PyErr_SetString(PyExc_ValueError,"Expecting a sequence of long integers"); + return NULL; + } + + temp[i] = PyLong_AsUnsignedLong(item.o); + } + $1 = &temp[0]; +} + +//------------------------------------------------------------------------- +// Same, but with non-fixed sized arrays. +%typemap(in) (const tid_t *path, int plen) (qvector temp) { + if (!PySequence_Check($input)) + { + PyErr_SetString(PyExc_TypeError,"Expecting a sequence"); + return NULL; + } + int plen = PySequence_Length($input); + if ( plen <= 0 ) + { + PyErr_SetString(PyExc_TypeError, "Sequence must have at least 1 item"); + return NULL; + } + temp.resize(plen); + for ( int i =0; i < plen; ++i ) + { + newref_t item(PySequence_GetItem($input,i)); + if (!PyLong_Check(item.o)) + { + PyErr_SetString(PyExc_ValueError,"Expecting a sequence of long integers"); + return NULL; + } + + temp[i] = PyLong_AsUnsignedLong(item.o); + } + $1 = temp.begin(); + $2 = int(temp.size()); +} + +//--------------------------------------------------------------------- +%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. +} + +//--------------------------------------------------------------------- +// md5/sha256 hash retrieval +%typemap(in, numinputs=0) uchar hash[ANY] (uchar temp[$1_dim0]) +{ + $1 = temp; +} + +%typemap(argout) uchar hash[ANY] +{ + Py_XDECREF(resultobj); + if (result) + { + char buf[2*$1_dim0 + 1]; + get_hex_string(buf, sizeof(buf), $1, $1_dim0); + resultobj = PyString_FromString(buf); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } +} + +// 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; +} +// Check that the argument is None or a callable Python object +//--------------------------------------------------------------------- +%typemap(in) PyObject *pyfunc_or_none { + if ($input != Py_None && !PyCallable_Check($input)) { + PyErr_SetString(PyExc_TypeError, "Expected None or a callable object"); + return NULL; + } + $1 = $input; +} +// Check that the argument is None or a tuple +//--------------------------------------------------------------------- +%typemap(in) PyObject *tuple_or_none { + if ($input != Py_None && !PyTuple_Check($input)) { + PyErr_SetString(PyExc_TypeError, "Expected None or a tuple"); + 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);" + +%typemap(in) qtime64_t "$1 = PyLong_AsUnsignedLongLong($input);" +%typemap(out) qtime64_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 */ +}; + +// 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 + +#ifdef __EA64__ +%apply longlong *INOUT { sval_t *value }; +%apply longlong *INOUT { adiff_t *disp }; +%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 int *INOUT { adiff_t *disp }; +%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__ = ida_idaapi._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 + +// Convert all of these +%cstring_output_maxstr_none(char *buf, size_t bufsize); +%cstring_output_maxstr_none(char *buf, int 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); + + +%{ +static 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; +} +%} + +//--------------------------------------------------------------------- +%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; +} + +%inline %{ +namespace Controls +{ + class TWinControl; + class TCustomControl; // ptr to custom ida viewer +} +using Controls::TWinControl; +using Controls::TCustomControl; +%} + +namespace Forms +{ + class TForm; +} +using Forms::TForm; + +// 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 }; + +%{ +#include +#include +#include "../../../pywraps.hpp" +%} + +// END: auto-inserted header diff --git a/hrdoc.cfg b/tools/docs/hrdoc.cfg.in similarity index 98% rename from hrdoc.cfg rename to tools/docs/hrdoc.cfg.in index 2d60bcd..167ce99 100644 --- a/hrdoc.cfg +++ b/tools/docs/hrdoc.cfg.in @@ -2,8 +2,7 @@ # The list of objects to document. Objects can be named using # dotted names, module filenames, or package directory names. # Aliases for this option include "objects" and "values". -modules: idc, idautils, idaapi -#modules: pywraps +modules: idc, idautils, idaapi, %IDA_MODULES% # The type of output that should be generated. Should be one # of: html, text, latex, dvi, ps, pdf. @@ -145,4 +144,4 @@ graph-font: Helvetica # The condition upon which Epydoc should exit with a non-zero # exit status. Possible values are error, warning, docstring_warning -#fail-on: error \ No newline at end of file +#fail-on: error diff --git a/hrdoc.css b/tools/docs/hrdoc.css similarity index 100% rename from hrdoc.css rename to tools/docs/hrdoc.css diff --git a/hrdoc.py b/tools/docs/hrdoc.py similarity index 84% rename from hrdoc.py rename to tools/docs/hrdoc.py index e815e1c..0a1849e 100755 --- a/hrdoc.py +++ b/tools/docs/hrdoc.py @@ -3,9 +3,22 @@ import sys import shutil from glob import glob +try: + import epydoc.apidoc + import epydoc.cli +except ImportError as e: + import idc + import traceback + idc.Message("Couldn't import module %s\n" % traceback.format_exc()) + idc.Exit(-1) + # -------------------------------------------------------------------------- DOC_DIR = 'hr-html' -PYWRAPS_FN = 'idaapi.py' + +# -------------------------------------------------------------------------- +def log(msg): + #print msg + pass # -------------------------------------------------------------------------- def add_footer(lines): @@ -30,20 +43,20 @@ def define_idaapi_resolver(): Whenever a module named \"idaapi_\" is spotted, turn it into \"idaapi\". """ - import epydoc.apidoc dn = epydoc.apidoc.DottedName.__init__ def resolver(piece): if piece is not None and isinstance(piece, basestring) and piece.startswith("idaapi_"): - return "idaapi" + res = "idaapi" else: - return piece + res = piece + log("Resolver '%s' => '%s'" % (piece, res)) + return res def wrapper(self, *pieces, **options): return dn(self, *map(resolver, pieces), **options); epydoc.apidoc.DottedName.__init__ = wrapper # -------------------------------------------------------------------------- def gen_docs(): - import epydoc.cli import swigdocs define_idaapi_resolver() @@ -57,7 +70,7 @@ def gen_docs(): os.unlink(fn) epydoc.cli.optparse.sys.argv = [ 'epydoc', - '--config', '../hrdoc.cfg', + '--config', '../tools/docs/hrdoc.cfg', '--simple-term' ] @@ -66,7 +79,7 @@ def gen_docs(): # -------------------------------------------------------------------------- def patch_docs(): - shutil.copy('../../hrdoc.css', 'epydoc.css') + shutil.copy('../../tools/docs/hrdoc.css', 'epydoc.css') os.system('chmod +w epydoc.css') for fn in glob('*.html'): diff --git a/tools/funlines.py b/tools/funlines.py new file mode 100644 index 0000000..0e37c99 --- /dev/null +++ b/tools/funlines.py @@ -0,0 +1,85 @@ + +import os, sys, pickle, subprocess + +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + +parser = ArgumentParser() +parser.add_argument("-l", "--left", required=True) +parser.add_argument("-r", "--right", required=True) +args = parser.parse_args() + +with open(args.left, "rb") as forig: + with open(args.right, "rb") as fthen: + orig = pickle.load(forig) + then = pickle.load(fthen) + +sk_orig = sorted(orig.keys()) +sk_then = sorted(then.keys()) +for key in sk_orig: + if not key in sk_then: + print "Missing expected key: %s" % key + +for key in sk_then: + if not key in sk_orig: + print "Unexpected key found: %s" % key + +subprocess.check_call(["rm", "-r", "-f", "/tmp/diffs"]) +subprocess.check_call(["mkdir", "/tmp/diffs"]) +for key in sk_orig: + if not key in sk_then: + continue + + lines_orig = orig[key] + # sanitize lines_orig + def san(l): + l = l.replace("raise_python_stl_bad_alloc", "__raise_ba") + l = l.replace("raise_python_out_of_range_exception", "__raise_oor") + l = l.replace("raise_python_stl_exception", "__raise_e") + l = l.replace("raise_python_swig_director_exception", "__raise_de") + l = l.replace("raise_python_unknown_exception", "__raise_u") + return l + lines_orig = map(san, lines_orig) + + lines_then = then[key] + if lines_orig != lines_then: + left_path = "/tmp/left_%s" % key + right_path = "/tmp/right_%s" % key + with open(left_path, "wb") as fleft: + fleft.write("\n".join(lines_orig)) + with open(right_path, "wb") as fright: + fright.write("\n".join(lines_then)) + patch_path = "/tmp/diffs/%s.patch" % key + with open(patch_path, "wb") as fout: + subprocess.call(["diff", "-u", "-w", left_path, right_path], stdout=fout) + # Now, let's see if the patch isn't trivial/acceptable. If it is, + # then we just delete that file + trivial_crap = [ +"""- { +- Py_INCREF(Py_None); +- resultobj = Py_None; +- } ++ resultobj = SWIG_Py_Void();""", + +"""- result = (bool)__init_hexrays_plugin(arg1); ++ result = (bool)py_init_hexrays_plugin(arg1);""", + +"""- result = (bool)__install_hexrays_callback(arg1); ++ result = (bool)py_install_hexrays_callback(arg1);""", + +"""- __add_custom_viewer_popup_item(arg1,(char const *)arg2,(char const *)arg3,arg4); ++ py_add_custom_viewer_popup_item(arg1,(char const *)arg2,(char const *)arg3,arg4);""", + + ] + + with open(patch_path, "rb") as fin: + lines = fin.readlines() + diff_lines = filter(lambda l: (l.startswith("-") and not l.startswith("---")) or (l.startswith("+") and not l.startswith("+++")), lines) + diff_clob = "".join(diff_lines) + # print "\"%s\"" % diff_clob + if diff_clob.strip() in trivial_crap: + # print "Removing trivial patch: %s" % patch_path + os.remove(patch_path) diff --git a/tools/gendocs.py b/tools/gendocs.py deleted file mode 100644 index 927e8ff..0000000 --- a/tools/gendocs.py +++ /dev/null @@ -1,25 +0,0 @@ -#------------------------------------------------------------ -# gendoc.py: Generate an API cross-reference for IDAPython -#------------------------------------------------------------ -__author__ = "Gergely Erdelyi " - -import epydoc.cli - -# This is a small hack to prevent epydoc from exiting the whole -# IDA process in case something goes wrong. -def exit(eval): - print "not exiting" -epydoc.cli.sys.exit = exit - -# Fill in the command-line arguments -epydoc.cli.optparse.sys.argv = [ 'epydoc', - '--no-sourcecode', - '-u', 'http://www.d-dome.net/idapython/', - '--navlink', 'IDAPython Reference', - '--no-private', - '--simple-term', - '-o', 'idapython-reference-%d.%d.%d' % (IDAPYTHON_VERSION[:3]), - '--html', - 'idc', 'idautils', 'idaapi' ] -# Generate the documentation -epydoc.cli.cli() diff --git a/tools/genhooks/genhooks.py b/tools/genhooks/genhooks.py index 085bf49..a0c2120 100644 --- a/tools/genhooks/genhooks.py +++ b/tools/genhooks/genhooks.py @@ -291,8 +291,8 @@ def gen_notifications(out): pass_expr = pname if clinked: - out.write(" ref_t clinked_%s = create_idaapi_linked_class_instance(%s, %s);\n" % - (pname, clinked["class_define"], pname)) + out.write(" ref_t clinked_%s = create_linked_class_instance(%s, %s, %s);\n" % + (pname, clinked["module_define"], clinked["class_define"], pname)) out.write(" if ( clinked_%s == NULL )\n" % pname) out.write(" break;\n") pass_expr = "clinked_%s.o" % pname diff --git a/tools/genhooks/recipe_idphooks.py b/tools/genhooks/recipe_idphooks.py index c7bf03d..24e4b01 100644 --- a/tools/genhooks/recipe_idphooks.py +++ b/tools/genhooks/recipe_idphooks.py @@ -3,6 +3,9 @@ recipe = { "last_cb_before_debugger" : {"ignore" : True}, "last_cb_before_type_callbacks" : {"ignore" : True}, + "get_operand_info" : {"ignore" : True}, + "obsolete_get_operand_info" : {"ignore" : True}, + "loader_elf_machine" : {"ignore" : True}, "custom_ana" : { "return" : { "type" : "bool", @@ -29,6 +32,7 @@ recipe = { "op" : { "type" : "PyObject *", "clinked" : { + "module_define" : "S_IDA_UA_MODNAME", "class_define" : "S_PY_OP_T_CLSNAME", }, }, diff --git a/tools/genidaapi.py b/tools/genidaapi.py new file mode 100644 index 0000000..ca3dfc7 --- /dev/null +++ b/tools/genidaapi.py @@ -0,0 +1,24 @@ + +import sys +import string + +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + +parser = ArgumentParser() +parser.add_argument("-i", "--input", required=True) +parser.add_argument("-o", "--output", required=True) +parser.add_argument("-m", "--modules", required=True) +args = parser.parse_args() + +with open(args.input, "rb") as fin: + with open(args.output, "wb") as fout: + template = string.Template(fin.read()) + kvps = { + "MODULES" : args.modules, + "IMPORTS" : "\n".join([("from ida_%s import *" % mod) for mod in args.modules.split(",")]) + } + fout.write(template.substitute(kvps)) diff --git a/tools/genswigheader.py b/tools/genswigheader.py new file mode 100644 index 0000000..6bc5ee3 --- /dev/null +++ b/tools/genswigheader.py @@ -0,0 +1,42 @@ + +import sys +import os +import glob + +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + +parser = ArgumentParser() +parser.add_argument("-i", "--input", required=True) +parser.add_argument("-o", "--output", required=True) +parser.add_argument("-m", "--modules", required=True) +parser.add_argument("-s", "--sdk", required=True) +args = parser.parse_args() + +with open(args.input, "rb") as fin: + with open(args.output, "wb") as fout: + parts = [] + + def add_imports_from_dep(depname): + DEPNAME = depname.upper() + for dep in glob.glob(os.path.join(args.sdk, "%s.*" % depname)): + parts.append(" #ifndef HAS_DEP_ON_INTERFACE_%s" % DEPNAME) + parts.append(" %import \"{0}\"".format(os.path.basename(dep))) + parts.append(" #endif") + parts.append("") + + parts.append("#if defined(IDA_MODULE_PRO)") + parts.append("// nothing; has to be handled in pro.i") + parts.append("#else") + required_headers = ["pro", "ida", "xref", "typeinf", "enum", "netnode", "area", "lines", "kernwin", "bytes", "auto", "nalt"] + for rh in required_headers: + add_imports_from_dep(rh) + parts.append("#endif") + + # Can't use string.Template here, because it's a nightmare + # to use with a file that has many '$' is it. + template = fin.read() + fout.write(template.replace("${ALL_IMPORTS}", "\n".join(parts))) diff --git a/tools/idapython_implib.def.in b/tools/idapython_implib.def.in new file mode 100644 index 0000000..913ec40 --- /dev/null +++ b/tools/idapython_implib.def.in @@ -0,0 +1,66 @@ +LIBRARY %LIBNAME% +EXPORTS + PyWStringOrNone_Check + PyW_CreateIdcException + PyW_GetError + PyW_GetNumber + PyW_GetNumberAsIDC + PyW_GetStringAttr + PyW_IntVecToPyList + PyW_IsSequenceType + PyW_ObjectToString + PyW_PyListToEaVec + PyW_PyListToIntVec + PyW_PyListToStrVec + PyW_register_compiled_form + PyW_ShowCbErr + PyW_TryGetAttrString + PyW_TryImportModule + PyW_unregister_compiled_form + add_notify_when + create_linked_class_instance + disable_script_timeout + enable_extlang_python + enable_python_cli + idcvar_to_pyvar + lookup_info_t_commit + lookup_info_t_del_by_py_view + lookup_info_t_find_by_form + lookup_info_t_find_by_py_view + lookup_info_t_new_entry + meminfo_vec_t_to_py + py_customidamemo_t_bind + py_customidamemo_t_collect_class_callbacks_ids + py_customidamemo_t_collect_pyobject_callbacks + py_customidamemo_t_create_groups + py_customidamemo_t_del_nodes_infos + py_customidamemo_t_delete_groups + py_customidamemo_t_ensure_view_callbacks_installed + py_customidamemo_t_get_current_renderer_type + py_customidamemo_t_get_node_info + py_customidamemo_t_install_custom_viewer_handlers + py_customidamemo_t_set_current_renderer_type + py_customidamemo_t_set_groups_visibility + py_customidamemo_t_set_node_info + py_customidamemo_t_set_nodes_infos + py_customidamemo_t_unbind + pyobj_get_clink + python_timer_new + python_timer_del + pyvar_to_idcvar + pyvar_to_idcvar_or_error + pyvar_walk_list + pyw_convert_idc_args + set_script_timeout + til_deregister_python_array_type_data_t_instance + til_deregister_python_func_type_data_t_instance + til_deregister_python_ptr_type_data_t_instance + til_deregister_python_tinfo_t_instance + til_deregister_python_udt_type_data_t_instance + til_register_python_array_type_data_t_instance + til_register_python_func_type_data_t_instance + til_register_python_ptr_type_data_t_instance + til_register_python_tinfo_t_instance + til_register_python_udt_type_data_t_instance + pycim_lookup_info DATA + PLUGIN DATA diff --git a/inject_pydoc.py b/tools/inject_pydoc.py similarity index 88% rename from inject_pydoc.py rename to tools/inject_pydoc.py index a6a849d..fd0121a 100644 --- a/inject_pydoc.py +++ b/tools/inject_pydoc.py @@ -10,12 +10,29 @@ # 3) Generating the idaapi_.py file. # -import re +import sys import os -import os.path +import re + +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + +parser = ArgumentParser() +parser.add_argument("-i", "--input", required=True) +parser.add_argument("-w", "--wrapper", required=True) +parser.add_argument("-o", "--output", required=True) +parser.add_argument("-v", "--verbose", default=False, action="store_true") +args = parser.parse_args() DOCSTR_MARKER = "\"\"\"" +def verb(msg): + if args.verbose: + print msg + # -------------------------------------------------------------------------- def split_oneliner_comments(lines): out_lines = [] @@ -97,8 +114,9 @@ class collect_idaapi_pydoc_t(object): state = S_UNKNOWN lines = None - def __init__(self): + def __init__(self, input_path): self.idaapi_pydoc = {"funcs" : {}, "classes" : {}} + self.input_path = input_path def next(self): line = self.lines[0] @@ -195,10 +213,9 @@ class collect_idaapi_pydoc_t(object): elif line.startswith(self.PYDOC_END): self.state = self.S_UNKNOWN - def collect(self, dirpath): - for root, dirs, files in os.walk(dirpath): - for f in files: - self.collect_file_pydoc(os.path.join(root, f)) + def collect(self): + verb("### Processing %s" % os.path.basename(self.input_path)) + self.collect_file_pydoc(self.input_path) return self.idaapi_pydoc @@ -255,6 +272,8 @@ class idaapi_fixer_t(object): else: found = self.get_method_info(class_info, fun_name) if found is not None: + verb("fix_%s: found info for %s" % ( + "method" if class_info else "fun", fun_name)); out.append("\n") for fl in found: out.append(indent + fl) @@ -266,13 +285,14 @@ class idaapi_fixer_t(object): def fix_method(self, class_info, out): return self.fix_fun(out, class_info) - def fix_cls(self, out): + def fix_class(self, out): line = self.copy(out) cls_name = get_class_name(line) - class_info = self.get_class_info(cls_name) - if class_info is None: + found = self.get_class_info(cls_name) + if found is None: return + verb("fix_class: found info for %s" % cls_name); line = self.copy(out) indent = get_indent_string(line) @@ -281,7 +301,7 @@ class idaapi_fixer_t(object): while True: line = self.next() if line.find(DOCSTR_MARKER) > -1: - doc = class_info["doc"] + doc = found["doc"] if doc is not None: out.append("\n") for dl in doc: @@ -300,15 +320,15 @@ class idaapi_fixer_t(object): if line.startswith(indent) or line.strip() == "": if line.startswith(method_start): self.push_front(line) - self.fix_method(class_info, out) + self.fix_method(found, out) else: out.append(line) else: self.push_front(line) break - def fix_file(self, idaapi_filename, out_filename): - with open(idaapi_filename, "rt") as f: + def fix_file(self, input_path, out_path): + with open(input_path, "rt") as f: self.lines = split_oneliner_comments(f.readlines()) out = [] while len(self.lines) > 0: @@ -319,22 +339,16 @@ class idaapi_fixer_t(object): self.fix_fun(out) elif line.startswith("class "): self.push_front(line) - self.fix_cls(out) + self.fix_class(out) else: out.append(line) - with open(out_filename, "wt") as o: + with open(out_path, "wt") as o: for ol in out: o.write(ol) o.write("\n") # -------------------------------------------------------------------------- -if __name__ == '__main__': - import sys - collecter = collect_idaapi_pydoc_t() - collected = collecter.collect(sys.argv[1]) - # import pprint - # pprint.pprint(collected, indent=2) - fixer = idaapi_fixer_t(collected) - target_file = sys.argv[2] - result_file = sys.argv[3] - fixer.fix_file(target_file, result_file) +collecter = collect_idaapi_pydoc_t(args.wrapper) +collected = collecter.collect() +fixer = idaapi_fixer_t(collected) +fixer.fix_file(args.input, args.output) diff --git a/tools/patch_constants.py b/tools/patch_constants.py new file mode 100644 index 0000000..551a234 --- /dev/null +++ b/tools/patch_constants.py @@ -0,0 +1,192 @@ + +import re + +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + +parser = ArgumentParser(description='Patch calling conventions for some functions, so it builds on windows') +parser.add_argument("-f", "--file", required=True) +parser.add_argument("-v", "--verbose", default=False, action="store_true") +args = parser.parse_args() + +outlines = [] +STAT_SEEKING = 1 +STAT_COLLECTING = 2 + +start_collecting_re = re.compile(r"^SWIG_init\(void\)") +end_collecting_re = re.compile(r"^\s*/\* Initialize threading \*/") + +# SWIG_Python_SetConstant(d, "BADADDR",SWIG_From_unsigned_SS_int(static_cast< unsigned int >(ea_t(-1)))); +set_constant_re = re.compile(r"^\s*SWIG_Python_SetConstant\(\s*d\s*,([^,]*),(.*)\);$") + +# Supported types we can simply expression for +# SWIG_From_int(static_cast< int >(0x00C0)) +pyobj_expr_int = re.compile(r"SWIG_From_int\(static_cast<\s*int\s*>\((.*)\)\)") +# SWIG_From_unsigned_SS_int(static_cast< unsigned int >((1u << 16))) +pyobj_expr_uint = re.compile(r"SWIG_From_unsigned_SS_int\(static_cast<\s*unsigned\s+int\s*>\((.*)\)\)") +# SWIG_From_long(static_cast< long >(0x00002000L)) +pyobj_expr_long = re.compile(r"SWIG_From_long\(static_cast<\s*long\s*>\((.*)\)\)") +# SWIG_From_unsigned_SS_long(static_cast< unsigned long >(0xF0000000LU)) +pyobj_expr_ulong = re.compile(r"SWIG_From_unsigned_SS_long\(static_cast<\s*unsigned\s+long\s*>\((.*)\)\)") +# SWIG_FromCharPtr("\22") +pyobj_expr_str = re.compile(r"SWIG_FromCharPtr\((.*)\)") + + +status = STAT_SEEKING +class Foldable(object): + def __init__(self, re, enum, initializer, replacement): + self.re = re + self.enum = enum + self.initializer = initializer + self.replacement = replacement + self.found_any = False + +subexprs = { + "i" : Foldable( + pyobj_expr_int, + "cit_int", + "i", + "SWIG_From_int(static_cast< int >(ci.val.i))"), + "u" : Foldable( + pyobj_expr_uint, + "cit_uint", + "u", + "SWIG_From_unsigned_SS_int(static_cast< unsigned int >(ci.val.u))"), + "l" : Foldable( + pyobj_expr_long, + "cit_long", + "l", + "SWIG_From_long(static_cast< long >(ci.val.l))"), + "ul" : Foldable( + pyobj_expr_ulong, + "cit_ulong", + "ul", + "SWIG_From_unsigned_SS_long(static_cast< unsigned long >(ci.val.ul))"), + "s" : Foldable( + pyobj_expr_str, + "cit_charptr", + "s", + "SWIG_FromCharPtr(ci.val.s)"), +} + +def generate_constants(constants): + # now, generate the array & loop + outlines.append(""" +/* 'SetConstant' replacement */ +union ida_local cival_t +{ + PyObject *o; + int i; + unsigned int u; + long l; + unsigned long ul; + const char *s; +}; + +enum cit_t +{ + cit_int = 1, + cit_uint, + cit_long, + cit_ulong, + cit_charptr, + cit_obj, +}; + +static const ida_local struct ci_t +{ + const char *name; + cival_t val; + cit_t type; +} cis[%d] = { +""" % len(constants)) + + for c in constants: + name = c[0] + expr = c[1] + init = "o" + citype = "cit_obj" + + # Analyze expression. If it is one of the + # supported types, let's simplify the code. + for kind in subexprs.keys(): + subexpr = subexprs[kind] + sematch = subexpr.re.search(expr) + if sematch: + citype = subexpr.enum + expr = sematch.group(1) + init = subexpr.initializer + subexpr.found_any = True + break + outlines.append("\t{%s, {%s: (%s)}, %s},\n" % (name, init, expr, citype)) + + outlines.append(""" +}; + +for ( size_t _cidx = 0; _cidx < qnumber(cis); ++_cidx ) +{ + const ci_t &ci = cis[_cidx]; + PyObject *o = NULL; + switch ( ci.type ) + { +""") + + for kind in subexprs.keys(): + subexpr = subexprs[kind] + if subexpr.found_any: + outlines.append(""" + case %s: + o = %s; + break; + """ % (subexpr.enum, subexpr.replacement)) + + outlines.append(""" + case cit_obj: + o = ci.val.o; + break; + default: INTERR(0); + } + SWIG_Python_SetConstant(d, ci.name, o); +} +""") + +with open(args.file, "rb") as f: + constants = [] + + for line in f: + if status == STAT_SEEKING: + outlines.append(line) + if start_collecting_re.match(line): + status = STAT_COLLECTING + if args.verbose: + print "Starting to collect at line: '%s'" % line + elif status == STAT_COLLECTING: + if end_collecting_re.match(line): + outlines.append(line) + + if len(constants): + generate_constants(constants) + + status = STAT_SEEKING + if args.verbose: + print "Done collecting at line: '%s'" % line + else: + match = set_constant_re.search(line) + if match: + tpl = (match.group(1), match.group(2)) + constants.append(tpl) + if args.verbose: + print "Found 'SetConstant' expression: %s => %s" % tpl + else: + outlines.append(line) + +import tempfile +temp = tempfile.NamedTemporaryFile(delete=False) +temp.write("".join(outlines)) +temp.close() + +import shutil +shutil.move(temp.name, args.file) diff --git a/tools/patch_directors_cc.py b/tools/patch_directors_cc.py new file mode 100644 index 0000000..6af3529 --- /dev/null +++ b/tools/patch_directors_cc.py @@ -0,0 +1,82 @@ + +try: + from argparse import ArgumentParser +except: + print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" + raise + +parser = ArgumentParser(description='Patch calling conventions for some functions, so it builds on windows') +parser.add_argument("-f", "--file", required=True) +parser.add_argument("-v", "--verbose", default=False, action="store_true") +args = parser.parse_args() + +patches = [ + # text_sink_t + "virtual int idaapi print", + + # user_lvar_modifier_t + "virtual bool idaapi modify_lvars", + + # ctree_visitor_t + "virtual int idaapi visit_insn", + "virtual int idaapi visit_expr", + "virtual int idaapi leave_insn", + "virtual int idaapi leave_expr", + + # ctree_parentee_t + "virtual int idaapi visit_insn", + "virtual int idaapi visit_expr", + "virtual int idaapi leave_insn", + "virtual int idaapi leave_expr", + + # cfunc_parentee_t + "virtual int idaapi visit_insn", + "virtual int idaapi visit_expr", + "virtual int idaapi leave_insn", + "virtual int idaapi leave_expr", + + # enum_member_visitor_t + "virtual int idaapi visit_enum_member", + + # struct_field_visitor_t + "virtual int idaapi visit_field", + + # tinfo_visitor_t + "virtual int idaapi visit_type", + + # aloc_visitor_t + "virtual int idaapi visit_location", + + # const_aloc_visitor_t + "virtual int idaapi visit_location", + + # area_visitor2_t + "virtual int idaapi visit_area", + + # highlighter_cbs_t + "virtual void idaapi set_style", + "virtual int32 idaapi prev_block_state", + "virtual int32 idaapi cur_block_state", + "virtual void idaapi set_block_state", +] + + +outlines = [] +with open(args.file, "rb") as f: + lines = f.readlines() + for line in lines: + for patch in patches: + from_text = patch.replace("idaapi ", "") + if line.find(from_text) > -1: + line = line.replace(from_text, patch) + patches.remove(patch) + break + outlines.append(line) + +import tempfile +temp = tempfile.NamedTemporaryFile(delete=False) +temp.writelines(outlines) +temp.close() + +import shutil +shutil.move(temp.name, args.file)