Skip to content

Instantly share code, notes, and snippets.

@R3MRUM
Forked from alexander-hanel/yara_ida_search.py
Last active December 6, 2019 20:29
Show Gist options
  • Save R3MRUM/f7b1b5ede9876c7e81385c138c871b13 to your computer and use it in GitHub Desktop.
Save R3MRUM/f7b1b5ede9876c7e81385c138c871b13 to your computer and use it in GitHub Desktop.
Minimum Yara Search for IDAPYTHON
import yara
import operator
import idautils
SEARCH_CASE = 4
SEARCH_REGEX = 8
SEARCH_NOBRK = 16
SEARCH_NOSHOW = 32
SEARCH_UNICODE = 64
SEARCH_IDENT = 128
SEARCH_BRK = 256
class YaraIDASearch():
def __init__(self):
self.mem_results = ""
self.mem_offsets = []
if not self.mem_results:
self._get_memory()
def _wowrange(self, start, stop, step=1):
# source https://stackoverflow.com/a/1482502
if step == 0:
raise ValueError('step must be != 0')
elif step < 0:
proceed = operator.gt
else:
proceed = operator.lt
while proceed(start, stop):
yield start
start += step
def _get_memory(self):
print "Status: Loading memory for Yara."
result = ""
segments_starts = [ea for ea in idautils.Segments()]
offsets = []
start_len = 0
for start in segments_starts:
end = idc.get_segm_end(start)
for ea in self._wowrange(start, end):
result += chr(idc.Byte(ea))
offsets.append((start, start_len, len(result)))
start_len = len(result)
print "Status: Memory has been loaded."
self.mem_results = result
self.mem_offsets = offsets
def _to_virtual_address(self, offset, segments):
va_offset = 0
for seg in segments:
if seg[1] <= offset < seg[2]:
va_offset = seg[0] + (offset - seg[1])
return va_offset
def _init_sig(self, sig_type, pattern, sflag):
if SEARCH_REGEX & sflag:
signature = "/%s/" % pattern
if SEARCH_CASE & sflag:
# ida is not case sensitive by default but yara is
pass
else:
signature += " nocase"
if SEARCH_UNICODE & sflag:
signature += " wide"
elif sig_type == "binary":
signature = "{ %s }" % pattern
elif sig_type == "text" and (SEARCH_REGEX & sflag) == False:
signature = '"%s"' % pattern
if SEARCH_CASE & sflag:
pass
else:
signature += " nocase"
if SEARCH_UNICODE & sflag:
signature += " wide"
yara_rule = "rule foo : bar { strings: $a = %s condition: $a }" % signature
return yara_rule
def _compile_rule(self, signature):
try:
rules = yara.compile(source=signature)
except Exception as e:
print "ERROR: Cannot compile Yara rule %s" % e
return False, None
return True, rules
def _search(self, signature):
status, rules = self._compile_rule(signature)
if not status:
return False, None
values = []
matches = rules.match(data=self.mem_results)
if not matches:
return False, None
for rule_match in matches:
for match in rule_match.strings:
match_offset = match[0]
values.append(self._to_virtual_address(match_offset, self.mem_offsets))
return values
def find_binary(self, bin_str, sflag=0):
yara_sig = self._init_sig("binary", bin_str, sflag)
offset_matches = self._search(yara_sig)
return offset_matches
def find_text(self, q_str, sflag=0):
yara_sig = self._init_sig("text", q_str, sflag)
offset_matches = self._search(yara_sig)
return offset_matches
def find_sig(self, yara_sig):
#Uncomment the following line if your existing signatures check for
#MZ header so that you dont have to modify all of your signatures:
#yara_sig = yara_sig.replace('uint16(0) == 0x5A4D and ', '')
offset_matches = self._search(yara_sig)
return offset_matches
def reload_scan_memory(self):
self._get_memory()
@R3MRUM
Copy link
Author

R3MRUM commented Dec 6, 2019

Added find_sig function which accepts an existing yara signature as an argument. The original functions, find_binary and find_text, built the yara for you. There was no option to search for a yara rule that you had already built outside of this script. This also allows your yara rule to be more advanced.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment