Events depicted in this article happened some time ago, but I’ve never had enough time and determination to actually write them down and publish them. Quite recently some of my friends who had heard this story convinced me to do so. Here it is.
It was a hot summer evening back in August 2014. I was working on my master’s thesis project. The project was about building software controlling a group of mobile robots and at that time I was running some simulations. The main part was running on a Linux machine, but the simulator had to be run on Windows, so I decided to put it in VMware virtual machine. Looking back, I must admit it was very overcomplicated, but I was still a student and I had a lot of free time.
Everything worked fine up until my control software lost connection to the simulator. Turning it off and on again didn’t help, so I looked at the WMware’s window and found out that the virtual machine had restarted and showed the following message:
Ransom message
The message was in Polish, my native language. It said:
Your computer has been locked and your disk has been encrypted. Please send a text message “WP A4792” to number 7928 to get the unlocking code. Enter the code in the box below.
The price for this message was 9 PLN, which is around $3.
Thoughts started to run through my head. What the hell? A Polish CryptoLocker? All my data lost? But how? And why is it that cheap to recover?
Then I nervously tried to recall what data I had on this virtual machine. Much to my relief, I realised that it was just the system and couple of programs. Every important file was backed up somewhere else, so in fact, the whole situation seemed like it wasn’t much of a problem. So why not take a break from the university stuff and spend some time on analysing it? Maybe I’ll be able to repair the machine without actually paying the ransom.
My first idea was to find out whether the disk was really encrypted. The original CryptoLocker encrypts all user files, but displays the ransom message on a running system instead of replacing the bootloader. But who knows, maybe this one is more radical.
To figure this out I had to take a look at the Master Boot Record. MBR is a small part at the beginning of a hard disk that contains two things: a partition table - information about how the disk is organised logically - and a boot loader - a piece of code that is used to start the operating system. It looked like the malware had changed the bootloader, so the system didn’t start, but what about the partition table?
I used Windows installation image to boot the virtual machine. Unfortunately, it did not recognize any system on the disk, so it seemed that the partition table was corrupted as well, or maybe the entire disk was encrypted.
Recovery console. No systems found
So I used TestDisk - a partition recovery tool that scans the actual data space and tries to recognise existing partitions. If only the MBR was corrupted, TestDisk would easily be able to repair it. After just a second, it announced a complete success and restored the partition table:
TestDisk restored the partition
Good, it seems that the files are fine. Now I can get back to the Windows recovery console and restore the bootloader.
Recovery console found the system
Much to my surprise, the system was then able to boot and operate normally. Moreover, I didn’t notice any missing or inaccessible files. So this malware had only replaced the MBR, but it hadn’t encrypted any data. What’s the deal with an “unlocking code” then? How does it check if the code is correct and restore the system to an operational state? I decided to solve this mystery by digging into its machine code. At this point I kinda started to enjoy myself.
Because the malware attacked a system on a virtual machine, I was able to copy its state before playing with the MBR recovery tools. Now I could use this snapshot as a playground. I booted it up again with some basic Linux distribution and dumped the first 10 sectors of the disk into a file:
dd if=/dev/sda of=mbr bs=512 count=10
I got back to my host Linux machine and started analysing the dump. First of all it would be nice to get a human readable assembler instructions out of this binary machine code:
objdump -D -b binary -mi386 -Maddr16,data16 mbr
The whole thing looked like a good puzzle, so I printed the code listing on paper and started going through it instruction by instruction. And here is what I found.
In the middle of the analysis
Let’s start with the first chunk, located at the very beginning of the MBR. This is the place where the execution starts. The part on the left contains a hexadecimal representation of the raw bytes from the file. On the right side, you can see the assembler mnemonics that represent disassembled machine code. I’ve also added some comments.
0: b8 00 00 mov ax,0x0 # put 0x0 into AX
3: 8e d8 mov ds,ax # copy AX into DS
5: b8 03 00 mov ax,0x3 # put 0x3 into AX
8: cd 10 int 0x10 # run 0x10 BIOS interruption
The only way that this kind of program can communicate with computer peripherals like disks, keyboards and screens are BIOS interrupts. They are special signals sent to the processor instructing it to run a particular external procedure. An interrupt can be called by using int
mnemonic with an interrupt identifier and some other parameters placed in processor registers.
The above piece of code runs BIOS interrupt number 0x10
. It can be used to perform different operations with a screen, like writing and reading characters. This program requests cursor shape and position. To be honest, I have no idea why the author put it there, but let’s treat it as a nice warm up before the next part.
a: b8 00 80 mov ax,0x8000 # set destination memory address
d: 8e c0 mov es,ax
f: b8 01 02 mov ax,0x201 # set AX to read one sector from disk
12: b5 00 mov ch,0x0 # configure the source
14: b1 02 mov cl,0x2
16: b6 00 mov dh,0x0
18: b2 80 mov dl,0x80
1a: 31 db xor bx,bx
1c: cd 13 int 0x13 # run disk-related interrupt 0x13
Now the things start to make more sense. Interruption 0x13
together with AH register set to 0x02
read bytes from the disk into the memory. In this case, it will read the entire second sector of the disk (512 bytes). Why? A quick look at this content with a tool called hexdump
immediately answers this question:
00000200 b8 00 00 8e d8 20 4b 6f 6d 70 75 74 65 72 20 7a |..... Komputer z|
00000210 61 62 6c 6f 6b 6f 77 61 6e 79 2c 20 64 79 73 6b |ablokowany, dysk|
00000220 20 7a 61 73 7a 79 66 72 6f 77 61 6e 79 0d 0a 20 | zaszyfrowany.. |
00000230 41 62 79 20 6f 64 62 6c 6f 6b 6f 77 61 63 20 77 |Aby odblokowac w|
00000240 79 73 6c 69 6a 20 73 6d 73 20 6f 20 74 72 65 73 |yslij sms o tres|
00000250 63 69 0d 0a 20 22 57 50 20 41 34 37 39 32 22 20 |ci.. "WP A4792" |
00000260 6e 61 20 6e 72 20 37 39 32 38 2e 20 28 4b 6f 73 |na nr 7928. (Kos|
00000270 7a 74 20 39 7a 6c 29 0d 0a 20 4b 6f 64 20 77 70 |zt 9zl).. Kod wp|
00000280 69 73 7a 20 70 6f 6e 69 7a 65 6a 2e 0d 0a 0d 0a |isz ponizej.....|
00000290 20 4b 6f 64 3a 20 5b 20 20 20 20 20 20 20 20 5d | Kod: [ ]|
It simply contains human readable characters forming the malware’s message.
Let’s move on. The next part calls a procedure at address 0x83
:
1e: b8 00 80 mov ax,0x8000
21: e8 5f 00 call 0x83
As I expected, the procedure at address 0x83
prints appropriate content of the memory to the screen:
83: 8e c0 mov es,ax
85: be 05 00 mov si,0x5
88: 26 8a 04 mov al,BYTE PTR es:[si] # read one character
8b: 3c 00 cmp al,0x0 # check if it is equal to zero
8d: 74 09 je 0x98 # if yes then jump to return
8f: 83 c6 01 add si,0x1
92: b4 0e mov ah,0xe
94: cd 10 int 0x10 # otherwise print it to the screen
96: eb f0 jmp 0x88 # and jump back in a loop
98: c3 ret
Once all characters are printed, the program gets back to the original execution and moves on. The next part sets the cursor at position (5, 6)
, which is the first field of the text input area surrounded by square brackets:
24: b4 02 mov ah,0x2
26: b7 00 mov bh,0x0
28: b6 05 mov dh,0x5 # set column number
2a: b2 06 mov dl,0x6 # set row number
2c: cd 10 int 0x10 # move the cursor
Now things get really interesting. Interruption 0x16
is responsible for reading characters from the keyboard. Here it reads one symbol:
3f: b8 e7 7c mov ax,0x7ce7 # set a destination for a character
42: 8e d8 mov ds,ax
44: bf 00 00 mov di,0x0
47: b4 00 mov ah,0x0
49: cd 16 int 0x16 # read one character
What happens next?
4b: 3c 0d cmp al,0xd
4d: 74 0f je 0x5e
The malware checks if a user pressed ENTER and in that case it jumps to address 0x5e
. Otherwise, it enters a loop where it reads exactly 10 characters (this is the length of the unlocking code). If none of the inputted keys were ENTER, the program clears the input field and starts the whole character-reading loop from the beginning.
And now the final part. What happens when the user presses ENTER? Let’s go to 0x5e
:
5e: 80 3e 08 00 32 cmp BYTE PTR ds:0x8,0x32
63: 75 09 jne 0x6e
65: 80 3e 09 00 37 cmp BYTE PTR ds:0x9,0x37
6a: 75 02 jne 0x6e
6c: eb 3d jmp 0xab
Here the whole mystery unveils. The malware looks at 9th and 10th character of the user inputted sequence and checks if they are equal to 2
and 7
, respectively. So you can actually enter any “unlocking code”, as long as it ends up with 27
. Maybe it was only me, but I actually expected something more fancy. Should I send the text message paying the ransom? I would probably get some random sequence of characters, with the last two matching this pattern.
Last question that remained unanswered was what happens when the user enters the correct code. To find that out we have to jump to address 0xab
. The instructions below take the 5th sector from the disk and move it to the very beginning, and as you can imagine this 5th sector contained content of my original MBR. After this operation, everything goes back to normal and my operating system can boot.
ab: b8 00 80 mov ax,0x8000 # destination memory address
ae: 8e c0 mov es,ax
b0: b8 01 02 mov ax,0x201
b3: b5 00 mov ch,0x0
b5: b1 05 mov cl,0x5 # source - 5th sector
b7: b6 00 mov dh,0x0
b9: b2 80 mov dl,0x80
bb: 31 db xor bx,bx
bd: cd 13 int 0x13 # read one sector
bf: b8 00 80 mov ax,0x8000 # source memory address
c2: 8e c0 mov es,ax
c4: b8 01 03 mov ax,0x301
c7: b5 00 mov ch,0x0
c9: b1 01 mov cl,0x1 # destination - 1st sector
cb: b6 00 mov dh,0x0
cd: b2 80 mov dl,0x80
cf: 31 db xor bx,bx
d1: cd 13 int 0x13 # write one sector
Having solved this mystery, I thought that maybe I wasn’t not the only person to have this problem. I googled the ransom message text and actually found a few results.
First of all, I came across a local advertisements page where somebody offered his services to fix computers locked with that malware (price negotiable). The person stated that he or she is located in Cracow, the place where I live, so I started to suspect that this whole thing may be very local. By the way, to make this business look attractive it would have to be cheaper than 9 PLN. Doesn’t sound like a good deal for the service provider.
Removing service ad
The second thing I found was a thread on a well-known Polish discussion board about electronics and computers called elektroda.pl. One of its users described exactly the same problem and asked for help. Other people in this thread also noticed that the malware corrupts only the MBR and advised him with a solution more or less similar to mine. Nevertheless, I wrote a post with steps that I came up with. I also included the correct code pattern to unlock the system.
And then I forgot about the whole case.
Three months later, out of the blue, somebody replied to this thread:
Hello everybody and thank you for the time spent on this. I’m the author of this application. Respect to m4jkel (that’s me), who decided to analyze the code of my program. It was written in Assembler (which I adore) with Fasm compiler.
(…)
To become infected with it, the user had to install a pirated program, such as a game or operating system. If he had bought the original software he would not have any problems.
(…)
Please, don’t send messages to the given number, because the code that you’ll receive will not unlock your computer. It’s a flaw of the messaging service, which I was not able to configure to generate codes ending with desired characters.
Regards, Karol
Actually, it was quite nice that somebody appreciated my effort, even if it was the author. Unfortunately for the victims who decided to send a message, the code they received was not correct. It also meant that the author had not tested it before releasing the malware. Too bad for the victims. Maybe he was also the person who offered his repairing services to actually make some money after discovering that his initial plan hadn’t worked? Who knows.
I don’t know how many people had been affected besides me, but I’ve found a few more discussion boards mentioning the same problem. It looks like the author had prepared installation images of several programs and games with his malicious code and had put it on warez sites.
The last question that you probably have is how have I got infected with the malware. I have to admit that Karol had a point there. I had downloaded and installed a pirated game. My bad. As an excuse I can say that this game was not available on Steam back then, and I really wanted to take a break from working on my master’s thesis ;) And I’ve actually bought it later.
So, that’s what I’ve learned. Do not, under any circumstances, download and install pirated software. Apart from the fact that it’s illegal, you just put yourself, your data and your privacy at unnecessary risk. On the other hand, if you want to write a malware you better test that it works correctly. And, by the way, always do backups.