Helper Functions

def unhex(hex_string):
    import binascii
    if type(hex_string) == str:
        return binascii.unhexlify(hex_string.encode('utf-8'))
    else:
        return binascii.unhexlify(hex_string)

def tohex(data):
    import binascii
    if type(data) == str:
        return binascii.hexlify(data.encode('utf-8'))
    else:
        return binascii.hexlify(data)

Known Data (Config) From Joe Sandbox

{
  "Version": 22201,
  "C2 list": [
    "103.42.56.15:443",
    "169.255.57.61:8116",
    "128.199.192.135:6602"
  ],
  "RC4 keys": [
    "s6ptqqQ96C42ODZyFUv32gPAVtURlPXrif68ogKV96MSXkXTs",
    "rZP9KOr8K1zzIudGa98GqFxsSgNXTDprMuKqtLAWjx1u1prqdOQuZjmv5"
  ]
}

RC4 Decryption

def rc4crypt(data, key):
    #If the input is a string convert to byte arrays
    if type(data) == str:
        data = data.encode('utf-8')
    if type(key) == str:
        key = key.encode('utf-8')
    x = 0
    box = list(range(256))
    for i in range(256):
        x = (x + box[i] + key[i % len(key)]) % 256
        box[i], box[x] = box[x], box[i]
    x = 0
    y = 0
    out = []
    for c in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append(c ^ box[(box[x] + box[y]) % 256])
    return bytes(out)

APLib

Credit: Sandor Nemes (snemes)

import struct
from binascii import crc32
from io import BytesIO

__all__ = ['APLib', 'decompress']
__version__ = '0.6'
__author__ = 'Sandor Nemes'


class APLib(object):

    __slots__ = 'source', 'destination', 'tag', 'bitcount', 'strict'

    def __init__(self, source, strict=True):
        self.source = BytesIO(source)
        self.destination = bytearray()
        self.tag = 0
        self.bitcount = 0
        self.strict = bool(strict)

    def getbit(self):
        # check if tag is empty
        self.bitcount -= 1
        if self.bitcount < 0:
            # load next tag
            self.tag = ord(self.source.read(1))
            self.bitcount = 7

        # shift bit out of tag
        bit = self.tag >> 7 & 1
        self.tag <<= 1

        return bit

    def getgamma(self):
        result = 1

        # input gamma2-encoded bits
        while True:
            result = (result << 1) + self.getbit()
            if not self.getbit():
                break

        return result

    def depack(self):
        r0 = -1
        lwm = 0
        done = False

        try:

            # first byte verbatim
            self.destination += self.source.read(1)

            # main decompression loop
            while not done:
                if self.getbit():
                    if self.getbit():
                        if self.getbit():
                            offs = 0
                            for _ in range(4):
                                offs = (offs << 1) + self.getbit()

                            if offs:
                                self.destination.append(self.destination[-offs])
                            else:
                                self.destination.append(0)

                            lwm = 0
                        else:
                            offs = ord(self.source.read(1))
                            length = 2 + (offs & 1)
                            offs >>= 1

                            if offs:
                                for _ in range(length):
                                    self.destination.append(self.destination[-offs])
                            else:
                                done = True

                            r0 = offs
                            lwm = 1
                    else:
                        offs = self.getgamma()

                        if lwm == 0 and offs == 2:
                            offs = r0
                            length = self.getgamma()

                            for _ in range(length):
                                self.destination.append(self.destination[-offs])
                        else:
                            if lwm == 0:
                                offs -= 3
                            else:
                                offs -= 2

                            offs <<= 8
                            offs += ord(self.source.read(1))
                            length = self.getgamma()

                            if offs >= 32000:
                                length += 1
                            if offs >= 1280:
                                length += 1
                            if offs < 128:
                                length += 2

                            for _ in range(length):
                                self.destination.append(self.destination[-offs])

                            r0 = offs

                        lwm = 1
                else:
                    self.destination += self.source.read(1)
                    lwm = 0

        except (TypeError, IndexError):
            if self.strict:
                raise RuntimeError('aPLib decompression error')

        return bytes(self.destination)

    def pack(self):
        raise NotImplementedError


def aplib_decompress(data, strict=False):
    packed_size = None
    packed_crc = None
    orig_size = None
    orig_crc = None
    if data.startswith(b'AP32') and len(data) >= 24:
        # data has an aPLib header
        header_size, packed_size, packed_crc, orig_size, orig_crc = struct.unpack_from('=IIIII', data, 4)
        data = data[header_size : header_size + packed_size]
    if strict:
        if packed_size is not None and packed_size != len(data):
            raise RuntimeError('Packed data size is incorrect')
        if packed_crc is not None and packed_crc != crc32(data):
            raise RuntimeError('Packed data checksum is incorrect')
    result = APLib(data, strict=strict).depack()
    if strict:
        if orig_size is not None and orig_size != len(result):
            raise RuntimeError('Unpacked data size is incorrect')
        if orig_crc is not None and orig_crc != crc32(result):
            raise RuntimeError('Unpacked data checksum is incorrect')
    return result

Test Data Decrypt

data = unhex('991fd7128ac6c8165a9d2daf172ef27944fd14f1fcaed3ff7f8cc4e02e434d439ee2becd4f076605a7bb5e20a2f4a73fa6c59f94cd7729eaa202576c1d33f0fc725fcdccd2b1d4dfb60c11f8d68aa7cc7acd78f97a9f9d777508d3d53c9c5fe6c64bb834f97680548dc6f093328097283261926ff58e662476ec8689b0b136ce9d7a8aa27a884c559fd5a60358f54c150587b89b01d6a3abdb2ec419029df02ce69c9997821cb14c8b2a4bbd74b747921650b98c93b50db1e557aa258b31397fe4ae9fde0f042dc856a2ed2865ccc639820ae7e94042f5deaa9c2d41f6a3034a251d954642e76b48002a2479ad96dea13bb77e74b1c28fbf5c61da648def0b3e99eda2a396594e3f59b3bc5d22c39f7aacc792a4e3db0bacdc5623dc2a333b8de3f27f3075ad2fd2a118b903e1f1f5fed45bde3fc1df0047f05ab27f0617a690440bba697ced12a1003eba57e49b69d40b9e4cbaf640ea8f03a57d2dc3507377ac4732a2eac76a10fc562139b3cc3cf616641feaeae1d580a4092f9ba3dbb0296ac8c7669d5c3635e2b2de5e05c2b2090c470b842be348f8aa35cac0b5a216d089689d88cb4f84d4a399c31d6acdb1bc7f86094fade26c3070bda7a37ae3254f94170f6163e8bef596c9025975d67bdcd1bf35cfdbed3ce789785f6d3c71b0472514dd2672c496d0fff1f5609ad632120c243930e481732ec5791ef33d0e7de20f925dc21f7aeeaea6a08af88ba3ae57863209f07d860cc0a54910043566ad4bdb4287a0fccce20685ec03f350a2fd22d7aaffb17304b5b7466fe317f624d91f53bf8d4d96369c810c0beae24c9307d555963ef0b3fd90fa1e7c99475a4b2c19047b356ca0e1220be20ba501f6e9c7e93a7803066d570be2d9f246a07ac151ec78ed665ab3ca827e6d03fe941b1ccc6e0d1a146c03c5751fe38e12781d34c90bf7636f9e5e322af63ba2e4fe71f6a2dffc05db028d5e3ac27a9a823c3c4471063f41a12263dd6378d32b602b6900d2de732ff215390ac8392fc6309f521e6a35d419dd8eb6a3be617fb48fc1ccbe1d640736b8fec3065a66b119fb7bdbf192bd9efd25c806a5cbb47d2fbcf3f5d2bd6a30a36617cc19bea06730e85417d006a44d35072fdb24dcb358c94b898a2fa80ab797113b636af50350a20cfb54ce441b15d26e62745354c223d342654e534a929ff5140b8bcba469e24794d795065ba8734b84d2063ae762d112fe5415266bab8ae35d10d68df561631acca2e69e61704bc4ee498c1c276c56251d3c7e5314e043bc6281d644e5e38d9fad56cb3284f2d2819b42423bd914bb7e75e46f47fce8f6da23c451d244bb807a5ce4399158f036b9364f6a91022ccc34dea34a2c00a0003c3fb773061c060a426c1ecd5aadbcaaccbe0146057cde291bf5f2968a58bb8d2a3793aa0431b6a5b20357eef157c0ed2340925c6ec1eb083d1edb3fe4fe72c2b9660507dee987cd5b612c5bf1048149c609feb0b658766af44f6368c6fd8ab2be56c4b1b102006681ab2423d9b47b6cda3445f7209c3b61d20f1a5855296a0d8bdae82b44a16e202b266bbd244c7918298b5d27ae28c775ff8f9b03220a9ace2f4e10474ae6db402f4deca0c54553cfabc348f8a3c45cb960b1272fb9434d0168af93a8d0201671b224fdca05281873e7cb5b3bb96a52657ed4de823c65422b44e1302509a8267ca8dc868ae3014add43e25c862210bc54118ab89893aa53f8da4adc008081a5dd95b50db9322f93a90b3daa5d36ce09680e6aad0f2a1bfe79d0136bf99bdeb13572ea6ed03e85f5e124eae9aad92310129a195f1ee97045db33f0acae6f13e7d3c909b9746901116c91b6d594574ba33192d244b82bdd03dc9c8bd88b6515257a95f85314037a6dd12291b9982d05eb87c83defcc47344f0200c4d395ad6fd43dce7475f284bb82e71efbef5aa1d58ce5dad95c5d584997e24629a6879afe7d06b6191564c9ace2ad8db1f64d0a0a6d5df4e7d5bd2868ef48347662e3d8ab3e579962c2a161588fc8fdde43bdf32ff4132aceed418999a5db02d3a38bacbc18360a42bc4f5a0ee6f4ae6d21f824ec237f2e9ca10ada9d9966a6d87a07f9313f6701702f12dd5b8cd1cce1e7cdeeac49bdfa3be62f1ec03d41ab5b7c7914d7ad5723d0e7acd8d2cac1d6473114e6562d08b27b4575864d22ca749ff148dd3478952cb03b663655fcd40cc26c4b444bada92fbbc6bbae16fc56316252dca3bf230c6b24d693f6f285da')

key = data[:48][::-1]
data = data[48:]

b64_data = rc4crypt(data, key)

import base64
import struct
cmp_data = base64.b64decode(b64_data)
decompressed_data_len = struct.unpack('<I',cmp_data[:4])
print("decompressed data len: %d" % decompressed_data_len)
out = aplib_decompress(cmp_data[4:])

SHELLCODE_FILE = '/tmp/dridex_shell1.bin'
open(SHELLCODE_FILE, 'wb').write(out)
decompressed data len: 1549
1549

Shellcode Analysis

The loader checks to see if the process is 64 or 32 bit and then decryptes a shellcode blob and injects it along with a struct containing an IAT and other information.

32bit shellcode (malshare)

To be analyzed...

gap_size = 68
for i in range(0, gap_size, 4): 
    print(f"DWORD dword_{i:0x};")
DWORD dword_0;
DWORD dword_4;
DWORD dword_8;
DWORD dword_c;
DWORD dword_10;
DWORD dword_14;
DWORD dword_18;
DWORD dword_1c;
DWORD dword_20;
DWORD dword_24;
DWORD dword_28;
DWORD dword_2c;
DWORD dword_30;
DWORD dword_34;
DWORD dword_38;
DWORD dword_3c;
DWORD dword_40;
shell_data_2 = unhex('4b93941a5f7671df49d159c96b178e1c96667e2ab79238f78d625bd468c1f2aee57a345f03721206fdee52034c3453b52c5d1fc76b2785b91e3442b9ea78da4e4f20eccb69cc1c0f2001fe7ccb6e9c5f8977f6fa89358a57ffd44f5c1e29213a3deaeb45160c655972cfcfc04a1260a89eff479e263dd500f6ec1ed713acb5bff9e2de331dc8dee30a67802dd8c66373af26c356d488013d52a220df8609d784ecad7ddeb888d436aa0c5451b69f2f03f51041538f3a2dcaf92db0968e27c81f9c34ee317777acaf53ecba288b5f0803cf29ba6170fe83f96d5c702a1c2aea4ebd21df44c62eb956db897631675f8e3f5f79e2b1498a47043b56d31d146c53fc53751433f89e8e2828c4bab1ddd49b13ec78fe91b890e617c907fde9653527b07d31801d42418e23fb9b2d715ed8b2a07495e102c97b36183e29e2baf47f')

key = shell_data_2[:48][::-1]
data = shell_data_2[48:]

b64_data = rc4crypt(data, key)

import base64
import struct
cmp_data = base64.b64decode(b64_data)
decompressed_data_len = struct.unpack('<I',cmp_data[:4])
print("decompressed data len: %d" % decompressed_data_len)
out = aplib_decompress(cmp_data[4:])
SHELLCODE_FILE_2 = '/tmp/dridex_shell2.bin'
open(SHELLCODE_FILE_2, 'wb').write(out)
decompressed data len: 245
245

Test String Decryption

data = unhex('63208a6488442f57a8414b4928cc1bb9660867a6c23f352adfa702ff121ceee14c69499849e264128bb21bda7fa2a8493244d4377003ec3caae05dc6a76ac7678acd82ca4b2ccceaf0602504576bc9c96223fda05aba4bbff6769f74bc7a3645839a41c4f610217d74d7d203cf38a89af0c05566f5e9301ea7d83f5102e8f324f90d3d38c0773e1a8db5dc6fb5808076269b686b40c6380c37de737d8838e530fac67a')
key = data[:48][::-1]
data = data[48:]

out = rc4crypt(data, key)
for s in out.split(b'\x00\x00'):
    print(s.replace(b'\x00',b'').decode('latin-1'))
ROOT\CIMV2
SELECT * FROM Win32_Fan
*.dll
*.exe
ntdll.dll


data = unhex('95c985f019ba926e4c776a4f39bb87c3af575e04263a3cfbf446fe76cd45826bdfc9c381775b910daa5af895189ac2037f94d7e0cbcb9f04a44b3dade44673637e63d0aeb931ee404dee9737afe9df5fefac345edde26397a125a483bbf9e4a80012c314d14eab1fe2a997e969b313ef290f7700bd7f3a3afb3f8a493f1449ff72a2e5163c632c7a79d450e1f8618cfaa15cba132da34e83c5dfe1aecf4b016b10e489041d096388fb2ada047bc1567732fe4b2c7d7b249be25e74ee1a6ee47abda1b8efd31253186df54c518aa596c5351fbe1cd148ef4bab2645')
key = data[:48][::-1]
data = data[48:]

out = rc4crypt(data, key)
for s in out.split(b'\x00\x00'):
    print(s.replace(b'\x00',b'').decode('latin-1'))
S:(ML;;NW;;;LW)D:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-1-0)
\Sessions\%d
\BaseNamedObjects\


data = unhex('763204228337e0fd87cd26f15d317e7753125863474a2a23cb087289562e629b87de7aebca904d887233a895007c1b7c8b01fc71443ef6ffb96a7a857d8b636bd5a0d3')
key = data[:48][::-1]
data = data[48:]

out = rc4crypt(data, key)
for s in out.split(b'\x00\x00'):
    print(s.replace(b'\x00',b'').decode('latin-1'))
GET
POST


data = unhex('9929d0667cd2bee6ce087b584cfa8d54a813828fe28e53422b76d901b32b276fd6bf3583d4c0078646c3f7e0d908c93f798c881c00a9f7b23498892e6dea7634eaa2b1c7e6c67b34f153c9de05b775602a824dc48d52a7175c86e0112db594a53b7fd4c79689ce1adbd8b8fada3a0c03b8b980fe72f8af41ed8ed3af9a544cdf15c7e3ed8e878f61eaa41b626b5d450e65fd78b8736da4aeb1504d65352e081c877863842ab6adae783a99d525cdeb0eb078f8ee748283ce9554b88e9053b63f4c79e4479f117a341f97d7de18f4276b73ec5db445db5865bb16c8f2d819103d436c1b208cf00ee656e799559082d0996e8d89a07202915cb38ea1f3ad813b08034385703110dba092baf240fb0b8d839a07befcb05830c4a8ee25206c9539c010732a75670c8cba4db4eb8996d7d6f3012971574c3da3fb4a0e5274256ccbe3ee349e1fe5ab7249c78505cee79aa943beca99bdfb52f9aaae4d29ca9b3f26db612a1d866a2162ab96a07ad4a69544b1573c2d6e2c7d0424c97aaa23fbff7b703d4ff88d24dda7')
key = data[:48][::-1]
data = data[48:]

out = rc4crypt(data, key)
for s in out.split(b'\x00\x00'):
    print(s.replace(b'\x00',b'').decode('latin-1'))
Program Manager
Progman
AdvApi32~PsApi~shlwapi~shell32~WinInet
/run /tn "%ws"
"%ws" /grant:r "%ws":F
\NTUSER.DAT
winsxs
x86_*
amd64_*
*.exe
\Sessions\%d\BaseNamedObjects\


data = unhex('0921161a62f46e068fcb7050fe9d5fb793d2666a91610f5c75813f2e6a7f6846478262fcb0bd4e0e5ecdb8763266b6dba380102939f258b4e4c6e3c31481976ae308b958793d404c3dfa35a79ac4551495846111e06c4f0e5c4f9dbcf4d26fd2c9d1130852a1bd812f97d6b9b92fb919866f0a1ab95167a0d111c8b214ed78f7a0aaa18dae2ee00fb34974eb3bbc3b778c3f88f4b29dc7d1efd1793d79fa4381e2d991e0115ffc398989f531053028cddcdec0151fa888d249030604224cae2dc1fc6fcb792c4a9943e12ed6c825a69342d275fe8f3652f8f3a79d355f461a67cd3b8b4700236b6d63683e638c5c83c80d0bac05eb7f40298f97e43f4f32af9d125643ebe245a5f65a181d70bcb147f63bcf24552e782565a7dd818a4439471ff4092097aeee12453c802332346c07e80a9b31d631428f299d11b6cdd962654eb918ef68f8e6b786f3c7fc4b473e74cc2721d4a292c1f8ab99e0b5968eb6d3452f1b9a1d12a6a2255281e44f56b7b0b87b30001b19958e6681d5272c560396580c87370eadf68ae1c1022fcb05c781bcf09635b2bd648aa6fa100320c79c6a8f86a7a2bc679dad4a31faa6358b061ca98259ad6497afb43a2a98a5d1a1061c8ada7c4710c32fb7742d68bfb6e0d52f3f56cc9519e75df72c377728c2bfc58aa75bad3b136c2ba610d23c02febad47f1394db071774fa9d058ebd512bc4a411466d2a6582a651a7443cc8a3ae106af4433099f5616d3414c09788359272ac4599c76b8686eeb26ad090de576a3345cf30ad95b8053233ecff47c4315e7d30cd41387e44b241d992625dfd4cf7b0f506797a89e85914d7593cc59e2e089fbbac5a56b96824af9773822e35b948616ff348d1545933b3eb8253c59d29ebfd4001d087a457ff4b49a6eb2b4a9a2d13b1dd3c4d29db90c3ddf4434fa81246f850b719cea0450c30438cf7ca272681a0a6a76ecb6fe7b460f6703f0e3ed0ffe9c43db993e3c07a97405ea42caba91c47070dd9080e32fcd678721a032956bb393c24714c88')
key = data[:48][::-1]
data = data[48:]

b64_data = rc4crypt(data, key)
print(b64_data)

out = base64.b64decode(b64_data)
print(out)

SHELLCODE_FILE = '/tmp/dridex_shell_2.bin'
open(SHELLCODE_FILE, 'wb').write(out)
b'o6ijqKOoo6ijqKOoo6ijqKqqqqqqqqqqrKqyqp+fn5+1o6Okqqq6qpaqqKrDw5aWt67WvaPFo6Ofw5yqqqyqvwN/EX8BfwE/AQGQgn2XWVlZWVl/WVlgfX9/WVlZWVlZWVlZWVlZmoh9WVBQUFBZWVlZYZRhnllZhVmSo2BgWVlZWVlZWVlZWVmfAQMBBAPVA8wBvAPwEBAQEFBQUFAUICAgIAEBAQHEAhAAAAAAAQHAwhARAgMRAwMEAAAUAAIAAMbIAgICAgAA/////wAAAP/KAQEBAAYABADAwgEBAwH//wEAA8TExgMBAQH/AwMDyEAACgAEAAAAAH8AMwEAAAAAAAD/v///AAAAAAcAAP8AAAAAAAAAAAAAAAAAAP//AAAAvwAAAAAAAAAAfwAA/0pKSkpLUkpKSkpPTEpKSkpKSkpKVUVASkpKRVlNRkpdSkpKSkpKSkpKSkpKSkphY2dOSkprbUpKRW1KSkRFSkoAAAACDQYGBgYOAAAAAAYGBgAGBgIGAAoKBwcGAgUFAgIAAAQEBAQAAAAOBQYGBgEGAAAIABAAGAAgACgAMACAAYIBhgD2z/4/qwCwALEAswC6+LsAwADBAMe/Yv8Ajf8AxP8Axf8A///rAf8OEggAEwkAFggAFwkAKwkArv8Hsv8AtP8Atf8AwwEAx/+/5wgA8AIA\x00\x00'
b'\xa3\xa8\xa3\xa8\xa3\xa8\xa3\xa8\xa3\xa8\xa3\xa8\xa3\xa8\xa3\xa8\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xac\xaa\xb2\xaa\x9f\x9f\x9f\x9f\xb5\xa3\xa3\xa4\xaa\xaa\xba\xaa\x96\xaa\xa8\xaa\xc3\xc3\x96\x96\xb7\xae\xd6\xbd\xa3\xc5\xa3\xa3\x9f\xc3\x9c\xaa\xaa\xac\xaa\xbf\x03\x7f\x11\x7f\x01\x7f\x01?\x01\x01\x90\x82}\x97YYYYY\x7fYY`}\x7f\x7fYYYYYYYYYYYY\x9a\x88}YPPPPYYYYa\x94a\x9eYY\x85Y\x92\xa3``YYYYYYYYYYY\x9f\x01\x03\x01\x04\x03\xd5\x03\xcc\x01\xbc\x03\xf0\x10\x10\x10\x10PPPP\x14    \x01\x01\x01\x01\xc4\x02\x10\x00\x00\x00\x00\x01\x01\xc0\xc2\x10\x11\x02\x03\x11\x03\x03\x04\x00\x00\x14\x00\x02\x00\x00\xc6\xc8\x02\x02\x02\x02\x00\x00\xff\xff\xff\xff\x00\x00\x00\xff\xca\x01\x01\x01\x00\x06\x00\x04\x00\xc0\xc2\x01\x01\x03\x01\xff\xff\x01\x00\x03\xc4\xc4\xc6\x03\x01\x01\x01\xff\x03\x03\x03\xc8@\x00\n\x00\x04\x00\x00\x00\x00\x7f\x003\x01\x00\x00\x00\x00\x00\x00\xff\xbf\xff\xff\x00\x00\x00\x00\x07\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\xffJJJJKRJJJJOLJJJJJJJJUE@JJJEYMFJ]JJJJJJJJJJJJJJacgNJJkmJJEmJJDEJJ\x00\x00\x00\x02\r\x06\x06\x06\x06\x0e\x00\x00\x00\x00\x06\x06\x06\x00\x06\x06\x02\x06\x00\n\n\x07\x07\x06\x02\x05\x05\x02\x02\x00\x00\x04\x04\x04\x04\x00\x00\x00\x0e\x05\x06\x06\x06\x01\x06\x00\x00\x08\x00\x10\x00\x18\x00 \x00(\x000\x00\x80\x01\x82\x01\x86\x00\xf6\xcf\xfe?\xab\x00\xb0\x00\xb1\x00\xb3\x00\xba\xf8\xbb\x00\xc0\x00\xc1\x00\xc7\xbfb\xff\x00\x8d\xff\x00\xc4\xff\x00\xc5\xff\x00\xff\xff\xeb\x01\xff\x0e\x12\x08\x00\x13\t\x00\x16\x08\x00\x17\t\x00+\t\x00\xae\xff\x07\xb2\xff\x00\xb4\xff\x00\xb5\xff\x00\xc3\x01\x00\xc7\xff\xbf\xe7\x08\x00\xf0\x02\x00'
516

C2 Networking

print("%d.%d.%d.%d" % (0x67, 0x2a, 0x38, 0x0f))
103.42.56.15
import re
import pefile
import struct 

# Change to be your own local file path
SAMPLE_FILE_PATH = '/tmp/dridex.bin'

file_data = open(SAMPLE_FILE_PATH, 'rb').read()
ip_parse_egg = rb'\xbb(....)\x89\x45\x00\x0f\xb7\x53\x04\x89\x10\x0f\xb6\x4b\x0b\x83\xf9\x0a'

pe = pefile.PE(data=file_data)
data_section = None
for s in pe.sections:
    if s.Name == b'.data\x00\x00\x00':
        data_section = s.get_data()

match = re.search(ip_parse_egg, file_data, re.DOTALL|re.MULTILINE)
ip_table_addr = None
if match:
    table_addr = struct.unpack('<I',match.group(1))[0]
    print("Found data table at: %s\n" % hex(table_addr))
        
# Turn address into rva/offset and calculate table offsets
table_offset = pe.get_offset_from_rva(table_addr - pe.OPTIONAL_HEADER.ImageBase)
ip_table_offset = table_offset + 0xb
version_table_offset = table_offset + 4

# Get bot version
bot_version = struct.unpack('<H', file_data[version_table_offset:version_table_offset+2])[0]
print("Bot Version: %d\n" % bot_version)


ip_table_len = ord(file_data[ip_table_offset:ip_table_offset+1])
print("IP table length: %d" % ip_table_len)
# Move to actual IP table start
ip_table_offset += 1
# Extract the c2 ips
c2_ips = []
for i in range(ip_table_len):
    ip_string = "%d.%d.%d.%d" % (ord(file_data[ip_table_offset:ip_table_offset+1]),
                                 ord(file_data[ip_table_offset+1:ip_table_offset+2]),
                                 ord(file_data[ip_table_offset+2:ip_table_offset+3]),
                                 ord(file_data[ip_table_offset+3:ip_table_offset+4]))
    port_string = struct.unpack('<H', file_data[ip_table_offset+4:ip_table_offset+6])[0]
    print("%s:%s" % (ip_string,port_string))
    c2_ips.append("%s:%s" % (ip_string,port_string))
    ip_table_offset += 6
Found data table at: 0x55d020

Bot Version: 22201

IP table length: 3
103.42.56.15:443
169.255.57.61:8116
128.199.192.135:6602