Last active
September 6, 2021 02:38
-
-
Save nosoop/82a6195b2140f708a039399b40278cf1 to your computer and use it in GitHub Desktop.
makesig.idc-like script in Ghidra
This file contains 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
#Generates a SourceMod-ready signature. | |
#@author nosoop | |
#@category _NEW_ | |
#@keybinding | |
#@menupath | |
#@toolbar | |
import ghidra.program.model.lang.OperandType as OperandType | |
import ghidra.program.model.lang.Register as Register | |
def uniqueSig(bs): | |
""" | |
Returns true if only one result containing the pattern was found. | |
""" | |
result = findBytes(None, bs, 2) | |
return len(result) == 1 | |
def dumpOperandInfo(ins, op): | |
t = hex(ins.getOperandType(op)) | |
print(' ' + str(ins.getPrototype().getOperandValueMask(op)) + ' ' + str(t)) | |
# TODO if register | |
for opobj in ins.getOpObjects(op): | |
print(' - ' + str(opobj)) | |
def shouldMaskOperand(ins, opIndex): | |
optype = ins.getOperandType(opIndex) | |
# if any(reg.getName() == "EBP" for reg in filter(lambda op: isinstance(op, Register), ins.getOpObjects(opIndex))): | |
# return False | |
return optype & OperandType.DYNAMIC or optype & OperandType.ADDRESS | |
def getMaskedInstruction(ins): | |
""" | |
Returns a generator that outputs either a byte to match or None if the byte should be masked. | |
""" | |
# print(ins) | |
# resulting mask should match the instruction length | |
mask = [0] * ins.length | |
proto = ins.getPrototype() | |
# iterate over operands and mask bytes | |
for op in range(proto.getNumOperands()): | |
# dumpOperandInfo(ins, op) | |
# TODO deal with partial byte masks | |
if shouldMaskOperand(ins, op): | |
mask = [ m | v & 0xFF for m, v in zip(mask, proto.getOperandValueMask(op).getBytes()) ] | |
# print(' ' + str(mask)) | |
# return None if the byte should be masked, else yield the byte | |
# TODO improve this logic | |
for m, b in zip(mask, ins.getBytes()): | |
if m == 0xFF: | |
yield None | |
else: | |
yield b & 0xFF | |
if __name__ == "__main__": | |
fm = currentProgram.getFunctionManager() | |
fn = fm.getFunctionContaining(currentAddress) | |
if not fn: | |
raise Exception("Not in a function") | |
cm = currentProgram.getCodeManager() | |
ins = cm.getInstructionAt(fn.getEntryPoint()) | |
if not ins: | |
raise Exception("Could not find entry point to function") | |
pattern = "" # contains pattern string (supports regular expressions) | |
byte_pattern = [] # contains integers 0x00 to 0xFF, or None if the byte was masked | |
found = False | |
# TODO proper find end of function | |
# for i in range(0, 200): | |
while not found and fm.getFunctionContaining(ins.getAddress()) == fn: | |
for entry in getMaskedInstruction(ins): | |
if entry is None: | |
pattern += '.' | |
byte_pattern.append(None) | |
else: | |
pattern += r'\x{:02x}'.format(entry) | |
byte_pattern.append(entry) | |
if uniqueSig(pattern): | |
found = True | |
break | |
ins = ins.getNext() | |
if not found: | |
print(" ".join('{:02X}'.format(b) if b is not None else '?' for b in byte_pattern)) | |
raise Exception("Could not find unique signature") | |
else: | |
print("Signature for " + fn.getName()) | |
print(" ".join('{:02X}'.format(b) if b is not None else '?' for b in byte_pattern)) | |
print("".join(r'\x{:02X}'.format(b) if b is not None else r'\x2A' for b in byte_pattern)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment