Technical Analysis : NotPetya\Petya MBR
This post will cover technical details of custom boot loader of NotPetya\Petya.
It starts with copying disk sectors (overwritten by malware) 1 to 32 at address 0x8000. Below is the set of instruction performing above operation.
To read sectors from disk it use INT13 BIOS interrupt call.Below is an example of INT 13 interrupt.
AH 42h = function number for extended read
DL 80h = drive index (e.g. 1st HDD = 80h)
DS:SI 7BDCh = segment:offset pointer to the DAP, see below
DAP : Disk Address Packet
offset range size value description
00h 1 byte 10h size of DAP (set this to 10h)
01h 1 byte 00h unused, should be zero
02h..03h 2 bytes 01h number of sectors to be read
04h..07h 4 bytes 0x8000h segment:offset pointer to the memory buffer to which sectors will be transferred
08h..0Fh 8 bytes 01h absolute number of the start of the sectors to be read (1st sector of drive has number 0)
memory buffer 0x8000 where 1st sector is read and next stage execution begins.
Next step is to get information of drives having status code set between 0x80-0x8F. Below information about a drive is collected and stored in array where each entry is of 8-byte.
1.Status Code - 1byte
2.Drive Sector 0 contain overwritten MBR - 1byte
3.Drive Present - 1byte
4.Total Number of Sectors - 4bytes
Read Drive Parameters, if Drive exist this call will be successful and Drive Present Flag will be set to 1 else set to 0.
Get total number of sectors in a drive by reading partition table entries and update value Total Number of Sectors.
Check sector 0 of drive contain bytes overwritten by NotPetya\Petya and set Drive Sector 0 contain overwritten MBR flag to 1 else set to 0.
Once information about all drives are read.It is stored into drive info array where each drive entry is eight bytes.Below drive info array containing two entries.
|Drive info array|
1.Status Code - 0x80
2.Drive Sector 0 contain overwritten MBR - 0x1
3.Drive Present - 0x1
4.Total Number of Sectors - 0x77FE098
Next it check whether drive is already encrypted by reading sector 32 (contains salsa20 key) of drive for which Drive Sector 0 contain overwritten MBR value is set to 1.If first byte of sector 32 is set to 1 it means already encrypted else not encrypted.
If disk is not encrypted.It proceeds with disk encryption and display below message on screen.
Read sector 32(contains sals20 key) and move 1 to first byte of sector 32 and destroy next 0x20 bytes (which is salsa20 key) by overwriting with 0's and write back to sector 32 of Hard Disk Drive.
Read sector 32.
move 1 to first byte of sector 32.
Destroy next 0x20 bytes (which is salsa20 key).
Buffer containing sector 32 overwritten bytes.
Write above buffer byte to sector 32 of Hard Drive Disk.
Next step is to iterate over drive info array entries mentioned above and if Drive Present value of an entry is set perform below task.
1.Read Sector 34 (holds encrypted System MBR).
2.Decrypt System MBR.
3.Read Partition Table entries from decrypted System MBR.
4.Read first absolute sector and Number of sectors for each partition table entry.
5.For each partition table entry calculate address of NTFS_BOOT_SECTOR structure.
6.From NTFS_BOOT_SECTOR structure read field n64MFTLogicalClustNum and calculate address of NTFS_MFT_FILE_ENTRY_HEADER.
7.From NTFS_MFT_FILE_ENTRY_HEADER read field wAttribOffset and jump to Master File Table Attributes represented by NTFS_ATTRIBUTE structure.
8.Continue to next Master File Table Attribute until Master File Table Attribute of type Data is not found.
9.Read Data run lists entry of Master File Table Data Attribute.
10.For each Data run list calculate number of clusters in it and offset of first cluster.
11.Start encrypting bytes of Data run list clusters using salsa20 algorithm.
Read sector 34(holds encrypted system MBR).
Decrypt System MBR.
Read Partition Table entries from decrypted System MBR
Read first absolute sector and Number of sectors for each partition table entry.
First Partition Entry
1.First Absolute Sector - 0x3F
2.Number of Sectors - 0x04FF9725
Second Partition Entry
1.First Absolute Sector - 0x04FF9764
2.Number of Sectors - 0x02804934
For each partition table entry calculate address of NTFS_BOOT_SECTOR structure.
NTFS_BOOT_SECTOR is located at first physical address of partition entry which can be calculated as -Address of NTFS_BOOT_SECTOR = First Absolute Sector of partition entry* Size of Sector = 0x3F * 0x200 = 0x7E00.
From NTFS_BOOT_SECTOR structure read field n64MFTLogicalClustNum and calculate address of NTFS_MFT_FILE_ENTRY_HEADER.
n64MFTLogicalClustNum - 0x0C0000
wBytesPerSector - 0x200
uchSectorPerClust - 0x08
Address of NTFS_MFT_FILE_ENTRY_HEADER = ((n64MFTLogicalClustNum*uchSectorPerClust) * wBytesPerSector + (First Absolute Sector * wBytesPerSector)) = (0xC0000*8)*0x200 + (0x3F*0x200) = 0xC0007E00
From NTFS_MFT_FILE_ENTRY_HEADER read field wAttribOffset and jump to Master File Table Attributes represented by NTFS_ATTRIBUTE structure.
wAttribOffset = 0x38
Continue to next Master File Table Attribute until Master File Table Attribute of type Data is not found.
Read Data run lists entry of Master File Table Data Attribute.
|MFT Data Attributes|
NTFS_ATTRIBUTE .dwType = 0x80
NTFS_ATTRIBUTE.dwFullLength = 0x48
NTFS_ATTRIBUTE.uchNonResFlag = 0x01
NTFS_ATTRIBUTE.NONRESIDENT.wDatarunOffset = 0x40
Portion highlighted in orange is Data run list entry.
First byte is 0x32.
Lower nibble of first byte (0x32) which is 2 means read next two bytes (highlighted in blue) adjacent to first byte 0x32. This value denotes number of cluster 0x1848.
Higher nibble of first byte (0x32) which is 3 means read three bytes (highlighted in green) after the number of clusters value .This value denotes Absolute value of start of clusters for Data run list (0xC0000).
Adding higher and lower nibble of first bytes i.e. 3 + 2 = 5, which denotes size(in bytes) of a Data run list entry after first byte and after that second Data run list entry begin. In above image it is 0x00 which mean no more Data run list entry.
For each Data run list calculate number of sectors in it and offset of first cluster.
Physical Address of Cluster = (Absolute Value of Start of Cluster Data run list entry* No of Sectors Per Cluster * BytesPerSector) + (First Absolute Sector * Size of Sector) = (0xC0000*8*0x200) + (0x3F*0x200) = C0007E00
In above code we can see it skips first 0x20 sectors i.e. 0x20 * 0x200 = 0x4000 bytes.
Therefore physical address of first cluster on disk from where encryption starts C0007E00 + 0x4000 = 0xC000BE00.
Number of sectors to encrypt = (Number of Cluster in Data run list entry * No of Sectors Per Cluster) = 0x1848 * 8 = 0xC240
It skips first 0x20 sectors therefore Number of sectors to encrypt = 0xC240 - 20 = 0xC220 (49696).
Before start with encryption of 0xC220(49696) sectors from physical address 0xC000BE00 it display fake CHKDSK message.
Read two sectors (0x400 bytes) from physical address 0xC000BE00 and encrypt using salsa20 algorithm.
Disk Address packet for reading disk.
Number of Sectors to read - 0x02
absolute number of the start of the sectors to be read - 0x60005F = 0x60005F * 0x200 = 0xC000BE00