/*
* dos.c
* Detection of DOS parition maps and file systems
*
* Copyright (c) 2003-2006 Christoph Pfisterer
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "global.h"
/*
* DOS partition types
*
* Taken from fdisk/i386_sys_types.c and fdisk/common.h of
* util-linux 2.11n (as packaged by Debian), Feb 08, 2003.
*/
struct systypes {
unsigned char type;
char *name;
};
struct systypes i386_sys_types[] = {
{ 0x00, "Empty" },
{ 0x01, "FAT12" },
{ 0x02, "XENIX root" },
{ 0x03, "XENIX usr" },
{ 0x04, "FAT16 <32M" },
{ 0x05, "Extended" },
{ 0x06, "FAT16" },
{ 0x07, "HPFS/NTFS" },
{ 0x08, "AIX" },
{ 0x09, "AIX bootable" },
{ 0x0a, "OS/2 Boot Manager" },
{ 0x0b, "Win95 FAT32" },
{ 0x0c, "Win95 FAT32 (LBA)" },
{ 0x0e, "Win95 FAT16 (LBA)" },
{ 0x0f, "Win95 Ext'd (LBA)" },
{ 0x10, "OPUS" },
{ 0x11, "Hidden FAT12" },
{ 0x12, "Compaq diagnostics" },
{ 0x14, "Hidden FAT16 <32M" },
{ 0x16, "Hidden FAT16" },
{ 0x17, "Hidden HPFS/NTFS" },
{ 0x18, "AST SmartSleep" },
{ 0x1b, "Hidden Win95 FAT32" },
{ 0x1c, "Hidden Win95 FAT32 (LBA)" },
{ 0x1e, "Hidden Win95 FAT16 (LBA)" },
{ 0x24, "NEC DOS" },
{ 0x39, "Plan 9" },
{ 0x3c, "PartitionMagic recovery" },
{ 0x40, "Venix 80286" },
{ 0x41, "PPC PReP Boot" },
{ 0x42, "SFS / MS LDM" },
{ 0x4d, "QNX4.x" },
{ 0x4e, "QNX4.x 2nd part" },
{ 0x4f, "QNX4.x 3rd part" },
{ 0x50, "OnTrack DM" },
{ 0x51, "OnTrack DM6 Aux1" },
{ 0x52, "CP/M" },
{ 0x53, "OnTrack DM6 Aux3" },
{ 0x54, "OnTrackDM6" },
{ 0x55, "EZ-Drive" },
{ 0x56, "Golden Bow" },
{ 0x5c, "Priam Edisk" },
{ 0x61, "SpeedStor" },
{ 0x63, "GNU HURD or SysV" },
{ 0x64, "Novell Netware 286" },
{ 0x65, "Novell Netware 386" },
{ 0x70, "DiskSecure Multi-Boot" },
{ 0x75, "PC/IX" },
{ 0x78, "XOSL" },
{ 0x80, "Old Minix" },
{ 0x81, "Minix / old Linux" },
{ 0x82, "Linux swap / Solaris" },
{ 0x83, "Linux" },
{ 0x84, "OS/2 hidden C: drive" },
{ 0x85, "Linux extended" },
{ 0x86, "NTFS volume set" },
{ 0x87, "NTFS volume set" },
{ 0x8e, "Linux LVM" },
{ 0x93, "Amoeba" },
{ 0x94, "Amoeba BBT" },
{ 0x9f, "BSD/OS" },
{ 0xa0, "IBM Thinkpad hibernation" },
{ 0xa5, "FreeBSD" },
{ 0xa6, "OpenBSD" },
{ 0xa7, "NeXTSTEP" },
{ 0xa9, "NetBSD" },
{ 0xaf, "Mac OS X" },
{ 0xb7, "BSDI fs" },
{ 0xb8, "BSDI swap" },
{ 0xbb, "Boot Wizard hidden" },
{ 0xc1, "DRDOS/sec (FAT-12)" },
{ 0xc4, "DRDOS/sec (FAT-16 < 32M)" },
{ 0xc6, "DRDOS/sec (FAT-16)" },
{ 0xc7, "Syrinx" },
{ 0xda, "Non-FS data" },
{ 0xdb, "CP/M / CTOS / ..." },
{ 0xde, "Dell Utility" },
{ 0xdf, "BootIt" },
{ 0xe1, "DOS access" },
{ 0xe3, "DOS R/O" },
{ 0xe4, "SpeedStor" },
{ 0xeb, "BeOS fs" },
{ 0xee, "EFI GPT protective" },
{ 0xef, "EFI System (FAT)" },
{ 0xf0, "Linux/PA-RISC boot" },
{ 0xf1, "SpeedStor" },
{ 0xf4, "SpeedStor" },
{ 0xf2, "DOS secondary" },
{ 0xfd, "Linux raid autodetect" },
{ 0xfe, "LANstep" },
{ 0xff, "BBT" },
{ 0, 0 }
};
char * get_name_for_mbrtype(int type)
{
int i;
for (i = 0; i386_sys_types[i].name; i++)
if (i386_sys_types[i].type == type)
return i386_sys_types[i].name;
return "Unknown";
}
/*
* DOS-style partition map / MBR
*/
static void detect_dos_partmap_ext(SECTION *section, u8 extbase,
int level, int *extpartnum);
void detect_dos_partmap(SECTION *section, int level)
{
unsigned char *buf;
int i, off, used, type, types[4], bootflags[4];
u4 start, size, starts[4], sizes[4];
int extpartnum = 5;
char s[256], append[64];
/* partition maps only occur at the start of a device */
if (section->pos != 0)
return;
if (get_buffer(section, 0, 512, (void **)&buf) < 512)
return;
/* check signature */
if (buf[510] != 0x55 || buf[511] != 0xAA)
return;
/* get entries and check */
used = 0;
for (off = 446, i = 0; i < 4; i++, off += 16) {
/* get data */
bootflags[i] = buf[off];
types[i] = buf[off + 4];
starts[i] = get_le_long(buf + off + 8);
sizes[i] = get_le_long(buf + off + 12);
/* bootable flag: either on or off */
if (bootflags[i] != 0x00 && bootflags[i] != 0x80)
return;
/* size non-zero -> entry in use */
if (starts[i] && sizes[i])
used = 1;
}
if (!used)
return;
/* parse the data for real */
print_line(level, "DOS/MBR partition map");
for (i = 0; i < 4; i++) {
start = starts[i];
size = sizes[i];
type = types[i];
if (start == 0 || size == 0)
continue;
sprintf(append, " from %lu", start);
if (bootflags[i] == 0x80)
strcat(append, ", bootable");
format_blocky_size(s, size, 512, "sectors", append);
print_line(level, "Partition %d: %s",
i+1, s);
print_line(level + 1, "Type 0x%02X (%s)", type, get_name_for_mbrtype(type));
if (type == 0x05 || type == 0x0f || type == 0x85) {
/* extended partition */
detect_dos_partmap_ext(section, start, level + 1, &extpartnum);
} else if (type != 0xee) {
/* recurse for content detection */
analyze_recursive(section, level + 1,
(u8)start * 512, (u8)size * 512, 0);
}
}
}
static void detect_dos_partmap_ext(SECTION *section, u8 extbase,
int level, int *extpartnum)
{
unsigned char *buf;
u8 tablebase, nexttablebase;
int i, off, type, types[4];
u4 start, size, starts[4], sizes[4];
char s[256], append[64];
for (tablebase = extbase; tablebase; tablebase = nexttablebase) {
/* read sector from linked list */
if (get_buffer(section, tablebase << 9, 512, (void **)&buf) < 512)
return;
/* check signature */
if (buf[510] != 0x55 || buf[511] != 0xAA) {
print_line(level, "Signature missing");
return;
}
/* get entries */
for (off = 446, i = 0; i < 4; i++, off += 16) {
types[i] = buf[off + 4];
starts[i] = get_le_long(buf + off + 8);
sizes[i] = get_le_long(buf + off + 12);
}
/* parse the data for real */
nexttablebase = 0;
for (i = 0; i < 4; i++) {
start = starts[i];
size = sizes[i];
type = types[i];
if (size == 0)
continue;
if (type == 0x05 || type == 0x85) {
/* inner extended partition */
nexttablebase = extbase + start;
} else {
/* logical partition */
sprintf(append, " from %llu+%lu", tablebase, start);
format_blocky_size(s, size, 512, "sectors", append);
print_line(level, "Partition %d: %s",
*extpartnum, s);
(*extpartnum)++;
print_line(level + 1, "Type 0x%02X (%s)", type, get_name_for_mbrtype(type));
/* recurse for content detection */
if (type != 0xee) {
analyze_recursive(section, level + 1,
(tablebase + start) * 512, (u8)size * 512, 0);
}
}
}
}
}
/*
* EFI GPT partition map
*/
struct gpttypes {
char *guid;
char *name;
};
struct gpttypes gpt_types[] = {
{ "\x28\x73\x2A\xC1\x1F\xF8\xD2\x11\xBA\x4B\x00\xA0\xC9\x3E\xC9\x3B", "EFI System (FAT)" },
{ "\x41\xEE\x4D\x02\xE7\x33\xD3\x11\x9D\x69\x00\x08\xC7\x81\xF3\x9F", "MBR partition scheme" },
{ "\x16\xE3\xC9\xE3\x5C\x0B\xB8\x4D\x81\x7D\xF9\x2D\xF0\x02\x15\xAE", "MS Reserved" },
{ "\xA2\xA0\xD0\xEB\xE5\xB9\x33\x44\x87\xC0\x68\xB6\xB7\x26\x99\xC7", "Basic Data" },
{ "\xAA\xC8\x08\x58\x8F\x7E\xE0\x42\x85\xD2\xE1\xE9\x04\x34\xCF\xB3", "MS LDM Metadata" },
{ "\xA0\x60\x9B\xAF\x31\x14\x62\x4F\xBC\x68\x33\x11\x71\x4A\x69\xAD", "MS LDM Data" },
{ "\x1E\x4C\x89\x75\xEB\x3A\xD3\x11\xB7\xC1\x7B\x03\xA0\x00\x00\x00", "HP/UX Data" },
{ "\x28\xE7\xA1\xE2\xE3\x32\xD6\x11\xA6\x82\x7B\x03\xA0\x00\x00\x00", "HP/UX Service" },
{ "\x0F\x88\x9D\xA1\xFC\x05\x3B\x4D\xA0\x06\x74\x3F\x0F\x84\x91\x1E", "Linux RAID" },
{ "\x6D\xFD\x57\x06\xAB\xA4\xC4\x43\x84\xE5\x09\x33\xC8\x4B\x4F\x4F", "Linux Swap" },
{ "\x79\xD3\xD6\xE6\x07\xF5\xC2\x44\xA2\x3C\x23\x8F\x2A\x3D\xF9\x28", "Linux LVM" },
{ "\x39\x33\xA6\x8D\x07\x00\xC0\x60\xC4\x36\x08\x3A\xC8\x23\x09\x08", "Linux Reserved" },
{ "\xB4\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", "FreeBSD Data" },
{ "\xB5\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", "FreeBSD Swap" },
{ "\xB6\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", "FreeBSD UFS" },
{ "\xB8\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", "FreeBSD Vinum" },
{ "\x00\x53\x46\x48\x00\x00\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", "Mac HFS+" },
{ 0, 0 }
};
static char * get_name_for_guid(void *guid)
{
int i;
for (i = 0; gpt_types[i].name; i++)
if (memcmp(gpt_types[i].guid, guid, 16) == 0)
return gpt_types[i].name;
return "Unknown";
}
void detect_gpt_partmap(SECTION *section, int level)
{
unsigned char *buf;
u8 diskblocks, partmap_start, start, end, size;
u4 partmap_count, partmap_entry_size;
u4 i;
char s[256], append[64];
int last_unused;
/* partition maps only occur at the start of a device */
if (section->pos != 0)
return;
/* get LBA 1: GPT header */
if (get_buffer(section, 512, 512, (void **)&buf) < 512)
return;
/* check signature */
if (memcmp(buf, "EFI PART", 8) != 0)
return;
/* get header information */
if (get_le_quad(buf + 0x18) != 1)
return;
diskblocks = get_le_quad(buf + 0x20) + 1;
partmap_start = get_le_quad(buf + 0x48);
partmap_count = get_le_long(buf + 0x50);
partmap_entry_size = get_le_long(buf + 0x54);
print_line(level, "GPT partition map, %d entries", (int)partmap_count);
format_blocky_size(s, diskblocks, 512, "sectors", NULL);
print_line(level+1, "Disk size %s", s);
format_guid(buf + 0x38, s);
print_line(level+1, "Disk GUID %s", s);
/* get entries */
last_unused = 0;
for (i = 0; i < partmap_count; i++) {
if (get_buffer(section, (partmap_start * 512) + i * partmap_entry_size, partmap_entry_size, (void **)&buf) < partmap_entry_size)
return;
if (memcmp(buf, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
if (last_unused == 0)
print_line(level, "Partition %d: unused", i+1);
last_unused = 1;
continue;
}
last_unused = 0;
/* size */
start = get_le_quad(buf + 0x20);
end = get_le_quad(buf + 0x28);
size = end + 1 - start;
sprintf(append, " from %llu", start);
format_blocky_size(s, size, 512, "sectors", append);
print_line(level, "Partition %d: %s", i+1, s);
/* type */
format_guid(buf, s);
print_line(level+1, "Type %s (GUID %s)", get_name_for_guid(buf), s);
/* partition name */
format_utf16_le(buf + 0x38, 72, s);
print_line(level+1, "Partition Name \"%s\"", s);
/* GUID */
format_guid(buf + 0x10, s);
print_line(level+1, "Partition GUID %s", s);
/* recurse for content detection */
if (start > 0 && size > 0) { /* avoid recursion on self */
analyze_recursive(section, level + 1,
start * 512, size * 512, 0);
}
}
}
/*
* FAT12/FAT16/FAT32 file systems
*/
static char *fatnames[] = { "FAT12", "FAT16", "FAT32" };
void detect_fat(SECTION *section, int level)
{
int i, score, fattype;
u4 sectsize, clustersize, reserved, fatcount, dirsize, fatsize;
u8 sectcount, clustercount;
u2 atari_csum;
unsigned char *buf;
char s[256];
if (get_buffer(section, 0, 512, (void **)&buf) < 512)
return;
/* first, some hard tests */
/* sector size has four allowed values */
sectsize = get_le_short(buf + 11);
if (sectsize != 512 && sectsize != 1024 &&
sectsize != 2048 && sectsize != 4096)
return;
/* sectors per cluster: must be a power of two */
clustersize = buf[13];
if (clustersize == 0 || (clustersize & (clustersize - 1)))
return;
/* since the above is also present on NTFS, make sure it's not NTFS... */
if (memcmp(buf + 3, "NTFS ", 8) == 0)
return;
/* next, some soft tests, taking score */
score = 0;
/* boot jump */
if ((buf[0] == 0xEB && buf[2] == 0x90) ||
buf[0] == 0xE9)
score++;
/* boot signature */
if (buf[510] == 0x55 && buf[511] == 0xAA)
score++;
/* reserved sectors */
reserved = get_le_short(buf + 14);
if (reserved == 1 || reserved == 32)
score++;
/* number of FATs */
fatcount = buf[16];
if (fatcount == 2)
score++;
/* number of root dir entries */
dirsize = get_le_short(buf + 17);
/* sector count (16-bit and 32-bit versions) */
sectcount = get_le_short(buf + 19);
if (sectcount == 0)
sectcount = get_le_long(buf + 32);
/* media byte */
if (buf[21] == 0xF0 || buf[21] >= 0xF8)
score++;
/* FAT size in sectors */
fatsize = get_le_short(buf + 22);
if (fatsize == 0)
fatsize = get_le_long(buf + 36);
/* determine FAT type */
dirsize = ((dirsize * 32) + (sectsize - 1)) / sectsize;
clustercount = sectcount - (reserved + (fatcount * fatsize) + dirsize);
clustercount /= clustersize;
if (clustercount < 4085)
fattype = 0;
else if (clustercount < 65525)
fattype = 1;
else
fattype = 2;
/* check for ATARI ST boot checksum */
atari_csum = 0;
for (i = 0; i < 512; i += 2)
atari_csum += get_be_short(buf + i);
/* tell the user */
s[0] = 0;
if (atari_csum == 0x1234)
strcpy(s, ", ATARI ST bootable");
print_line(level, "%s file system (hints score %d of %d%s)",
fatnames[fattype], score, 5, s);
if (sectsize > 512)
print_line(level + 1, "Unusual sector size %lu bytes", sectsize);
format_blocky_size(s, clustercount, clustersize * sectsize,
"clusters", NULL);
print_line(level + 1, "Volume size %s", s);
/* get the cached volume name if present */
if (fattype < 2) {
if (buf[38] == 0x29) {
memcpy(s, buf + 43, 11);
s[11] = 0;
for (i = 10; i >= 0 && s[i] == ' '; i--)
s[i] = 0;
if (strcmp(s, "NO NAME") != 0)
print_line(level + 1, "Volume name \"%s\"", s);
}
} else {
if (buf[66] == 0x29) {
memcpy(s, buf + 71, 11);
s[11] = 0;
for (i = 10; i >= 0 && s[i] == ' '; i--)
s[i] = 0;
if (strcmp(s, "NO NAME") != 0)
print_line(level + 1, "Volume name \"%s\"", s);
}
}
}
/*
* NTFS file system
*/
void detect_ntfs(SECTION *section, int level)
{
u4 sectsize, clustersize;
u8 sectcount;
unsigned char *buf;
char s[256];
if (get_buffer(section, 0, 512, (void **)&buf) < 512)
return;
/* check signatures */
if (memcmp(buf + 3, "NTFS ", 8) != 0)
return;
/* disabled for now, mkntfs(8) doesn't generate it
if (memcmp(buf + 0x24, "\x80\x00\x80\x00", 4) != 0)
return;
*/
/* sector size: must be a power of two */
sectsize = get_le_short(buf + 11);
if (sectsize < 512 || (sectsize & (sectsize - 1)))
return;
/* sectors per cluster: must be a power of two */
clustersize = buf[13];
if (clustersize == 0 || (clustersize & (clustersize - 1)))
return;
/* get size in sectors */
sectcount = get_le_quad(buf + 0x28);
/* tell the user */
print_line(level, "NTFS file system");
format_blocky_size(s, sectcount, sectsize, "sectors", NULL);
print_line(level + 1, "Volume size %s", s);
}
/*
* HPFS file system
*/
void detect_hpfs(SECTION *section, int level)
{
unsigned char *buf;
char s[256];
u8 sectcount;
if (get_buffer(section, 16*512, 512, (void **)&buf) < 512)
return;
if (memcmp(buf, "\xF9\x95\xE8\x49\xFA\x53\xE9\xC5", 8) != 0)
return;
print_line(level, "HPFS file system (version %d, functional version %d)",
(int)buf[8], (int)buf[9]);
sectcount = get_le_long(buf + 16);
format_blocky_size(s, sectcount, 512, "sectors", NULL);
print_line(level + 1, "Volume size %s", s);
/* TODO: BPB in boot sector, volume label -- information? */
}
/*
* DOS/Windows boot loaders
*/
void detect_dos_loader(SECTION *section, int level)
{
int fill;
unsigned char *buf;
if (section->flags & FLAG_IN_DISKLABEL)
return;
fill = get_buffer(section, 0, 2048, (void **)&buf);
if (fill < 512)
return;
if (find_memory(buf, fill, "NTLDR", 5) >= 0)
print_line(level, "Windows NTLDR boot loader");
else if (find_memory(buf, 512, "WINBOOT SYS", 11) >= 0)
print_line(level, "Windows 95/98/ME boot loader");
else if (find_memory(buf, 512, "MSDOS SYS", 11) >= 0)
print_line(level, "Windows / MS-DOS boot loader");
}
/* EOF */
syntax highlighted by Code2HTML, v. 0.9.1