cover image for post 'The DGA of Ranbyus'

The DGA of Ranbyus

Table of Contents

The DGA in this blog post has been implemented by the DGArchive project.


For more information about the malware in this blog post see the Malpedia entry on Ranbyus.

Ranbyus is a trojan that steals banking information — among other personal data. End of April 2015 I first noticed samples of Ranbyus that use a Domain Generation Algorithm (DGA) to generate the domains for its command and control (C2) traffic: 	

In this post I show how the DGA works by reversing the following sample:

PE32 executable (GUI) Intel 80386, for MS Windows

I focused my efforts exclusively on the domain generation part of Ranbyus. Refer to the blog posts of Aleksandr Matrosov here and here for an in-depth analysis of Ranbyus.


This section shows the algorithm behind the domains of Ranbyus and its seeding and parametrization.

Callback Loop

The next image represents the part of the Ranbyus that tries to find a valid C2 target. It consists of an outer loop (month_loop) and an inner loop. The register edi holds the index of the outer loop. It runs from 0 down to -30. The number of iterations for the inner loop is specified by a parameter of the DGA (set to 40 in all analysed samples):

Callback Loop

The first act of the outer loop is to get the current time:

Getting the Current Date

Ranbyus then subtracts days from the current date according to the index of the outer loop:

Subtract Days

The resulting date will be used to seed the DGA with a granularity of one day. In the first iteration, the DGA uses the current date. In the next iteration — when the index is -1 — yesterday’s date is used. This continues up to 30 days in the past if need be. So even though the DGA generates a fresh set of domains every day, it also checks the domains of past days. This gives the DGA the benefit of fast changing domains in case domains get blocked or sinkholed, while at the same time enabling older domains to be used for up to one month if they still work.

The inner loop generates the domains for the day with the_dga and makes the callback. In case of failure, Ranbyus sleeps for wait_time milliseconds (500 in my samples) and retries up to nr_of_domains (40) different domains.

DGA Parameters and Seed

Apart from the current date, the DGA is seeded with a hardcoded magic number:

Hardcoded Seed

The number of domains per day is hardcoded to 40:

Hardcoded Number of Domains

The wait time after a failed callback attempt is set to 500 ms:

Hardcoded Sleep Time

Ranbyus also uses a hard-coded list of top level domains:

Hardcoded Top Level Domains

The top level domains are: .in, .me, .cc, .su, .tw, .net, .com, .pw, and .org. The last domain .org is never used due to a bug of the DGA. The top level domains are tested one after another (except the last one), starting at a day-dependent offset:

Starting Index

The error of subtracting 1 from the modulus is repeated also when picking the letters of the second level domain.


This is the disassembly of the DGA routine:


The subroutine generates domains in two independent parts:

  1. the top level domain is picked from the hardcoded list shown above
  2. the second level domain is generated.

The following disassembly shows how the top level domain is picked:

Top Level Domain

Starting at the day dependent offset determined earlier, the algorithm picks the domains in a circular fashion, omitting the last domain because the DGA wrongly subtracts one from the modulus.

[".in", ".me", ".cc", ".su", ".tw", ".net", ".com", ".pw", ".org"][offset % (9-1)]

The disassembly for the second level domain looks as follows. It generates 14 different letters based on the DGA’s seed, and the value of day, month and year. Note that these names are misleading: although theses values are initialized with the current or past dates, the values are modified by each call to the routine.

Second Level Domain

This pseudo-code summarizes the algorithm:

FOR i = 0 TO 13
    day = (day >> 15) ^ 16 * (day & 0x1FFF ^ 4 * (seed ^ day))
    year = ((year & 0xFFFFFFF0) << 17) ^ ((year ^ (7 * year)) >> 11)
    month = 14 * (month & 0xFFFFFFFE) ^ ((month ^ (4 * month)) >> 8)
    seed = (seed >> 6) ^ ((day + 8 * seed) << 8) & 0x3FFFF00
    int x = ((day ^ month ^ year) % 25) + 'a' 
    domain[i] = x;

The malware authors repeated their modulus error: like for the tld, the modulus needed to be increased by one. As it stands, ‘z’ is no reachable. Ranbyus shares this bug with the DGAs of Ramnig and Necurs.

Seed the end of this blog post for a C-implementation of the DGA.

Observed Seeds

The following tables lists some of the samples from that are Ranbyus with the described DGA. All samples use the same parametrization, only the seed varies.



DGA Characteristics

The characteristics of Ranbyus’ DGA are:

seedmagic number and current date
granularity1 day, with a 31 day sliding window
domains per seed and day40
domains per sliding window1240
wait time between domains500 ms
top level, .me, .cc, .su, .tw, .net, .com, .pw
second level characterslower case letters except ‘z’
second level domain length14 letters

Decompiled Code

The following C code generates the domains for a given day and seed. In order to generate all domains that the malware can generate for any given seed and date, one would also need to run the code for all dates going 30 days in the past.

Edit 23.5.2015: The following code had contained a bug that led to a wrong sequence of top level domains, thanks to Anthony Kasza for sharing that with me.

#include <stdio.h>
#include <stdlib.h>

char* dga(unsigned int day, unsigned int month, unsigned int year, 
        unsigned int seed, unsigned int nr) 
    char *tlds[] = {"in", "me", "cc", "su", "tw", "net", "com", "pw", "org"};
    char domain[15];
    int d;
    int tld_index = day;
    for(d = 0; d < nr; d++)
        unsigned int i;
        for(i = 0; i < 14; i++)
            day = (day >> 15) ^ 16 * (day & 0x1FFF ^ 4 * (seed ^ day));
            year = ((year & 0xFFFFFFF0) << 17) ^ ((year ^ (7 * year)) >> 11);
            month = 14 * (month & 0xFFFFFFFE) ^ ((month ^ (4 * month)) >> 8);
            seed = (seed >> 6) ^ ((day + 8 * seed) << 8) & 0x3FFFF00;
            int x = ((day ^ month ^ year) % 25) + 97; 
            domain[i] = x;
        printf("%s.%s\n", domain, tlds[tld_index++ % 8]);

main (int argc, char *argv[])
    if(argc != 5) {
        printf("Usage: dga <day> <month> <year> <seed>\n");
        printf("Example: dga 14 5 2015 b6354bc3\n"); 

    dga(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), 
            strtoul(argv[4], NULL, 16), 40);

Archived Comments

Note: I removed the Disqus integration in an effort to cut down on bloat. The following comments were retrieved with the export functionality of Disqus. If you have comments, please reach out to me by Twitter or email.

Sandor Nemes Aug 26, 2015 07:42:01 UTC

In the C code the strtoul function should be used instead as strtol will limit the seed to 0x7fffffff.

Johannes Bader Aug 26, 2015 09:36:08 UTC

Thanks, fixed it.