{
	"id": "6c513f66-f977-4a2a-91c4-9d2532a3fef9",
	"created_at": "2026-04-06T00:08:08.03811Z",
	"updated_at": "2026-04-10T13:11:49.445452Z",
	"deleted_at": null,
	"sha1_hash": "4e94e808f64c42140343734e83bdb59446753bff",
	"title": "The DGA of Symmi",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 97223,
	"plain_text": "The DGA of Symmi\r\nArchived: 2026-04-05 21:13:40 UTC\r\nLooking through the most recent reports on malwr.com, a sample sparked my interest because it suits my current\r\ninterest in domain generation algorithms (DGA). Virus scanners label the sample as Symmi, other names for the\r\nsame or similar malware family are MewsSpy and Graftor. The sample is very noisy. It tries to resolve many\r\ndomains in a short period of time — only limited by the response time of the DNS server:\r\nThere are hardly any samples online that match the above DGA pattern; I only found the following four:\r\ndate sha-256\r\n2014-12-12 55f6945302a5baa49f32ef25425b793c\r\n2014-12-27 e0166446a676adb9e3160c9c06e56401\r\n2014-12-29 1ca728b9d0c64b1edfc47aeeebb899b4\r\n2015-01-19 b75f00d7ae2857a3e1cc8f5eb4dc11b9\r\nThe earliest sample is from December 12th, 2014, which could indicate that the DGA — or at least the used\r\nconfiguration — is fairly new.\r\nConfiguration\r\nThe malware uses a large structure for its configuration settings, including most of the parameters that control the\r\nDGA:\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 1 of 9\n\nconfig_data_struct struc\r\n...\r\n+7A8 seed_constant ; char*\r\n+7AC days_period ; char*\r\n+7B0 nr_of_domains ; char*\r\n+7B4 third_lvl_min_len ; char*\r\n+7B8 third_lvl_max_len ; char*\r\n...\r\nAll parameters are stored as XOR encrypted strings. The following table lists the purpose of the parameters along\r\nwith the value from the examined sample. The full meaning of the parameters will become clear when discussing\r\nthe individual parts of the DGA:\r\nvariable value purpose\r\nseed_constant 42\r\nA constant added to the seed. This allows for different sets of generated\r\ndomains at any given point in time.\r\ndays_period 16\r\nFor how many days the DGA will produce the same domains. For example, the\r\nvalue 16 means the DGA produces one set of domains for the first 16 days of\r\nthe month, and a new set for the second half.\r\nnr_of_domains 64\r\nHow many different domains the DGA generates before restarting with the\r\nfirst domain.\r\nthird_lvl_min_len 8 How long the third level domain is at the least.\r\nthird_lvl_max_len 15 How long the third level domain is at most.\r\nThe DGA\r\nRandom Number Generator\r\nThe malware uses msvcrt’s implementation of rand — a linear congruential pseudo-random number generator\r\nwith multiplier 214013 and increment 2531011. The code is inlined — meaning each call to rand amounts about\r\nsix lines of assembly:\r\n100066CC mov ecx, [esi]\r\n100066CE imul ecx, 343FDh\r\n100066D4 add ecx, 269EC3h\r\n100066DA mov [esi], ecx\r\n...\r\n100066E1 shr ecx, 10h\r\n100066E4 and ecx, 7FFFh\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 2 of 9\n\nIn many cases the removal of the low order bytes and clipping to RAND_INT is optimized away by the compiler.\r\nFor example:\r\n100066CC mov ecx, [esi]\r\n100066CE imul ecx, 343FDh\r\n100066D4 add ecx, 269EC3h\r\n100066DA mov [esi], ecx\r\n...\r\n100066E1 test ecx, 1000000h\r\nIn contrast to the famous Conficker.B, the rand function is used correctly and all random aspects of the DGA are\r\ntruly pseudo-random.\r\nSeed\r\nThe random number generator is seeded with the current date and a hardcoded constant:\r\n10008625 call ds:GetLocalTime ; kernel32.GetLocalTime\r\n1000862B movzx eax, [esp+60h+day]\r\n10008630 xor edx, edx\r\n10008632 div [esp+60h+days_period] ; is 0x10\r\n10008636 mov [esp+60h+domain_counter], 0\r\n1000863E mov edx, eax\r\n10008640 movzx eax, [esp+60h+month]\r\n10008645 imul edx, 100\r\n10008648 add edx, eax\r\n1000864A movzx eax, [esp+60h+year]\r\n1000864F imul edx, 10000\r\n10008655 add edx, [esp+60h+seed_constant] ; is 0x2A\r\n10008659 add eax, edx\r\nwhich calculates:\r\nseed = (⌊\r\n𝑑𝑎𝑦\r\ndays_period⌋ ⋅ 100 + month)⋅ 10000 + year + seed_constant\r\nThe configurable seed_constant (42 for all four samples listed in the introduction) allows for different sets of\r\ndomains at the same point in time. The parameter days_period controls how many days the DGA will produces\r\nthe same domains. For the examined sample I got days_period = 16 , meaning the domains will change twice a\r\nmonth.\r\nThird Level Domain Length\r\nRight after the DGA is seeded, the length of the third level domain is determined:\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 3 of 9\n\n10008666 mov ebx, [esp+60h+third_lvl_max_len]\r\n1000866A sub ebx, [esp+60h+third_lvl_min_len]\r\n1000866E inc ebx\r\n1000866F jmp short loc_10008675\r\n10008671 ; ---------------------------------------------------------------------------\r\n10008671\r\n10008671 loc_10008671: ; CODE XREF: calls_create_next_url+B00j\r\n10008671 mov eax, [esp+60h+rand]\r\n10008675\r\n10008675 loc_10008675: ; CODE XREF: calls_create_next_url+9FFj\r\n10008675 imul eax, 343FDh\r\n1000867B add eax, 269EC3h\r\n10008680 mov [esp+60h+rand], eax\r\n10008684 shr eax, 10h\r\n10008687 and eax, 7FFFh\r\n1000868C xor edx, edx\r\n1000868E div ebx\r\n...\r\n1000869B add edx, [esp+68h+third_lvl_min_len]\r\n1000869F push edx ; third_lvl_len\r\nThe above code sets the domain length to a value picked uniformly at random between third_lvl_min_len and\r\nthird_lvl_max_len (including the boundaries):\r\nspan = third_lvl_max_len - third_lvl_min_len + 1\r\nthird_lvl_len = third_lvl_min_len + r.rand() % span\r\nGenerating the Domain\r\nNext follows the core of the DGA algorith: generating the actual domain inside create_domain . Apart from the\r\ndomain length, the subroutine also takes a type and the third and top level string as parameters.\r\n10008690 mov ecx, [esp+60h+second_and_top_lvl]\r\n10008694 push 1 ; type\r\n10008696 push ecx ; second_and_top_lvl\r\n10008697 lea eax, [esp+68h+rand]\r\n1000869B add edx, [esp+68h+third_lvl_min_len]\r\n1000869F push edx ; third_lvl_len\r\n100086A0 call create_domain\r\nThe create_domain routine includes two similar types of DGA, selected by the type parameter passed to the\r\nroutine. The examined sample only used type 1 to generate the domains, so I didn’t investigate the other type\r\noption. The routine is rather long (over 300 lines of disassembly), which is mostly attributed to the fact that the\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 4 of 9\n\ncompiler inlined rand and strcat . I therefore only show the Python version of the DGA here, you can find the\r\nunderlying disassembly on Github Gist:\r\ndef next_domain(r, second_and_top_lvl, third_lvl_domain_len):\r\n letters = [\"aeiouy\", \"bcdfghklmnpqrstvwxz\"]\r\n domain = \"\"\r\n for i in range(third_lvl_domain_len):\r\n if not i % 2:\r\n offset_1 = 0 if r.rand() \u0026 0x100 == 0 else 1\r\n s = r.rand()\r\n offset = (offset_1 + i) % 2\r\n symbols = letters[offset]\r\n domain += symbols[s % (len(symbols) - 1)]\r\n return domain + second_and_top_lvl\r\nThe DGA picks random letters from either “aeiouy” (vowels) or “bcdfghklmnpqrstvwxz” (consonants). The letter\r\n“j” is missing. Also, because of a bug in the DGA, the last characters of either character class, i.e., “y” or “z”\r\ncan’t be picked. The DGA randomly choses either the vowel or consonant class for letters with even index (0, 2, 4,\r\n…); the subsequent letter is then always from the other character class. For example:\r\np \u003c- consonant\r\ni \u003c- vowel\r\nv \u003c- consonant\r\nu \u003c- vowel\r\no \u003c- vowel\r\ng \u003c- consonant\r\nu \u003c- vowel\r\ns \u003c- consonant\r\no \u003c- vowel\r\nd \u003c- consonant\r\nt \u003c- consonant\r\no \u003c- vowel\r\nk \u003c- consonant\r\nu \u003c- vowel\r\nThis will lead to domain names that are somewhat pronounceable. After the third level domain is generated, the\r\nDGA appends the configured second and top level string, for this sample “.ddns.net”\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 5 of 9\n\nNumber of Domains\r\nThe malware is very aggressive in that it doesn’t wait when it tries to resolve a NXDOMAIN, but instead\r\nimmediately moves on to the next domain — first determining the length, then building the url with\r\ncreate_domain . The DGA only waits when it can either resolve a domain or if nr_of_domains (in my sample\r\n64) domains are consumed:\r\n10008B38 mov ecx, [esp+60h+nr_of_domains]\r\n10008B3C mov cl, [eax+ecx]\r\n10008B3F mov [edx+ebx], cl\r\n10008B42 inc eax\r\n10008B43 inc edx\r\n10008B44 cmp eax, esi\r\n10008B46 jnb short loc_10008B5E\r\nThe DGA will finally sleep after all nr_of_domains domains are tested without success. When all domains were\r\nNXDOMAINS, the sleep time is a mere thirty seconds; If the C2 sites could not be reached for another reason\r\n(flag dword_100393D8 set), the DGA sleeps for five minutes:\r\nseg129:10008D74 cmp ds:dword_100393D8, 0\r\nseg129:10008D7B jz short loc_10008D8F\r\nseg129:10008D7D push 300000 ; unsigned __int32\r\nseg129:10008D82 call __sleep_1\r\nseg129:10008D87 add esp, 4\r\nseg129:10008D8A jmp loc_10007C87\r\nseg129:10008D8F ; ---------------------------------------------------------------------------\r\nseg129:10008D8F\r\nseg129:10008D8F loc_10008D8F:\r\nseg129:10008D8F push 30000 ; unsigned __int32\r\nseg129:10008D94 call __sleep_1\r\nseg129:10008D99 add esp, 4\r\nseg129:10008D9C jmp loc_10007C87\r\nseg129:10008D9C calls_create_next_url endp\r\nPython Code of DGA\r\nThe following Python code generates 64 domains for a given date (or today, if no date is provided):\r\nimport argparse\r\nfrom datetime import datetime\r\nseed_const = 42\r\ndays_period = 16\r\nnr_of_domains = 64\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 6 of 9\n\nthird_lvl_min_len = 8\r\nthird_lvl_max_len = 15\r\nclass Rand:\r\n def __init__(self, seed):\r\n self.seed = seed\r\n def rand(self):\r\n self.seed = (self.seed*214013 + 2531011) \u0026 0xFFFFFFFF\r\n return (self.seed \u003e\u003e 16) \u0026 0x7FFF\r\ndef next_domain(r, second_and_top_lvl, third_lvl_domain_len):\r\n letters = [\"aeiouy\", \"bcdfghklmnpqrstvwxz\"]\r\n domain = \"\"\r\n for i in range(third_lvl_domain_len):\r\n if not i % 2:\r\n offset_1 = 0 if r.rand() \u0026 0x100 == 0 else 1\r\n s = r.rand()\r\n offset = (offset_1 + i) % 2\r\n symbols = letters[offset]\r\n domain += symbols[s % (len(symbols) - 1)]\r\n return domain + second_and_top_lvl\r\ndef dga(seed, second_and_top_lvl, nr):\r\n r = Rand(seed)\r\n for i in range(nr):\r\n span = third_lvl_max_len - third_lvl_min_len + 1\r\n third_lvl_len = third_lvl_min_len + r.rand() % span\r\n print(next_domain(r, second_and_top_lvl, third_lvl_len))\r\ndef create_seed(date):\r\n return 10000*(date.day//days_period*100 + date.month) + date.year + seed_const\r\nif __name__==\"__main__\":\r\n parser = argparse.ArgumentParser()\r\n parser.add_argument(\"-d\", \"--date\", help=\"as YYYY-mm-dd\")\r\n args = parser.parse_args()\r\n date_str = args.date\r\n if date_str:\r\n date = datetime.strptime(date_str, \"%Y-%m-%d\")\r\n else:\r\n date = datetime.now()\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 7 of 9\n\nseed = create_seed(date)\r\n dga(seed, \".ddns.net\", nr_of_domains)\r\nYou also find the script on GitHub Gist.\r\nDGA Characteristics\r\nThe following table summarizes the properties of the DGA\r\nproperty value\r\nseed\r\nbased on date and configurable constant, set to change twice a month in analysed\r\nsample\r\ndomains per seed unlimited\r\ntested domains configurable, set to 64 in analysed sample\r\nsequence one after another, restarting with first domain when no success\r\nwait time between\r\ndomains\r\nnone, before restarting with first domain 30 second or 5 minute wait time\r\ntop and second level\r\ndomain\r\n.ddns.net\r\nsecond level characters all letters except “z”, “y” and “j”\r\nsecond level domain\r\nlength\r\nuniformly distributed between configurable bounds, for the analysed sample 8 to\r\n15 characters\r\nPast and Upcoming Domains\r\nFor the seed constant “42” and the day period “16”, these are the first three domains generated between November\r\nof last year and the upcoming 2015.\r\nstart date end date first three domains\r\n2014-11-01 2014-11-15 vitevecaasbaim.ddns.net, buxotopelah.ddns.net, doefruevtan.ddns.net\r\n2014-11-16 2014-11-30 tevalurii.ddns.net, ufrasequcoequdi.ddns.net, qularivafou.ddns.net\r\n2014-12-01 2014-12-15 urasahrenaheen.ddns.net, xoegfeima.ddns.net, niubsacaosuce.ddns.net\r\n2014-12-16 2014-12-31 leuvuftet.ddns.net, obneifqumea.ddns.net, bodihemouxk.ddns.net\r\n2015-01-01 2015-01-15 uwuhuhawidb.ddns.net, exdihasuhes.ddns.net, axtoomov.ddns.net\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 8 of 9\n\nstart date end date first three domains\r\n2015-01-16 2015-01-31 pivuogusodtoku.ddns.net, onogibuluremg.ddns.net, geevheuqsemaif.ddns.net\r\n2015-02-01 2015-02-15 obwihecidik.ddns.net, umashadilauru.ddns.net, irmatexoitn.ddns.net\r\n2015-02-16 2015-02-28 opacutebmadufo.ddns.net, ercehuhowi.ddns.net, uwsiokxua.ddns.net\r\n2015-03-01 2015-03-15 eqosenrealq.ddns.net, duwugunuwaqauk.ddns.net, loopowakm.ddns.net\r\n2015-03-16 2015-03-31 efrubatoiketxa.ddns.net, loliqooq.ddns.net, tixufaheurvo.ddns.net\r\n2015-04-01 2015-04-15 haaxicuconx.ddns.net, xaguuswibuoqope.ddns.net, ukwoubapgi.ddns.net\r\n2015-04-16 2015-04-30 aginemkiroacus.ddns.net, haerbugoviosmu.ddns.net, vireacvio.ddns.net\r\n2015-05-01 2015-05-15 wewateikho.ddns.net, ovicceosweub.ddns.net, siriomilfomu.ddns.net\r\n2015-05-16 2015-05-31 vabuibofqouxog.ddns.net, tubiebikceli.ddns.net, olkaerxedus.ddns.net\r\n2015-06-01 2015-06-15 muavosecit.ddns.net, uxefilka.ddns.net, deivekmiuwoxe.ddns.net\r\n2015-06-16 2015-06-30 looxnaaluhotw.ddns.net, feohpoaqpeheuw.ddns.net, gabouhlat.ddns.net\r\n2015-07-01 2015-07-15 ucsauhdune.ddns.net, deohupivoco.ddns.net, haufidasu.ddns.net\r\n2015-07-16 2015-07-31 bovuugodvuecf.ddns.net, cuopxeudu.ddns.net, muarocavhaqe.ddns.net\r\n2015-08-01 2015-08-15 edehgogoep.ddns.net, uciluswaaqnieb.ddns.net, ugxeicbeveudusu.ddns.net\r\n2015-08-16 2015-08-31 ogovugtuipawi.ddns.net, afowkaupbabe.ddns.net, ipkureleakm.ddns.net\r\n2015-09-01 2015-09-15 rocaexesti.ddns.net, veonwient.ddns.net, axgoxevikupoxa.ddns.net\r\n2015-09-16 2015-09-30 uhrixaloduuse.ddns.net, gecoohocalifluw.ddns.net, ecunxoorokonw.ddns.net\r\n2015-10-01 2015-10-15 huosinamu.ddns.net, udebliena.ddns.net, imewgiopaqexacb.ddns.net\r\n2015-10-16 2015-10-31 gouhumuvelcua.ddns.net, decouqunic.ddns.net, eretodweqee.ddns.net\r\n2015-11-01 2015-11-15 hiwosoofa.ddns.net, oxacuhuvanoxxo.ddns.net, tahimoteev.ddns.net\r\n2015-11-16 2015-11-30 vebiabipkilo.ddns.net, hunoikuxibi.ddns.net, imugoqsoakiqahi.ddns.net\r\n2015-12-01 2015-12-15 ociqusdal.ddns.net, xiupfisuaw.ddns.net, mivibicoruq.ddns.net\r\n2015-12-16 2015-12-31 veeswaehsisa.ddns.net, uhbacoinm.ddns.net, baugkoosdui.ddns.net\r\nSource: https://bin.re/blog/the-dga-of-symmi/\r\nhttps://bin.re/blog/the-dga-of-symmi/\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://bin.re/blog/the-dga-of-symmi/"
	],
	"report_names": [
		"the-dga-of-symmi"
	],
	"threat_actors": [],
	"ts_created_at": 1775434088,
	"ts_updated_at": 1775826709,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/4e94e808f64c42140343734e83bdb59446753bff.pdf",
		"text": "https://archive.orkl.eu/4e94e808f64c42140343734e83bdb59446753bff.txt",
		"img": "https://archive.orkl.eu/4e94e808f64c42140343734e83bdb59446753bff.jpg"
	}
}