Skip to content

Instantly share code, notes, and snippets.

@alexander-hanel
Created November 20, 2017 15:52
Show Gist options
  • Save alexander-hanel/4afc54d54c4689fd6050a3e292b57abe to your computer and use it in GitHub Desktop.
Save alexander-hanel/4afc54d54c4689fd6050a3e292b57abe to your computer and use it in GitHub Desktop.
IDAPython string decrytor for variants of Nuclear Bot
import idautils
from cStringIO import StringIO
from collections import Counter
from itertools import cycle
from itertools import product
MAX_INSTR = 8
"""
Example
.text:004020AA push 2 ; size of encoded data
.text:004020AC pop esi ; esi = size
.text:004020AD push esi ; size is pushed to the stack as an argument
.text:004020AE push offset unk_4067C4 ; xor key
.text:004020B3 push offset unk_4067C8 ; encode data
.text:004020B8 mov dword_4081A0, eax ; save off previous decoded data
.text:004020BD call DECODER ; call decoder
.text:004020C2 push 0Eh ; size
.text:004020C4 push offset aMhotcxkqkdpej7 ; "mhOTCXKqkdPEj7" ; key
.text:004020C9 push offset unk_4067DC ; encoded data
.text:004020CE mov dword_4081A4, eax ; save off encoded data from unk_4067C8
.text:004020D3 call DECODER ; call decoded data
.text:004020D8 add esp, 48h ; reset stack
.text:004020DB mov dword_4081A8, eax ; save off decoded data unk_4067DC
.text:004020E0 push 11h ; size
.text:004020E2 push offset aEwhvwgz1e3mqfp ; "EWHVWgz1E3MqFP7p5" ; key
.text:004020E7 push offset unk_406800 ; encoded data
.text:004020EC call DECODER ; call decoder
.text:004020F1 push 7 ; size
.text:004020F3 push offset aUw2atv1 ; "UW2ATV1" ; xor key
.text:004020F8 push offset a6?g?3u ; "6?G/?3U" ; encoded data
.text:004020FD mov dword_4081AC, eax ; save off previous decoded data
"""
def xor_mb(message, key):
"""multi-byte XOR args str1, str2 """
return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))
def is_offset_data(addr):
"""hack because IdaPython says its an integer"""
for x in idautils.DataRefsTo(addr):
return True
return False
def trace_reg(addr):
"""trace back to find populating of register"""
cur_addr = addr
reg = idc.GetOpnd(cur_addr,0)
while(True):
cur_addr = idc.PrevHead(cur_addr)
# break infinite loop
if cur_addr == BADADDR:
return False, None
if idc.GetMnem(cur_addr) == "pop" and idc.GetOpnd(cur_addr,0) == reg:
push_addr = cur_addr
while(True):
push_addr = idc.PrevHead(push_addr)
# break infinite loop
if push_addr == BADADDR:
return False, None
if idc.GetMnem(push_addr) == "push":
if idc.GetOpType(push_addr, 0) == o_imm:
return True, idc.GetOperandValue(push_addr, 0)
else:
return False, None
def get_encoded_data(addr):
"""retrive size, offset_data, key from offset"""
size = None
data_offset = None
key = None
bool_reg = False
cur_addr = addr
for c in range(0, MAX_INSTR):
# ignore mov and add instruct
cur_addr = idc.PrevHead(cur_addr)
cur_mnem = idc.GetMnem(cur_addr)
if "mov" in cur_mnem or "add" in cur_mnem or "pop" in cur_mnem:
continue
if cur_mnem == "push":
op_type = idc.GetOpType(cur_addr,0)
bool_data = is_offset_data(idc.GetOperandValue(cur_addr,0))
# get data offset
if bool_data and data_offset is None:
data_offset = idc.GetOperandValue(cur_addr,0)
continue
# get key string
if key is None and data_offset is not None and bool_data:
key = idc.GetString(idc.GetOperandValue(cur_addr,0))
# check if the size is stored in a register
if op_type == o_reg:
# backtrace and the popped register value
status, size = trace_reg(cur_addr)
# fail to find size via register
if status == False:
return None, None, None
# size is present as argument
elif op_type == o_imm and bool_data == False:
size = idc.GetOperandValue(cur_addr, 0)
if size:
encoded_data = ""
for byte in GetManyBytes(data_offset, size):
encoded_data += byte
return size, encoded_data, key
return None, None, None
def add_comment(addr, comment):
cur_addr = addr
for c in range(0, MAX_INSTR):
cur_addr = idc.NextHead(cur_addr)
cur_mnem = idc.GetMnem(cur_addr)
if "mov" in cur_mnem:
if idc.GetOpType(cur_addr, 0) == o_mem and "eax" in idc.GetOpnd(cur_addr, 1):
comment_addr = idc.GetOperandValue(cur_addr, 0)
idc.MakeRptCmt(comment_addr, comment)
func_addr = idc.LocByName("DECODER") # 004049DF
for addr in idautils.CodeRefsTo(func_addr, 0):
size, offset_data, key, = get_encoded_data(addr)
if size:
decoded = xor_mb(offset_data, key)
add_comment(addr, decoded)
else:
print hex(addr)[:-1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment