本部分的主要功能是提供对程序元素的修改能力,支持重命名局部变量、全局变量和函数,设置全局变量和局部变量的类型,以及修改函数原型。可以读取全局变量的编译时值,例如从C语言声明创建或更新自定义类型,并能刷新反编译器窗口以实时显示修改结果,方便逆向分析中的人工调整和类型标注。
(1)下面代码的功能是定义了一个名为 refresh_decompiler_widget 的函数,用于刷新反编译器窗口,确保所做的更改能够立即显示出来。
def refresh_decompiler_widget(): """刷新反编译器窗口""" widget = ida_kernwin.get_current_widget() if widget is not None: vu = ida_hexrays.get_widget_vdui(widget) if vu is not None: vu.refresh_ctext()(2)下面代码的功能是定义了一个名为 refresh_decompiler_ctext 的函数,用于刷新指定函数地址的反编译文本,确保反编译器视图是最新的。
def refresh_decompiler_ctext(function_address: int): """刷新函数的反编译文本""" error = ida_hexrays.hexrays_failure_t() cfunc: ida_hexrays.cfunc_t = ida_hexrays.decompile_func(function_address, error, ida_hexrays.DECOMP_WARNINGS) if cfunc: cfunc.refresh_func_ctext()(3)下面代码的功能是定义了一个名为 rename_local_variable 的函数,用于重命名函数中的局部变量。它首先获取函数对象,然后尝试重命名局部变量,如果失败则抛出错误。
@jsonrpc @idawrite def rename_local_variable( function_address: Annotated[str, "包含变量的函数地址"], old_name: Annotated[str, "变量的当前名称"], new_name: Annotated[str, "变量的新名称(空字符串表示默认名称)"] ): """重命名函数中的局部变量""" func = idaapi.get_func(parse_address(function_address)) if not func: raise IDAError(f"地址{function_address}处未找到函数") if not ida_hexrays.rename_lvar(func.start_ea, old_name, new_name): raise IDAError(f"重命名函数{hex(func.start_ea)}中的局部变量{old_name}失败") refresh_decompiler_ctext(func.start_ea)(4)下面代码的功能是定义了一个名为 rename_global_variable 的函数,用于重命名全局变量。它通过获取全局变量的地址,然后尝试重命名该变量,如果失败则抛出错误。
@jsonrpc @idawrite def rename_global_variable( old_name: Annotated[str, "全局变量的当前名称"], new_name: Annotated[str, "全局变量的新名称(空字符串表示默认名称)"] ): """重命名全局变量""" ea = idaapi.get_name_ea(idaapi.BADADDR, old_name) if not idaapi.set_name(ea, new_name): raise IDAError(f"将全局变量{old_name}重命名为{new_name}失败") refresh_decompiler_ctext(ea)(5)下面代码的功能是定义了一个名为 set_global_variable_type 的函数,用于设置全局变量的类型。它通过获取变量地址和新类型信息,然后应用新类型,如果失败则抛出错误。
@jsonrpc @idawrite def set_global_variable_type( variable_name: Annotated[str, "全局变量的名称"], new_type: Annotated[str, "变量的新类型"] ): """设置全局变量的类型""" ea = idaapi.get_name_ea(idaapi.BADADDR, variable_name) tif = get_type_by_name(new_type) if not tif: raise IDAError(f"解析的声明不是变量类型") if not ida_typeinf.apply_tinfo(ea, tif, ida_typeinf.PT_SIL): raise IDAError(f"应用类型失败")(6)下面代码的功能是定义了一个名为 get_global_variable_value_by_name 的函数,用于读取全局变量的值(如果编译时已知)。它通过变量名获取变量地址,然后调用内部函数获取变量值。
@jsonrpc @idaread def get_global_variable_value_by_name(variable_name: Annotated[str, "全局变量的名称"]) -> str: """ 读取全局变量的值(如果编译时已知) 优先使用此函数,而非`data_read_*`函数。 """ ea = idaapi.get_name_ea(idaapi.BADADDR, variable_name) if ea == idaapi.BADADDR: raise IDAError(f"未找到全局变量{variable_name}") return get_global_variable_value_internal(ea)(7)下面代码的功能是定义了一个名为 get_global_variable_value_at_address 的函数,该函数通过地址读取全局变量的值(如果编译时已知)。它解析地址并调用内部函数获取变量值。
@jsonrpc @idaread def get_global_variable_value_at_address(ea: Annotated[str, "全局变量的地址"]) -> str: """ 通过地址读取全局变量的值(如果编译时已知) 优先使用此函数,而非`data_read_*`函数。 """ ea = parse_address(ea) return get_global_variable_value_internal(ea)(8)下面代码的功能是定义了一个名为 get_global_variable_value_internal 的内部函数,用于根据地址读取全局变量的值。它获取变量的类型信息,确定变量大小,然后根据大小读取并返回变量的值。
def get_global_variable_value_internal(ea: int) -> str: """内部函数:根据地址读取全局变量的值""" # 获取变量的类型信息 tif = ida_typeinf.tinfo_t() if not ida_nalt.get_tinfo(tif, ea): # 无类型信息,尝试通过名称推断大小 if not ida_bytes.has_any_name(ea): raise IDAError(f"无法获取地址{ea:#x}处变量的类型信息") size = ida_bytes.get_item_size(ea) if size == 0: raise IDAError(f"无法获取地址{ea:#x}处变量的类型信息") else: # 确定变量大小 size = tif.get_size() # 根据大小读取值 if size == 0 and tif.is_array() and tif.get_array_element().is_decl_char(): return_string = idaapi.get_strlit_contents(ea, -1, 0).decode("utf-8").strip() return f"\"{return_string}\"" elif size == 1: return hex(ida_bytes.get_byte(ea)) elif size == 2: return hex(ida_bytes.get_word(ea)) elif size == 4: return hex(ida_bytes.get_dword(ea)) elif size == 8: return hex(ida_bytes.get_qword(ea)) else: # 其他大小返回原始字节 return ' '.join(hex(x) for x in ida_bytes.get_bytes(ea, size))(9)下面代码的功能是定义了一个名为 rename_function 的函数,该函数用于重命名函数。首先获取函数对象,然后尝试重命名函数,如果失败则抛出错误。
@jsonrpc @idawrite def rename_function( function_address: Annotated[str, "要重命名的函数地址"], new_name: Annotated[str, "函数的新名称(空字符串表示默认名称)"] ): """重命名函数""" func = idaapi.get_func(parse_address(function_address)) if not func: raise IDAError(f"地址{function_address}处未找到函数") if not idaapi.set_name(func.start_ea, new_name): raise IDAError(f"将函数{hex(func.start_ea)}重命名为{new_name}失败") refresh_decompiler_ctext(func.start_ea)(10)下面代码的功能是定义了一个名为 set_function_prototype 的函数,该函数用于设置指定函数的原型。它首先获取函数对象,然后尝试应用新的函数原型,如果失败则抛出错误。成功应用后,会刷新反编译器窗口以显示更改。
@jsonrpc @idawrite def set_function_prototype( function_address: Annotated[str, "函数的地址"], prototype: Annotated[str, "函数的新原型"] ): """设置函数的原型""" func = idaapi.get_func(parse_address(function_address)) if not func: raise IDAError(f"地址{function_address}处未找到函数") try: tif = ida_typeinf.tinfo_t(prototype, None, ida_typeinf.PT_SIL) if not tif.is_func(): raise IDAError(f"解析的声明不是函数类型") if not ida_typeinf.apply_tinfo(func.start_ea, tif, ida_typeinf.PT_SIL): raise IDAError(f"应用类型失败") refresh_decompiler_ctext(func.start_ea) except Exception as e: raise IDAError(f"解析原型字符串失败:{prototype}")(11)下面代码的功能是定义了一个名为 my_modifier_t 的类,该类继承自类ida_hexrays.user_lvar_modifier_t,用于修改局部变量的类型。类y_modifier_t包含了始化方法和修改局部变量类型的 modify_lvars 方法。
class my_modifier_t(ida_hexrays.user_lvar_modifier_t): """用于修改局部变量类型的自定义修改器""" def __init__(self, var_name: str, new_type: ida_typeinf.tinfo_t): ida_hexrays.user_lvar_modifier_t.__init__(self) self.var_name = var_name self.new_type = new_type def modify_lvars(self, lvars): """修改局部变量的类型""" for lvar_saved in lvars.lvvec: lvar_saved: ida_hexrays.lvar_saved_info_t if lvar_saved.name == self.var_name: lvar_saved.type = self.new_type return True return False(12)下面代码的功能是定义了一个名为 parse_decls_ctypes 的函数,该函数通过 ctypes 解析 C 类型声明,主要用于在 Windows 平台上获取错误信息。
def parse_decls_ctypes(decls: str, hti_flags: int) -> tuple[int, str]: """通过ctypes解析声明(主要用于Windows平台获取错误信息)""" if sys.platform == "win32": import ctypes assert isinstance(decls, str), "decls必须是字符串" assert isinstance(hti_flags, int), "hti_flags必须是整数" c_decls = decls.encode("utf-8") c_til = None ida_dll = ctypes.CDLL("ida") ida_dll.parse_decls.argtypes = [ ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_int, ] ida_dll.parse_decls.restype = ctypes.c_int messages = [] @ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p) def magic_printer(fmt: bytes, arg1: bytes): if fmt.count(b"%") == 1 and b"%s" in fmt: formatted = fmt.replace(b"%s", arg1) messages.append(formatted.decode("utf-8")) return len(formatted) + 1 else: messages.append(f"不支持的magic_printer格式:{repr(fmt)}") return 0 errors = ida_dll.parse_decls(c_til, c_decls, magic_printer, hti_flags) else: # 注意:上述方法也可能在其他平台上工作,但未经过测试,且可变参数ABI存在差异。 errors = ida_typeinf.parse_decls(None, decls, False, hti_flags) messages = [] return errors, messages(13)下面代码的功能是定义了一个名为 declare_c_type 的函数,该函数从 C 声明创建或更新本地类型。它解析 C 声明,如果解析成功则返回成功信息,否则抛出错误。
@jsonrpc @idawrite def declare_c_type( c_declaration: Annotated[str, "类型的C声明。例如:typedef int foo_t; struct bar { int a; bool b; };"] ): """从C声明创建或更新本地类型""" # PT_SIL:抑制警告对话框(尽管在此处似乎不必要) # PT_EMPTY:允许空类型(也不必要?) # PT_TYP:带结构体标签打印状态消息 flags = ida_typeinf.PT_SIL | ida_typeinf.PT_EMPTY | ida_typeinf.PT_TYP errors, messages = parse_decls_ctypes(c_declaration, flags) pretty_messages = "\n".join(messages) if errors > 0: raise IDAError(f"解析类型失败:\n{c_declaration}\n\n错误:\n{pretty_messages}") return f"成功\n\n信息:\n{pretty_messages}"(14)下面代码的功能是定义了一个名为 set_local_variable_type 的函数,该函数用于设置局部变量的类型。它首先解析新类型,然后获取函数对象,尝试重命名局部变量,接着应用新类型,如果失败则抛出错误。
@jsonrpc @idawrite def set_local_variable_type( function_address: Annotated[str, "包含变量的反编译函数地址"], variable_name: Annotated[str, "变量名"], new_type: Annotated[str, "变量的新类型"] ): """设置局部变量的类型""" try: # 某些版本的IDA不支持此构造函数 new_tif = ida_typeinf.tinfo_t(new_type, None, ida_typeinf.PT_SIL) except Exception: try: new_tif = ida_typeinf.tinfo_t() # parse_decl需要类型后带分号 ida_typeinf.parse_decl(new_tif, None, new_type + ";", ida_typeinf.PT_SIL) except Exception: raise IDAError(f"解析类型失败:{new_type}") func = idaapi.get_func(parse_address(function_address)) if not func: raise IDAError(f"地址{function_address}处未找到函数") if not ida_hexrays.rename_lvar(func.start_ea, variable_name, variable_name): raise IDAError(f"未找到局部变量:{variable_name}") modifier = my_modifier_t(variable_name, new_tif) if not ida_hexrays.modify_user_lvars(func.start_ea, modifier): raise IDAError(f"修改局部变量失败:{variable_name}") refresh_decompiler_ctext(func.start_ea)