{
	"id": "ccd3b5d9-76b8-47a1-8b62-713905950274",
	"created_at": "2026-04-06T00:11:48.825568Z",
	"updated_at": "2026-04-10T13:12:17.92765Z",
	"deleted_at": null,
	"sha1_hash": "dbf7d677d991956ddd6f550214cc0b087efb9c3b",
	"title": "The DGA of Ranbyus",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 79588,
	"plain_text": "The DGA of Ranbyus\r\nArchived: 2026-04-05 16:33:21 UTC\r\nRanbyus is a trojan that steals banking information — among other personal data. End of April 2015 I first noticed\r\nsamples of Ranbyus that use a Domain Generation Algorithm (DGA) to generate the domains for its command\r\nand control (C2) traffic:\r\nhcfoopojnuqxho.su\r\nundrdsbhivryqn.tw\r\ndkehliueofdued.net\r\nmpuakxjqpscfpj.com\r\neelolbwmfmtkae.pw\r\nnoppsmyiijqujh.in\r\njoxrsxwdybbgqb.me\r\n(...)\r\nIn this post I show how the DGA works by reversing the following sample:\r\nfilename\r\n_RANDOMNUM_6_11__vozvrat.exe\r\nfiletype\r\nPE32 executable (GUI) Intel 80386, for MS Windows\r\nmd5\r\nfa57f601402aab8168dea94c7c5f029f\r\nsha256\r\ndc4f3340ca8e623a5a77eb95411696fc25a7e6f5ef657ac9fd76eb4bc11c16b4\r\nmalwr\r\nlink\r\nI focused my efforts exclusively on the domain generation part of Ranbyus. Refer to the blog posts of Aleksandr\r\nMatrosov here and here for an in-depth analysis of Ranbyus.\r\nAlgorithm\r\nThis section shows the algorithm behind the domains of Ranbyus and its seeding and parametrization.\r\nCallback Loop\r\nThe next image represents the part of the Ranbyus that tries to find a valid C2 target. It consists of an outer loop\r\n( month_loop ) and an inner loop. The register edi holds the index of the outer loop. It runs from 0 down to -30.\r\nThe number of iterations for the inner loop is specified by a parameter of the DGA (set to 40 in all analysed\r\nsamples):\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 1 of 12\n\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 2 of 12\n\nThe first act of the outer loop is to get the current time:\r\nRanbyus then subtracts days from the current date according to the index of the outer loop:\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 3 of 12\n\nThe resulting date will be used to seed the DGA with a granularity of one day. In the first iteration, the DGA uses\r\nthe current date. In the next iteration — when the index is -1 — yesterday’s date is used. This continues up to 30\r\ndays in the past if need be. So even though the DGA generates a fresh set of domains every day, it also checks the\r\ndomains of past days. This gives the DGA the benefit of fast changing domains in case domains get blocked or\r\nsinkholed, while at the same time enabling older domains to be used for up to one month if they still work.\r\nThe inner loop generates the domains for the day with the_dga and makes the callback. In case of failure,\r\nRanbyus sleeps for wait_time milliseconds (500 in my samples) and retries up to nr_of_domains (40) different\r\ndomains.\r\nDGA Parameters and Seed\r\nApart from the current date, the DGA is seeded with a hardcoded magic number:\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 4 of 12\n\nThe number of domains per day is hardcoded to 40:\r\nThe wait time after a failed callback attempt is set to 500 ms:\r\nRanbyus also uses a hard-coded list of top level domains:\r\nThe top level domains are: .in, .me, .cc, .su, .tw, .net, .com, .pw, and .org. The last domain .org is never used due to\r\na bug of the DGA. The top level domains are tested one after another (except the last one), starting at a day-dependent offset:\r\nThe error of subtracting 1 from the modulus is repeated also when picking the letters of the second level domain.\r\nThe DGA\r\nThis is the disassembly of the DGA routine:\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 5 of 12\n\nThe subroutine generates domains in two independent parts:\r\n1. the top level domain is picked from the hardcoded list shown above\r\n2. the second level domain is generated.\r\nThe following disassembly shows how the top level domain is picked:\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 6 of 12\n\nStarting at the day dependent offset determined earlier, the algorithm picks the domains in a circular fashion,\r\nomitting the last domain because the DGA wrongly subtracts one from the modulus.\r\n[\".in\", \".me\", \".cc\", \".su\", \".tw\", \".net\", \".com\", \".pw\", \".org\"][offset % (9-1)]\r\noffset++\r\nThe disassembly for the second level domain looks as follows. It generates 14 different letters based on the DGA’s\r\nseed, and the value of day , month and year . Note that these names are misleading: although theses values are\r\ninitialized with the current or past dates, the values are modified by each call to the routine.\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 7 of 12\n\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 8 of 12\n\nThis pseudo-code summarizes the algorithm:\r\nFOR i = 0 TO 13\r\n day = (day \u003e\u003e 15) ^ 16 * (day \u0026 0x1FFF ^ 4 * (seed ^ day))\r\n year = ((year \u0026 0xFFFFFFF0) \u003c\u003c 17) ^ ((year ^ (7 * year)) \u003e\u003e 11)\r\n month = 14 * (month \u0026 0xFFFFFFFE) ^ ((month ^ (4 * month)) \u003e\u003e 8)\r\n seed = (seed \u003e\u003e 6) ^ ((day + 8 * seed) \u003c\u003c 8) \u0026 0x3FFFF00\r\n int x = ((day ^ month ^ year) % 25) + 'a'\r\n domain[i] = x;\r\nThe malware authors repeated their modulus error: like for the tld, the modulus needed to be increased by one. As\r\nit stands, ‘z’ is no reachable. Ranbyus shares this bug with the DGAs of Ramnig and Necurs.\r\nSeed the end of this blog post for a C-implementation of the DGA.\r\nObserved Seeds\r\nThe following tables lists some of the samples from malwr.com that are Ranbyus with the described DGA. All\r\nsamples use the same parametrization, only the seed varies.\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 9 of 12\n\nmd5 seed\r\n4b04f6baaf967e9c534e962c98496497 65BA0743\r\n087b19ce441295207052a610d1435b03 65BA0743\r\n28474761f28538a05453375635a53982 65BA0743\r\nb309eab0277af32d7a344b8a8b91bd73 C5F128F3\r\n4c7057ce783b2e4fb5d1662a5cb1312a C5F128F3\r\nb309eab0277af32d7a344b8a8b91bd73 C5F128F3\r\n7cbc671bcb97122e0ec5b448f0251dc0 C5F128F3\r\n437028f94ceea4cab0d302d9ac6973eb C5F128F3\r\nb309eab0277af32d7a344b8a8b91bd73 C5F128F3\r\n6378b7af643e87c063f69ddfb498d852 B6354BC3\r\nfa57f601402aab8168dea94c7c5f029f B6354BC3\r\n9f2c89ad17e9b6cf386028a8c9189264 0478620C\r\nSummary\r\nDGA Characteristics\r\nThe characteristics of Ranbyus’ DGA are:\r\nproperty value\r\nseed magic number and current date\r\ngranularity 1 day, with a 31 day sliding window\r\ndomains per seed and day 40\r\ndomains per sliding window 1240\r\nsequence sequential\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 10 of 12\n\nproperty value\r\nwait time between domains 500 ms\r\ntop level domains .in, .me, .cc, .su, .tw, .net, .com, .pw\r\nsecond level characters lower case letters except ‘z’\r\nsecond level domain length 14 letters\r\nDecompiled Code\r\nThe following C code generates the domains for a given day and seed. In order to generate all domains that the\r\nmalware can generate for any given seed and date, one would also need to run the code for all dates going 30 days\r\nin the past.\r\nEdit 23.5.2015: The following code had contained a bug that led to a wrong sequence of top level domains,\r\nthanks to Anthony Kasza for sharing that with me.\r\n#include \u003cstdio.h\u003e\r\n#include \u003cstdlib.h\u003e\r\nchar* dga(unsigned int day, unsigned int month, unsigned int year,\r\n unsigned int seed, unsigned int nr)\r\n{\r\n char *tlds[] = {\"in\", \"me\", \"cc\", \"su\", \"tw\", \"net\", \"com\", \"pw\", \"org\"};\r\n char domain[15];\r\n int d;\r\n int tld_index = day;\r\n for(d = 0; d \u003c nr; d++)\r\n {\r\n unsigned int i;\r\n for(i = 0; i \u003c 14; i++)\r\n {\r\n day = (day \u003e\u003e 15) ^ 16 * (day \u0026 0x1FFF ^ 4 * (seed ^ day));\r\n year = ((year \u0026 0xFFFFFFF0) \u003c\u003c 17) ^ ((year ^ (7 * year)) \u003e\u003e 11);\r\n month = 14 * (month \u0026 0xFFFFFFFE) ^ ((month ^ (4 * month)) \u003e\u003e 8);\r\n seed = (seed \u003e\u003e 6) ^ ((day + 8 * seed) \u003c\u003c 8) \u0026 0x3FFFF00;\r\n int x = ((day ^ month ^ year) % 25) + 97;\r\n domain[i] = x;\r\n }\r\n printf(\"%s.%s\\n\", domain, tlds[tld_index++ % 8]);\r\n }\r\n}\r\nmain (int argc, char *argv[])\r\n{\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 11 of 12\n\nif(argc != 5) {\r\n printf(\"Usage: dga \u003cday\u003e \u003cmonth\u003e \u003cyear\u003e \u003cseed\u003e\\n\");\r\n printf(\"Example: dga 14 5 2015 b6354bc3\\n\");\r\n exit(0);\r\n }\r\n dga(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]),\r\n strtoul(argv[4], NULL, 16), 40);\r\n}\r\nNote: I removed the Disqus integration in an effort to cut down on bloat. The following comments were retrieved\r\nwith the export functionality of Disqus. If you have comments, please reach out to me by Twitter or email.\r\nSource: https://bin.re/blog/the-dga-of-ranbyus/\r\nhttps://bin.re/blog/the-dga-of-ranbyus/\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://bin.re/blog/the-dga-of-ranbyus/"
	],
	"report_names": [
		"the-dga-of-ranbyus"
	],
	"threat_actors": [],
	"ts_created_at": 1775434308,
	"ts_updated_at": 1775826737,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/dbf7d677d991956ddd6f550214cc0b087efb9c3b.pdf",
		"text": "https://archive.orkl.eu/dbf7d677d991956ddd6f550214cc0b087efb9c3b.txt",
		"img": "https://archive.orkl.eu/dbf7d677d991956ddd6f550214cc0b087efb9c3b.jpg"
	}
}