Created
April 8, 2017 02:16
-
-
Save herrcore/caa41b90ffcf1374b6b6b0c659be6607 to your computer and use it in GitHub Desktop.
Ramnit DGA generator for MD5: abd2b832007338d6d6550339eec09fb0 - seed 0x36F066D
This file contains 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
#!/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