Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python2
- #-*- coding: utf-8 -*-
- # Andromeda botnet configuration extractor
- # Written by aaSSfxxx for phun and chocapicz :þ
- # Decompression algorithm taken from http://goo.gl/UTP6o and modified to
- # match Andromeda's aPLib variant.
- # And as usual, I'm using pefile to load the malware :D
- import pefile
- import sys
- import struct
- # aPLib variant used by Andromeda
- def int2lebin(value, size):
- """ouputs value in binary, as little-endian"""
- result = ""
- for i in xrange(size):
- result = result + chr((value >> (8 * i)) & 0xFF )
- return result
- def modifystring(s, sub, offset):
- """overwrites 'sub' at 'offset' of 's'"""
- return s[:offset] + sub + s[offset + len(sub):]
- def getbinlen(value):
- """return the bit length of an integer"""
- result = 0
- if value == 0:
- return 1
- while value != 0:
- value >>= 1
- result += 1
- return result
- class _bits_decompress():
- """bit machine for variable-sized auto-reloading tag decompression"""
- def __init__(self, data, tagsize):
- self.__curbit = 0
- self.__offset = 0
- self.__tag = None
- self.__tagsize = tagsize
- self.__in = data
- self.out = ""
- def getoffset(self):
- """return the current byte offset"""
- return self.__offset
- def read_bit(self):
- """read next bit from the stream, reloads the tag if necessary"""
- if self.__curbit != 0:
- self.__curbit -= 1
- else:
- self.__curbit = (self.__tagsize * 8) - 1
- self.__tag = ord(self.read_byte())
- for i in xrange(self.__tagsize - 1):
- self.__tag += ord(self.read_byte()) << (8 * (i + 1))
- bit = (self.__tag >> ((self.__tagsize * 8) - 1)) & 0x01
- self.__tag <<= 1
- return bit
- def is_end(self):
- return self.__offset == len(self.__in) and self.__curbit == 1
- def read_byte(self):
- """read next byte from the stream"""
- if type(self.__in) == str:
- result = self.__in[self.__offset]
- elif type(self.__in) == file:
- result = self.__in.read(1)
- self.__offset += 1
- return result
- def read_fixednumber(self, nbbit, init=0):
- """reads a fixed bit-length number"""
- result = init
- for i in xrange(nbbit):
- result = (result << 1) + self.read_bit()
- return result
- def read_variablenumber(self):
- """return a variable bit-length number x, x >= 2
- reads a bit until the next bit in the pair is not set"""
- result = 1
- result = (result << 1) + self.read_bit()
- while self.read_bit():
- result = (result << 1) + self.read_bit()
- return result
- def read_setbits(self, max_, set_=1):
- """read bits as long as their set or a maximum is reached"""
- result = 0
- while result < max_ and self.read_bit() == set_:
- result += 1
- return result
- def back_copy(self, offset, length=1):
- for i in xrange(length):
- self.out += self.out[-offset]
- return
- def read_literal(self, value=None):
- if value is None:
- self.out += self.read_byte()
- else:
- self.out += value
- return False
- def lengthdelta(offset):
- if offset < 0x80 or 0x7D00 <= offset:
- return 2
- elif 0x500 <= offset:
- return 1
- return 0
- class decompress(_bits_decompress):
- def __init__(self, data):
- _bits_decompress.__init__(self, data, tagsize=1)
- self.__pair = True
- self.__lastoffset = 0
- self.__functions = [
- self.__literal,
- self.__block,
- self.__shortblock,
- self.__singlebyte]
- return
- def __literal(self):
- self.read_literal()
- self.__pair = True
- return False
- def __block(self):
- b = self.read_variablenumber()
- if b == 2 and self.__pair :
- offset = self.__lastoffset
- length = self.read_variablenumber()
- else:
- # Andromeda' aPLib variant doesn't care about self.__pair to
- # decrement high so we don't need to check parity.
- high = b - 3
- offset = (high << 8) + ord(self.read_byte())
- length = self.read_variablenumber()
- length += lengthdelta(offset)
- self.__lastoffset = offset
- self.back_copy(offset, length)
- self.__pair = False
- return False
- def __shortblock(self):
- b = ord(self.read_byte())
- if b <= 1:
- return True
- length = 2 + (b & 0x01)
- offset = b >> 1
- self.back_copy(offset, length)
- self.__lastoffset = offset
- self.__pair = False
- return False
- def __singlebyte(self):
- offset = self.read_fixednumber(4)
- if offset:
- self.back_copy(offset)
- else:
- self.read_literal('\x00')
- self.__pair = True
- return False
- def do(self):
- """returns decompressed buffer and consumed bytes counter"""
- self.read_literal()
- while True:
- if self.__functions[self.read_setbits(3)]():
- break
- return self.out
- # End of aPLib variant
- # RC4 cryptoshit routines
- def initialize(strkey):
- """Produce a 256-entry list based on `key` (a sequence of numbers)
- as the first step in RC4.
- Note: indices in key greater than 255 will be ignored.
- """
- key = [ord(i) for i in strkey]
- k = range(256)
- j = 0
- for i in range(256):
- j = (j + k[i] + key[i % len(key)]) % 256
- k[i], k[j] = k[j], k[i]
- return k
- def gen_random_bytes(k):
- """Yield a pseudo-random stream of bytes based on 256-byte array `k`."""
- i = 0
- j = 0
- while True:
- i = (i + 1) % 256
- j = (j + k[i]) % 256
- k[i], k[j] = k[j], k[i]
- yield k[(k[i] + k[j]) % 256]
- def run_rc4(k, text):
- cipher_chars = []
- random_byte_gen = gen_random_bytes(k)
- for char in text:
- byte = ord(char)
- cipher_byte = byte ^ random_byte_gen.next()
- cipher_chars.append(chr(cipher_byte))
- return ''.join(cipher_chars)
- # End of RC4 cryptoshit routines
- #Finds a str by its null byte
- def find_str(s): return s[:s.find('\x00')]
- # Let's run baby !
- if len(sys.argv) != 2:
- print "Usage: " + sys.argv[0] + " <andromeda_bot.exe>"
- sys.exit(0)
- #Load the PE and grabs the offset of the payload
- f = pefile.PE(sys.argv[1])
- mem = f.get_memory_mapped_image()
- payload_offset = struct.unpack("L", mem[0x13C5:0x13C9])[0] - 0x400000
- # Grabs the size of data
- size = struct.unpack("L", mem[payload_offset+4:payload_offset+8])[0]
- rc4_key = mem[payload_offset+0x1c:payload_offset+0x3c]
- cyphered = mem[payload_offset+0x3c:payload_offset+0x3c+size]
- #Do decypher
- k = initialize(rc4_key)
- decyphered = run_rc4(k, cyphered)
- offset = 0x474 # String table is stored at this address.
- try:
- payload = decompress(decyphered).do()
- except Exception:
- print "Unable do decompress image ! Maybe it's not an andromeda bot..."
- mem = struct.unpack("L",payload[offset:offset+4])[0]
- while mem != 0:
- offset += 4
- mem -= 0x01001000
- print find_str(payload[mem:mem+500])
- mem = struct.unpack("L",payload[offset:offset+4])[0]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement