Last active
March 13, 2025 15:55
-
-
Save ifarbod/4bdb72fbf226f4e646b4b611d428d0ae to your computer and use it in GitHub Desktop.
IDA Pro plugin, SIGMaker remade with IDAPython, tested with ida 9.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ida_ua | |
| import ida_funcs | |
| import ida_name | |
| import ida_idaapi | |
| import ida_kernwin | |
| import ida_bytes | |
| def bit(n): | |
| return 1 << n | |
| # Define the operand types using ida_ua | |
| WILDCARDABLE_OPERAND_TYPE_BITMASK = ( | |
| bit(ida_ua.o_reg) | |
| | bit(ida_ua.o_mem) | |
| | bit(ida_ua.o_phrase) | |
| | bit(ida_ua.o_displ) | |
| | bit(ida_ua.o_imm) | |
| | bit(ida_ua.o_far) | |
| | bit(ida_ua.o_near) | |
| | bit(ida_ua.o_idpspec0) | |
| | bit(ida_ua.o_idpspec1) | |
| | bit(ida_ua.o_idpspec2) | |
| | bit(ida_ua.o_idpspec3) | |
| | bit(ida_ua.o_idpspec4) | |
| | bit(ida_ua.o_idpspec5) | |
| ) | |
| # value: u8, is_wildcard: bool = sigbyte | |
| class SignatureByte: | |
| def __init__(self, byte: int, is_wildcard: bool): | |
| self.byte = byte | |
| self.is_wildcard = is_wildcard | |
| # sigbytes = list[sigbyte] | |
| class Signature: | |
| def __init__(self): | |
| self.signature: list[SignatureByte] = [] | |
| def append(self, byte, is_wildcard): | |
| self.signature.append(SignatureByte(byte, is_wildcard)) | |
| def add_byte_to_signature(signature: Signature, address, wildcard): | |
| signature.append(ida_bytes.get_byte(address), wildcard) | |
| def add_bytes_to_signature(signature, address, count, wildcard): | |
| for i in range(count): | |
| add_byte_to_signature(signature, address + i, wildcard) | |
| def trim_signature(signature: Signature): | |
| # Find the last non-wildcard byte | |
| for i in range(len(signature.signature) - 1, -1, -1): | |
| if not signature.signature[i].is_wildcard: | |
| break | |
| # Trim the signature | |
| signature.signature = signature.signature[: i + 1] | |
| def to_ida_sig(signature: Signature, double_qm: bool = False): | |
| result = [] | |
| for byte in signature.signature: | |
| if byte.is_wildcard: | |
| result.append("??" if double_qm else "?") | |
| else: | |
| result.append(f"{byte.byte:02X}") | |
| result.append(" ") | |
| # Join the list into a single string and remove the trailing space | |
| signature_string = "".join(result).strip() | |
| return signature_string | |
| def get_function_addr_from_name(name: str) -> str: | |
| return ida_name.get_name_ea(ida_idaapi.BADADDR, name) | |
| def get_operand(instruction: ida_ua.insn_t, operand_offset, operand_length, operand_type_bitmask): | |
| # Handle metapc x86/64 | |
| # Iterate all operands | |
| for op in instruction.ops: # type: ignore | |
| # Skip if we have no operand | |
| if op.type == ida_ua.o_void: | |
| continue | |
| # offb = 0 means unknown | |
| if op.offb == 0: | |
| continue | |
| # Apply operand bitmask filter | |
| if bit(op.type) & operand_type_bitmask == 0: | |
| continue | |
| operand_offset[0] = op.offb | |
| operand_length[0] = instruction.size - op.offb | |
| return True | |
| return False | |
| def generate_sig(ea_start, ea_end, wildcard_operands, operand_type_bitmask): | |
| if ea_start == ida_idaapi.BADADDR or ea_end == ida_idaapi.BADADDR: | |
| raise ValueError("Invalid address") | |
| signature = Signature() | |
| # Copy data section, no wildcards | |
| if not ida_bytes.is_code(ida_bytes.get_flags(ea_start)): | |
| add_bytes_to_signature(signature, ea_start, ea_end - ea_start, False) | |
| return signature | |
| current_address = ea_start | |
| while True: | |
| # Handle IDA "cancel" event | |
| if ida_kernwin.user_cancelled(): | |
| print("Canceled") | |
| raise RuntimeError("Canceled") | |
| insn = ida_ua.insn_t() | |
| current_instruction_length = ida_ua.decode_insn(insn, current_address) | |
| if current_instruction_length <= 0: | |
| if not signature.signature: | |
| raise Exception("Failed to decode first instruction") | |
| print(f"Signature reached end of executable code @ {hex(current_address)}") | |
| # If we have some bytes left, add them | |
| if current_address < ea_end: | |
| add_bytes_to_signature(signature, current_address, ea_end - current_address, False) | |
| trim_signature(signature) | |
| return signature | |
| operand_offset = [0] | |
| operand_length = [0] | |
| if ( | |
| wildcard_operands | |
| and get_operand(insn, operand_offset, operand_length, operand_type_bitmask) | |
| and operand_length[0] > 0 | |
| ): | |
| # Add opcodes | |
| add_bytes_to_signature(signature, current_address, operand_offset[0], False) | |
| # Wildcards for operands | |
| add_bytes_to_signature(signature, current_address + operand_offset[0], operand_length[0], True) | |
| # If the operand is on the "left side", add the operator from the "right side" | |
| if operand_offset[0] == 0: | |
| add_bytes_to_signature( | |
| signature, | |
| current_address + operand_length[0], | |
| current_instruction_length - operand_length[0], | |
| False, | |
| ) | |
| else: | |
| # No operand, add all bytes | |
| add_bytes_to_signature(signature, current_address, current_instruction_length, False) | |
| current_address += current_instruction_length | |
| if current_address >= ea_end: | |
| trim_signature(signature) | |
| return signature | |
| raise Exception("Unknown error") | |
| # PUBLIC | |
| def generate_sig_for_range(ea_start, ea_end): | |
| return generate_sig( | |
| ea_start, ea_end, wildcard_operands=True, operand_type_bitmask=WILDCARDABLE_OPERAND_TYPE_BITMASK | |
| ) | |
| # PUBLIC | |
| def generate_sig_for_func_address(func_addr): | |
| f = ida_funcs.get_func(func_addr) | |
| return generate_sig_for_range(f.start_ea, f.end_ea) | |
| # PUBLIC | |
| def generate_sig_for_func_name(func_name): | |
| return generate_sig_for_func_address(get_function_addr_from_name(func_name)) | |
| def main(): | |
| # print(to_ida_sig(generate_sig_for_func_name("?Clear@CAttractorScanner@@QAEXXZ"))) | |
| print(to_ida_sig(generate_sig_for_func_address(0x5FFFD0))) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment