Skip to content

Instantly share code, notes, and snippets.

@herrcore
Created April 8, 2017 02:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save herrcore/caa41b90ffcf1374b6b6b0c659be6607 to your computer and use it in GitHub Desktop.
Save herrcore/caa41b90ffcf1374b6b6b0c659be6607 to your computer and use it in GitHub Desktop.
Ramnit DGA generator for MD5: abd2b832007338d6d6550339eec09fb0 - seed 0x36F066D
#!/usr/bin/env python
##################################################################
#
# Ref sample:
# MD5: abd2b832007338d6d6550339eec09fb0 (AegisI5.exe)
# \_ MD5: cf5de95d94bb349f1f21bb5713a05d25 (fA1L0mX.exe)
# \_ MD5: 17cb0563f7c4621bc98abd06965bdfa9 (svchost.exe injected DLL)
#
# DGA generator for Ramnit Trojan
#
# Another @herrcore production
#
# NOTE: This script was developed for a tutorial video on DGAs
# All credit goes to Johannes Bader @viql for reverse
# engineering the DGA first and publishing this excellent
# analysis: https://johannesbader.ch/2014/12/the-dga-of-ramnit/
# Not much has changed since 2014 : )
#
##################################################################
import argparse
def rng(seed, mod):
# In the video I forgot to add the & 0xFFFFFFFF, this just "casts" the result
# back to a 32bit DWORD
new_seed = (16807 * (seed % 0x1F31D) - 2836 * (seed / 0x1F31D)) & 0xFFFFFFFF
rnd_num = new_seed % mod
return rnd_num, new_seed
def gen_domain(start_seed):
domain = ''
# get domain length
rnd_num, new_seed = rng(start_seed, 12)
domain_length = rnd_num + 8
# we need to save this intial iteration
# see details below
new_start_seed = new_seed
# build the domain string
for i in range(0,domain_length):
rnd_num, new_seed = rng(new_seed, 25)
domain += chr(rnd_num + 0x61)
domain += ".com"
# In the tutorial video I forgot to mention that the "maybe_rng_dga" function will only produce one
# domain and since we want to produce all the domains we need to save the returned iteration seed from
# that function to use to generate the next domain.
#
# I mention this briefly in the video but the iteration seed for the domain is multiplied by the initial
# start_seed and is returned as the next seed for the next dga domain. There is one extra trick in that after
# the multiplication EDX is added to EAX which is then returned as the next seed.
#
# Multiplication in assembly places the result in EDX:EAX so this is essentially a multiplication so the
# following algorithm is the equivalent of:
# EAX * EBX = EDX:EAX
# return EAX + EDX
edx_eax = start_seed * new_start_seed
edx = edx_eax & 0xffffffff00000000
eax = edx_eax & 0xffffffff
edx = edx >> 32
eax = (eax + edx) & 0xffffffff
return domain, eax
def main():
parser = argparse.ArgumentParser(description="Print the first 32 domains for Ramnit DGA!")
parser.add_argument('--seed',dest="seed",type=int,default=0x36F066D,help="Specify a new seed value (as decimal integer not hex), default is 0x36F066D.")
parser.add_argument('--domain_count',dest="domain_count",type=int,default=None,help="Specify the number of domains to generate, default is 32.")
args = parser.parse_args()
seed = args.seed
if args.domain_count != None:
domain_count = args.domain_count
else:
domain_count = 32
for i in range(0, domain_count):
domain, seed = gen_domain(seed)
print domain
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment