The pseudo-random domain generators are not new – these were previously used by Sober, Kraken, or Conficker worms. The important thing about reproducing a particular domain generator is an ability to predict what domains the worm will query in the future. Once known, these domain registrations can potentially be blocked, "sinkholed" or at least monitored.
Now, domain generator reproduction is a tricky task. It can basically be done in 2 ways.
First, the original algorithm can be studied in its disassembled form, and then its logic reproduced in a higher programming language. The second method assumes that the original algorithm studying can take longer than expected, so it offers a shortcut solution – a "hack" – to take the original code "as is" and then either replicate it in a standalone tool written in Assembly from scratch (e.g. by using MASM) or use it in inline Assembler of a higher level language such as C++ or Delphi.
Another approach is to patch the malicious binary in order to force it looping the way you need and then hook and log some particular APIs it is calling (such as UrlDownloadToFile()) in order to obtain the output.
We’ll take the route of ripping the original code apart. This is a no brainer exercise – it shouldn’t take long time as we don’t have to understand how exactly the domain generator works – we only need to understand where the code is located, what it does functionally, and most importantly, how to interface it properly with our higher level code. That is, we need to "glue" or attach it correctly to our code.
Murofet is a file infector. It appends 1,771 bytes to a host executable.
The APIs it calls are dynamically retrieved from shlwapi.dll, urlmon.dll, kernel32.dll, advapi32.dll DLLs by matching their ASCII name hashes – it is a very common technique.
The domain generator routine requires 4 parameters:
- a base address of the adavapi32.dll module – the domain generator needs it to dynamically retrieve the APIs CryptAcquireContectW(), CryptCreateHash(), CryptHashData(), CryptGetHashParam(), CryptDestroyHash(), CryptReleaseContext(). It calls these APIs during the domain name generation.
- a seed value – it starts from a fixed number 119 (current year mod 256, multiplied by 17, that is 7 * 17)
- current date (GMT)
- a pointer to a buffer that will store the result – the generated domain name
Once the domain is generated, the sample attempts to download an executable from that domain. Next, it increments the seed value (119 -> 120), and repeats the same loop – that is, generates a new domain name, then attempts to download from there. The loop repeats 800 times.
Thus, Murofet, generates 800 domains a day.
The domain generation routine is called by Murofet the following way:
As seen in the listing, the routine takes 4 parameters on stack – base address of adavapi32.dll, the seed value, a pointer to the SYSTEMTIME structure filled with the current time and data (Murofet calls GetSystemTime() for that), and a pointer to a buffer that will receive the result.
In order to reproduce this algorithm, this is what has can be done.
- Create a VC++ MFC project that generates a day, a month, and a year value, then creates a log file to dump there the generated domains.
- Create a stub in your executable that declares a buffer filled with 0x90 values. The stub can take a few Kb in size.
- Create an inline assembler code in your code that wraps the domain generation routine calling, that is, pushes 4 required parameters on stack – we’ll need this code to "glue in" the native Murofet domain name generator. The call to the routine itself should now consist of 5 NOPs – we’ll patch this call later as now we don’t know where in the code the routine will be located. This step is best outlined with the code below:
- Compile your executable in debug mode.
- Patch the section with the stub in it to make it executable.
- Open your compile in a HEX editor, find your stub, replace 1,771 bytes in it with the original Murofet code. This way, we are sort of "infecting" our executable with the Murofet, but we don’t give its code any control just yet.
- Open your executable in the disassembler, find your "glue" code created in step 3, and find the domain generation routine. Find the difference between their addresses, that is, subtract the address where your 5 NOPs are located (incremented by 5 as your call will take 5 bytes) from the address of the domain generation routine, let’s say the distance between these calls is 0x010203. Then, patch your 5 NOPs with a call to the generator by replacing them with E8 03 02 01 00.
- Run your executable.
// get the base address of advapi32.dll - it will be needed by Murofet to obtain Crypto-API hashes
HMODULE hAdvapi32;
hAdvapi32 = LoadLibrary("advapi32.dll");
// prepare a SYSTEMTIME structure
LPSYSTEMTIME lpst;
lpst = (LPSYSTEMTIME)malloc(sizeof(SYSTEMTIME));
// prepare you year, month, and day values: wYear, wMonth, wDay
// fill your SYSTEMTIME structure with these values
lpst->wYear = wYear;
lpst->wMonth = wMonth;
lpst->wDay = wDay;
// prepare a log file name
char szLogFile[MAX_PATH];
sprintf(szLogFile, "c:\\logs\\log_%d_%02d_%02d.txt", y, m, d);
// prepare a buffer that will hold your domain name
LPBYTE lpbyDomainName;
lpbyDomainName = (LPBYTE)malloc(1024);
// this is our "glue" - prepare the initial seed value of 0x77
_asm
{
push 77h
}
// start the loop - 800 domain will have to be generated for a given day (wYear, wMonth, wDay)
for (int i = 0; i < 800; i++)
{
memset(lpbyDomainName, 1024, 0);
_asm
{
pop eax
push eax
xor edx, edx
mov ecx, 3fch
div ecx ; divide the seed value by 1024 - the reminder is the same
NOP
;push 4 parameters on stack
push hAdvapi32 ; 1st - base address of the advapi32.dll (retrieved earlier)
push edx ; 2nd - a reminder of division of the seed value by 1020, which is the same as the seed value
push lpst ; 3rd - a pointer to our SYSTEMTIME structure, where we've put a specific year, month and day
push lpbyDomainName ; 4th - a pointer to a buffer that will receive the result
NOP ; these 5 NOPs will later by patched with a call to the routine
NOP ; 1st NOP will be replaced with E8 (call)
NOP ; other 4 NOPs with a distance between the following operand (see pop eax below)
NOP ; and the routine itself
NOP
pop eax ; increment the seed value that we store in eax
inc eax
push eax
}
// after the stub above is executed, we'll have the generated domain name in our lpbyDomainName buffer
// drop it into the log
DropLog(szLogFile, (LPCSTR)lpbyDomainName);
}
_asm
{
pop eax
}