Created
November 20, 2017 15:52
-
-
Save alexander-hanel/4afc54d54c4689fd6050a3e292b57abe to your computer and use it in GitHub Desktop.
IDAPython string decrytor for variants of Nuclear Bot
This file contains hidden or 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
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