Let us skip the long introduction on BlackEnergy threat and go straight to studying the malware component called “ololo.exe” also known to the public as KillDisk. KillDisk is a module of BlackEnergy framework aimed at data destruction and creating havoc / distraction during the APT operations.
The main tools used in our analysis today are Process Monitor that is a part of SysInternals utilities by Mark Russinovich and IDA Pro Disassembler. All manipulations will be performed in virtual environment based on Windows XP operating system. We start with making a quick initial setup of test VM, power on the machine and create a snapshot called “Before infection”. Let us begin!
In order to keep track of all events related to our subject of study we launch Process Monitor and make sure it stays visible:
And that it tracks the needed process:
Next, we load the virus into IDA Pro Disassembler and see the following picture.
This is the WinMain function, i.e. the main function so we begin its analysis and see that the first (and only) thing it does will be procedure call named
So let us head straight to that procedure. Here we can find procedures responsible for extraction of file extensions into memory and the first procedure right after them that calls an interesting thingy called
Let us take a closer look at its contents:
Its insides contain one interesting system call
CreateFile and couple procedures
sub_40C400. So it is about time to take a snapshot of our virtual lab and continue our journey!
Let us examine function call CreateFile, set the BreakPoint on it and execute our study sample:
A little hint regarding this function:
CreateFile function creates or opens a file or I/O device. The most commonly used I/O devices are as follows: file, file stream, directory, physical disk, volume, console buffer (CONIN$ or CONOUT$), tape drive, communications resource, mailslot, and named pipe. The function returns a handle that can be used to access the file or device for various types of I/O depending on the file or device and the flags and attributes specified.
As we know most of compilers pass argumetns to a function through a stack and since we are dealing with a sample initially written in C, and according to C directives arguments are pushed onto the stack from right to left in a way that first function argument is pushed last onto the stack and ends up being the first element on it’s top. .
There is a special register for working with the stack – ESP (Extended Stack Pointer). The ESP, by definition, always points to the top of the stack:
A look into the stack:
The last argument that got pushed into the stack (one on its top) is the first argument accepted by the function (according to syntax of
CreateFile function) and it contains file name in our case
\\.\PHYSICALDRIVE0, thus the target is first physical drive.
Now we analyze other arguments sent to the function by taking their values from the stack:
other processes can open file for read/write;
NULLvalue is used;
open file if it exists, and return an error if it does not exist;
ЕАХAnd if function failed a
NULLis written into
Alright so after calling
CreateFile we get a non-zero value for EAX which means that it contains the handler of requested file e.g.
We got our handler so let us proceed to the next call,
Same as before we look inside the procedure and see another interesting call:
DeviceIoControl function sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
A look into stack right before calling this function:
IOCTL_DISK_GET_DRIVE_GEOMETRYor retrieval of the information regarding disks geometry (amount of cylinders, type of media, tracks on cylinder, sectors per track, bytes per sector);
0in our case;
Let’s interpret the answer:
0x0011FCA4hwe have a return of the amount of bytes written into output buffer as intended by the function (marked with green) we got as many as we asked for 24 symbols.
0x0011FCA8hwe’ve got information on first physical disk geometry (
0x0Ch(12) meaning fixed hard disk.
Now we refer to procedure three,
Inside the procedure two calls immediately attracted our attention, specifically
A hint about the function:
As per established tradition we look into the function call stack:
0x0011FCA8h. In a case when parameter is EMPTY (NULL) a new pointer is not returned in the file;
0x200h(512) – bytes in sector;
Now our “beast” uses the
WriteFile function and to fill up file with zeros to which it already established direct and reliable access.
A function hint:
A WriteFile function Writes data to the specified file or input/output (I/O) device starting with the position specified in the file. This function is designed for both synchronous and asynchronous operation.
Function arguments list:
Alright, to make sure we do not miss anything interesting we take a look at the dashboard and among all things we pay attention to the stack:
All set for action:
512) of zero’s.
We execute the command by pressing F8 key and observe the change:
As we expected a keen eye of Process Monitor is spot on and fixes the writing of 512 bytes beginning from position 0.
Then procedure exits and is called again but now with different values:
Now we take the next 512 bytes which is particularly visible at 0x200 (unlike the previous arguments stack that we marked by a blue frame)
And they get the same fate as previous ones:
This merry-go-round goes on until the check of
0x40C865h address returns the value of
EBX register equal to
255 times ):
To make sure this happened we put a stop point on the instruction that follows our vicious cycle of “destruction”:
As a total we receive a
256 of re-write operations:
That’s basically it, our “beast” has done its dirty work and closes the file handler by calling the
Our traditional view of the stack preparation:
And what do we have in ESI register?
As expected our familiar descriptor
0x44h is passed to stack as function argument.
We see values returned by the function in the EAX register:
If function ends with success the returned value
If function exists with an error the returned value is null.
This action did not miss the watchful eye of our Process Monitor:
Everything worked out as normal (if you can call the destruction of the most important part of the system drive normal) ) and the file is closed.
Let us go on:
For a more comfortable perception I renamed function to
Eraser. After it successfully destroyed the data on
PhysicalDrive0, counter located in
ESI register is increased by
1, checked with value of
10) and launches re-write operation with a new value:
and so on until 10 is reached.
This way the beast keeps walking across all physical drives and erasing first
512 * 255 = 128kb (this depends on the amount of bytes in a sector that “beast” learned from knowing disk’s geometry).
This time however, result of the function
CreateFileW will be a following value:
Which means that in our «improvised lab» there is no second physical disk (or well not any kind of disk with sequence number greater than 0) exists and the cycle has nothing to re-write.
Now that we are armed with a freshly gained knowledge let us try to disable the destructive function we just learned by modifying the code in a way that our modification can allow to bypass the re-writing of the data:
Let us remember procedure
sub_40E080 that contains function call
CreateFileW. Since we know that value of
0xFFFFFFFFh would appear after completion of function
CreateFileW only in the case when physical drive is not present and in such scenario the “data destruction” function performs an exit, let us change a conditional branch instruction located at the
0x0040C7E4h address to an unconditional one:
IDA Pro is a cool instrument so we just have to write a command.
The only thing we need to consider is the command size before and after so let us check out how many bytes are used by conditional sequence:
As we can see a conditional branch instruction equals to
6 bytes while the unconditional sequence will only take
5 and thus we shall add another command – NOP, in order to keep things equal:
We NOP next command regardless of what it contained before (even though we see a 0 there):
We do a double-check that command addresses are now equalized based on address of the following command:
and press cancel. Done.
After these manipulations the procedure will look simple: enter and exit, making an empty loop (since we do not know what other parts of the program may hook into it we just leave it be in such state).
The final proof spotted by Process Monitor indicates that no physical drives were harmed in result of the dissected KillDisk’s execution:
Malware has read the information about the disk, studied it and continued on with its business that we will analyze thoroughly in our next articles.
 – Hacker Disassembling Uncovered. Kris Kaspersky, 2009.
Translated from original by Andrii Bezverkhyi | CEO SOC Prime