Skip to content

Instantly share code, notes, and snippets.

@ifarbod
Last active March 13, 2025 15:55
Show Gist options
  • Select an option

  • Save ifarbod/4bdb72fbf226f4e646b4b611d428d0ae to your computer and use it in GitHub Desktop.

Select an option

Save ifarbod/4bdb72fbf226f4e646b4b611d428d0ae to your computer and use it in GitHub Desktop.
IDA Pro plugin, SIGMaker remade with IDAPython, tested with ida 9.
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