Reverse engineering

Linux Booting Process

Dejan Lukan
April 3, 2013 by
Dejan Lukan

Introduction

In this article, we'll take a closer look at the booting process of the Linux operating system. We've already described the booting process in this article, especially how the system boots if the system partition is encrypted. Let's take a look again at the same picture that was presented in that article:

We can see from the picture that the booting process starts from the MBR (Master Boot Record), but actually, the booting process begins with the BIOS and continues from there. It's the BIOS's job to tell the computer which hard drives are available and to try to search for an active MBR record in the primary hard drive. The MBR record then tells the booting process where the /boot partition is located. Afterwards, the /boot partition is read and examined for any boot loader, which in our case is GRUB.

At that point, the booting of a computer is handed to the GRUB boot loader, which loads the grub.conf file and the kernel of the Linux system and then mounts the root partition, which in this case is /dev/sda3. The execution continues with the kernel image located on the /boot partition. The kernel is loaded and the execution is handed over to the Gentoo init process (with a PID number 1), which is already located on the system partition and is responsible for starting the entire system.

The init process takes care of everything the system needs to boot up successfully. One of the things it does is to start the required scripts, usually located in the /etc/rc.d/ directory. It must also mount the partitions that are listed in the /etc/fstab configuration file. Once everything is done, the login window appears, allowing us to log in to our operating system.[1]

We've described the process of booting the Linux operating system only, but it's quite the same for other operating systems too. In the next part of the article, we'll take a look at more details regarding the booting process but only at what happens until the operating system itself gains control and does its thing. This way, we'll eliminate the need to explain the booting process of every single operating system, because each and every OS does things differently and we're not interested in those at the moment.

BIOS and the MBR

First, we must be aware that the majority of computers have something called the BIOS that begins the initial booting process right after we turn on the computer. But why do we even need BIOS? The reason is that the boot loader is located on a hard drive we have in our machine, and right upon turning on the computer, the processor doesn't know anything about the boot loader being available on the hard drive. It's the BIOS's job to find and load the boot loader installed on the hard drive.

We all know that when we turn on the computer, the BIOS first scans for devices, determines the amount of memory available, etc. After that, the BIOS must scan for bootable devices as configured in the BIOS settings, in that order. Typically, the BIOS finds the hard drive that contains the MBR (Master Boot Record), which must also contain the primary boot loader code.

When the BIOS finds the MBR record, it passes control to the primary boot loader. The primary boot loader must then scan the partition table that is also written in the MBR record and find the active partition, which contains the secondary boot loader. Let's first dump the MBR in Linux and check its contents. To do that, we must copy the first sector of the disk to the mbr.bin file with the dd command like this:

[bash]

# dd if=/dev/sda of=/tmp/mbr.bin bs=512 count=1

1+0 records in

1+0 records out

512 bytes (512 B) copied, 0.0006952 s, 736 kB/s

[/bash]

Now we can use the all-powerful file command to display the contents of the MBR in a more readable format. We could read the hex bytes directly, but why should we? Note that we've used the tr command only to replace all the ';' characters with the new line characters to make the output easier to read:

[plain]

# file /tmp/mbr.bin | tr ';' 'n'

/tmp/mbr.bin: x86 boot sector

GRand Unified Bootloader, stage1 version 0x3, stage2 address 0x2000, stage2 segment 0x200

partition 1: ID=0x83, starthead 32, startsector 2048, 3906250 sectors

partition 2: ID=0x83, starthead 71, startsector 3908298, 78125000 sectors

partition 3: ID=0x83, starthead 85, startsector 82033298, 418084894 sectors, code offset 0x48

[/plain]

We can see that the MBR image has knowledge of three partitions, which have the ID of 0x83 that identifies them as ext3 file system. We can see that none of the partitions is marked as active partitions, but that isn't absolutely necessary for the system to boot successfully (my system boots fine without this). But let's mark the first partition as bootable with the fdisk command:

[plain]

# fdisk /dev/sda

Welcome to fdisk (util-linux 2.22.2).

Command (m for help): p

Device Boot Start End Blocks Id System

/dev/sda1 2048 3908297 1953125 83 Linux

/dev/sda2 3908298 82033297 39062500 83 Linux

/dev/sda3 82033298 500118191 209042447 83 Linux

Command (m for help): a

Partition number (1-4): 1

Command (m for help): p

Device Boot Start End Blocks Id System

/dev/sda1 * 2048 3908297 1953125 83 Linux

/dev/sda2 3908298 82033297 39062500 83 Linux

/dev/sda3 82033298 500118191 209042447 83 Linux

Command (m for help): w

The partition table has been altered!

[/plain]

We've entered the p command in the fdisk to display all the partitions. We can see from the output that none of the partitions is currently active. After that, we used the a command to mark the first partition as active and printed the partitions again. This time, the first partition contains the '*' character in the output in the second column, which means that that partition is currently active. At the end, we also have to enter the w command to save the changes we've just made to the MBR sector.

Now we need to reboot the system for the changes to take effect. After rebooting, we should dump the contents of the MBR sector again and print them with the file command. The contents of the MBR are shown below:

[plain]

# file /tmp/mbr.bin | tr ';' 'n'

/tmp/mbr.bin: x86 boot sector

GRand Unified Bootloader, stage1 version 0x3, stage2 address 0x2000, stage2 segment 0x200

partition 1: ID=0x83, active, starthead 32, startsector 2048, 3906250 sectors

partition 2: ID=0x83, starthead 71, startsector 3908298, 78125000 sectors

partition 3: ID=0x83, starthead 85, startsector 82033298, 418084894 sectors, code offset 0x48

[/plain]

We can see that the first partition is the active partition. We've identified that the MBR contains the first boot loader as well as the partition table, which is very important information since, without it, the operating system cannot boot. The job of the BIOS is to identify the first bootable device that contains the MBR record and pass control to the primary boot loader, who must then determine which partition is active and load the secondary boot loader from that partition.

The structure of the normal MBR record is as follows (the picture was taken from [3]):

You can see that the first boot loader takes the first 446 bytes of the 512-byte MBR, which is 88% of the size. There are also four partition entries, which clearly gives us the idea why we can only set up four primary partitions in any partition editor. If we want to set up more partitions, we have to use logical partitions. The MBR entry also ends with the boot signature 0x55aa, as we can see on the picture above.

Let's dump the contents of the mbr.bin file we've dumped previously:

[plain]

# xxd /tmp/mbr.bin | sed 's/ .*//g'

0000000: eb48 9010 8ed0 bc00 b0b8 0000 8ed8 8ec0

0000010: fbbe 007c bf00 06b9 0002 f3a4 ea21 0600

0000020: 00be be07 3804 750b 83c6 1081 fefe 0775

0000030: f3eb 16b4 02b0 01bb 007c b280 8a74 0302

0000040: ff00 0020 0100 0000 0002 fa90 90f6 c280

0000050: 7502 b280 ea59 7c00 0031 c08e d88e d0bc

0000060: 0020 fba0 407c 3cff 7402 88c2 52be 7f7d

0000070: e834 01f6 c280 7454 b441 bbaa 55cd 135a

0000080: 5272 4981 fb55 aa75 43a0 417c 84c0 7505

0000090: 83e1 0174 3766 8b4c 10be 057c c644 ff01

00000a0: 668b 1e44 7cc7 0410 00c7 4402 0100 6689

00000b0: 5c08 c744 0600 7066 31c0 8944 0466 8944

00000c0: 0cb4 42cd 1372 05bb 0070 eb7d b408 cd13

00000d0: 730a f6c2 800f 84ea 00e9 8d00 be05 7cc6

00000e0: 44ff 0066 31c0 88f0 4066 8944 0431 d288

00000f0: cac1 e202 88e8 88f4 4089 4408 31c0 88d0

0000100: c0e8 0266 8904 66a1 447c 6631 d266 f734

0000110: 8854 0a66 31d2 66f7 7404 8854 0b89 440c

0000120: 3b44 087d 3c8a 540d c0e2 068a 4c0a fec1

0000130: 08d1 8a6c 0c5a 8a74 0bbb 0070 8ec3 31db

0000140: b801 02cd 1372 2a8c c38e 0648 7c60 1eb9

0000150: 0001 8edb 31f6 31ff fcf3 a51f 61ff 2642

0000160: 7cbe 857d e840 00eb 0ebe 8a7d e838 00eb

0000170: 06be 947d e830 00be 997d e82a 00eb fe47

0000180: 5255 4220 0047 656f 6d00 4861 7264 2044

0000190: 6973 6b00 5265 6164 0020 4572 726f 7200

00001a0: bb01 00b4 0ecd 10ac 3c00 75f4 c300 0000

00001b0: 0000 0000 0000 0000 3455 0000 0000 0020

00001c0: 2100 8347 1ef3 0008 0000 ca9a 3b00 0047

00001d0: 1ff3 8355 f5f2 caa2 3b00 c817 a804 0055

00001e0: f6f2 83e9 7f9a 92ba e304 1e78 eb18 0000

00001f0: 0000 0000 0000 0000 0000 0000 0000 55aa

[/plain]

Notice that the signature of the MBR is indeed 0x55aa at the end of the dump.

The secondary boot loader is often GRUB or LILO for Linux and Bootmgr for Windows. Their job is to load the kernel and boot the operating system. The primary boot loader searches for the active partition and loads its VBR (Volume Boot Record) into memory. The VBR is a boot sector that contains the machine code for bootstrapping programs, usually operating systems stored in other parts of the device.[2]

It's also often the case that we install the GRUB or LILO boot loader directly into the MBR, because if we don't, the BIOS still needs a primary boot loader that loads the secondary one. But why shouldn't we install GRUB directly in the MBR as the primary boot loader instead? This is actually the default way that GRUB/LILO is installed today.

Let's take a look at how we would install GRUB to be the primary and secondary boot loader. To install Grub as the primary boot loader, therefore on the MBR, we should run the following:

[bash]

# grub-install --no-floppy /dev/sda

[/bash]

But if we would like to install Grub on the VBR of a partition /dev/sda1, we should run the following:

[bash]

# grub-install --no-floppy /dev/sda1

[/bash]

Let's also take a look at the contents of the /boot/grub/ folder, which are listed below:

[plain]

# ls /boot/grub/

default e2fs_stage1_5 ffs_stage1_5 iso9660_stage1_5 menu.lst reiserfs_stage1_5 stage1 stage2.old ufs2_stage1_5 xfs_stage1_5

device.map fat_stage1_5 grub.conf jfs_stage1_5 minix_stage1_5 splash.xpm.gz stage2 stage2_eltorito vstafs_stage1_5

[/plain]

This gives us a clear indication that the boot loader must also support the file system that the system is using. In our case, since we're using ext3 file system, GRUB should be able to support it, because it must mount it and pass control to the operating system. GRUB supports the following file systems: ext2, DOS FAT16, FAT32, FFS, JFS, ReiserFS, MinixFS, UFS, XFS, VstaFS and Iso9660.

Conclusion

We've taken a look at how GRUB boots the Linux operating system, but we've also reviewed the role the BIOS has in the whole picture. We've seen that the BIOS is pretty important when it comes to booting the primary boot loader, as without it, it wouldn't be possible to boot the operating system from the hard drive in normal computers.

References:

[1] Dejan Lukan, LUKS: Swap, Root and Boot Partitions, accessible at /luks-swap-root-boot-partitions/.

[2] Volume boot record, accessible at http://en.wikipedia.org/wiki/Volume_boot_record.

[3] Master boot record, accessible at http://en.wikipedia.org/wiki/Master_boot_record.

Dejan Lukan
Dejan Lukan

Dejan Lukan is a security researcher for InfoSec Institute and penetration tester from Slovenia. He is very interested in finding new bugs in real world software products with source code analysis, fuzzing and reverse engineering. He also has a great passion for developing his own simple scripts for security related problems and learning about new hacking techniques. He knows a great deal about programming languages, as he can write in couple of dozen of them. His passion is also Antivirus bypassing techniques, malware research and operating systems, mainly Linux, Windows and BSD. He also has his own blog available here: http://www.proteansec.com/.