Skip to content

Instantly share code, notes, and snippets.

@0xcpu
Last active June 12, 2020 06:03
Show Gist options
  • Save 0xcpu/705a7f50a81e1b7b1f9b0c599bfd92f8 to your computer and use it in GitHub Desktop.
Save 0xcpu/705a7f50a81e1b7b1f9b0c599bfd92f8 to your computer and use it in GitHub Desktop.
IDAPython 3 Snake ransomware string decryption
# IDAPython 3
import ida_funcs
import idautils
import ida_ua
import ida_xref
import ida_bytes
import heapq
from collections import defaultdict
from ctypes import c_ubyte
def snake_str_decrypt():
# find parent function by number of xrefs / out-degree count
xrefs_cnt = defaultdict(list)
func = ida_funcs.get_next_func(0)
while func:
# search near calls / p type references
xrefs = [xref for xref in idautils.XrefsTo(func.start_ea) if xref.type == ida_xref.fl_CN]
if len(xrefs) == 1:
src_node = ida_funcs.get_func(xrefs[0].frm)
if src_node:
xrefs_cnt[src_node.start_ea].append(xrefs[0].to)
func = ida_funcs.get_next_func(func.end_ea)
heap = []
for k, v in xrefs_cnt.items():
heapq.heappush(heap, (len(v), k))
# get first n functions having the highest out degree count
nfuncs = heapq.nlargest(10, heap)
# Iterate over every child node and pattern match
for ea in nfuncs:
for func_ea in xrefs_cnt[ea[1]]:
func = ida_funcs.get_func(func_ea)
if func is None:
print("Failed obtaining function")
continue
print("Function @ {:08x}".format(func_ea))
curr_ea = func.start_ea
# length of the blob to decrypt, it's offset and key offset
length, offsets = None, [None, None]
# True when decryption key blob was found
matched = False
while curr_ea < func.end_ea:
insn = idautils.DecodeInstruction(curr_ea)
if insn is None:
print("Failed decoding instruction at {:08x}".format(curr_ea))
break
"""
General pattern
.text:00567560 074 8D 05 E3 54 62 00 lea eax, byte_6254E3
.text:00567566 074 89 44 24 04 mov [esp+74h+var_70], eax
.text:0056756A 074 C7 44 24 08 14 00 00 00 mov [esp+74h+var_6C], 14h
.text:00567572 074 E8 19 34 ED FF call runtime_stringtoslicebyte
"""
if insn.get_canon_mnem() == "lea" and insn.Op1.type == ida_ua.o_reg:
insn2 = idautils.DecodeInstruction(insn.ea + insn.size)
insn3 = idautils.DecodeInstruction(insn2.ea + insn2.size)
insn4 = idautils.DecodeInstruction(insn3.ea + insn3.size)
insn5 = idautils.DecodeInstruction(insn4.ea + insn4.size)
if any(t is None for t in (insn2, insn3, insn4, insn5)):
print("Failed decoding following instructions")
break
if insn2.get_canon_mnem() == "mov" and insn2.Op2.type == ida_ua.o_reg:
if insn3.get_canon_mnem() == "mov" and insn3.Op2.type == ida_ua.o_imm \
and insn4.get_canon_mnem() == "call" and insn5.get_canon_mnem() != "ud2":
if matched:
if length == insn3.Op2.value:
offsets[1] = insn.Op2.addr
print("Blob1 @ {:08x}".format(offsets[0]))
print("Blob2 @ {:08x}".format(offsets[1]))
# decrypt
blob1 = ida_bytes.get_bytes(offsets[0], length)
blob2 = ida_bytes.get_bytes(offsets[1], length)
dec_blob = [c_ubyte(b1 + 2 * i).value ^ b2 for b1, b2, i in zip(blob1, blob2, range(len(blob1)))]
print("".join(map(chr, dec_blob)))
ida_bytes.set_cmt(offsets[0], "".join(map(chr, dec_blob)), False)
#ida_bytes.patch_bytes(offsets[0], bytes(dec_blob))
matched = False
else:
matched = True
length = insn3.Op2.value
offsets[0] = insn.Op2.addr
curr_ea += insn.size
if __name__ == "__main__":
snake_str_decrypt()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment