TestDisk: detect APFS partition

This commit is contained in:
Christophe Grenier 2021-06-02 19:07:44 +02:00
parent 8f11e3a897
commit cd7b7032f6
9 changed files with 416 additions and 13 deletions

View file

@ -1,4 +1,4 @@
.PRECIOUS: session_%.framac session_fidentify-%.framac .PRECIOUS: session_%.framac session_fidentify-%.framac session_photorec-%.framac
FRAMA_C_FLAGS=-machdep x86_64 \ FRAMA_C_FLAGS=-machdep x86_64 \
-warn-left-shift-negative \ -warn-left-shift-negative \
-warn-right-shift-negative \ -warn-right-shift-negative \
@ -36,13 +36,13 @@ endif
bin_PROGRAMS = testdisk photorec fidentify $(QPHOTOREC) bin_PROGRAMS = testdisk photorec fidentify $(QPHOTOREC)
EXTRA_PROGRAMS = photorecf fuzzerfidentify EXTRA_PROGRAMS = photorecf fuzzerfidentify
smallbase_C = common.c crc.c ext2_common.c fat_common.c log.c misc.c setdate.c smallbase_C = common.c crc.c apfs_common.c ext2_common.c fat_common.c log.c misc.c setdate.c
smallbase_H = common.h crc.h ext2_common.h fat_common.h log.h misc.h setdate.h smallbase_H = common.h crc.h apfs_common.h ext2_common.h fat_common.h log.h misc.h setdate.h
base_C = $(smallbase_C) autoset.c ewf.c fnctdsk.c hdaccess.c hdcache.c hdwin32.c hidden.c hpa_dco.c intrf.c iso.c list_sort.c log_part.c msdos.c parti386.c partgpt.c parthumax.c partmac.c partsun.c partnone.c partxbox.c io_redir.c ntfs_io.c ntfs_utl.c partauto.c sudo.c unicode.c win32.c base_C = $(smallbase_C) autoset.c ewf.c fnctdsk.c hdaccess.c hdcache.c hdwin32.c hidden.c hpa_dco.c intrf.c iso.c list_sort.c log_part.c msdos.c parti386.c partgpt.c parthumax.c partmac.c partsun.c partnone.c partxbox.c io_redir.c ntfs_io.c ntfs_utl.c partauto.c sudo.c unicode.c win32.c
base_H = $(smallbase_H) alignio.h autoset.h ewf.h fnctdsk.h hdaccess.h hdwin32.h hidden.h guid_cmp.h guid_cpy.h hdcache.h hpa_dco.h intrf.h iso.h iso9660.h lang.h list.h list_sort.h log_part.h types.h io_redir.h msdos.h ntfs_utl.h parti386.h partgpt.h parthumax.h partmac.h partsun.h partxbox.h partauto.h sudo.h unicode.h win32.h base_H = $(smallbase_H) alignio.h autoset.h ewf.h fnctdsk.h hdaccess.h hdwin32.h hidden.h guid_cmp.h guid_cpy.h hdcache.h hpa_dco.h intrf.h iso.h iso9660.h lang.h list.h list_sort.h log_part.h types.h io_redir.h msdos.h ntfs_utl.h parti386.h partgpt.h parthumax.h partmac.h partsun.h partxbox.h partauto.h sudo.h unicode.h win32.h
fs_C = analyse.c bfs.c bsd.c btrfs.c cramfs.c exfat.c ext2.c fat.c fatx.c f2fs.c jfs.c gfs2.c hfs.c hfsp.c hpfs.c luks.c lvm.c md.c netware.c ntfs.c refs.c rfs.c savehdr.c sun.c swap.c sysv.c ufs.c vmfs.c wbfs.c xfs.c zfs.c fs_C = analyse.c apfs.c bfs.c bsd.c btrfs.c cramfs.c exfat.c ext2.c fat.c fatx.c f2fs.c jfs.c gfs2.c hfs.c hfsp.c hpfs.c luks.c lvm.c md.c netware.c ntfs.c refs.c rfs.c savehdr.c sun.c swap.c sysv.c ufs.c vmfs.c wbfs.c xfs.c zfs.c
fs_H = analyse.h bfs.h bsd.h btrfs.h cramfs.h exfat.h ext2.h fat.h fatx.h f2fs.h f2fs_fs.h jfs_superblock.h jfs.h gfs2.h hfs.h hfsp.h hpfs.h hfsp_struct.h luks.h luks_struct.h lvm.h md.h netware.h ntfs.h ntfs_struct.h refs.h rfs.h savehdr.h sun.h swap.h sysv.h ufs.h vmfs.h wbfs.h xfs.h xfs_struct.h zfs.h fs_H = analyse.h apfs.h bfs.h bsd.h btrfs.h cramfs.h exfat.h ext2.h fat.h fatx.h f2fs.h f2fs_fs.h jfs_superblock.h jfs.h gfs2.h hfs.h hfsp.h hpfs.h hfsp_struct.h luks.h luks_struct.h lvm.h md.h netware.h ntfs.h ntfs_struct.h refs.h rfs.h savehdr.h sun.h swap.h sysv.h ufs.h vmfs.h wbfs.h xfs.h xfs_struct.h zfs.h
testdisk_ncurses_C = addpart.c addpartn.c adv.c askloc.c chgarch.c chgarchn.c chgtype.c chgtypen.c dimage.c dirn.c dirpart.c diskacc.c diskcapa.c edit.c ext2_sb.c ext2_sbn.c fat1x.c fat32.c fat_adv.c fat_cluster.c fatn.c geometry.c geometryn.c godmode.c hiddenn.c intrface.c intrfn.c nodisk.c ntfs_adv.c ntfs_fix.c ntfs_udl.c parti386n.c partgptn.c partmacn.c partsunn.c partxboxn.c tanalyse.c tbanner.c tdelete.c tdiskop.c tdisksel.c testdisk.c texfat.c thfs.c tload.c tlog.c tmbrcode.c tntfs.c toptions.c tpartwr.c testdisk_ncurses_C = addpart.c addpartn.c adv.c askloc.c chgarch.c chgarchn.c chgtype.c chgtypen.c dimage.c dirn.c dirpart.c diskacc.c diskcapa.c edit.c ext2_sb.c ext2_sbn.c fat1x.c fat32.c fat_adv.c fat_cluster.c fatn.c geometry.c geometryn.c godmode.c hiddenn.c intrface.c intrfn.c nodisk.c ntfs_adv.c ntfs_fix.c ntfs_udl.c parti386n.c partgptn.c partmacn.c partsunn.c partxboxn.c tanalyse.c tbanner.c tdelete.c tdiskop.c tdisksel.c testdisk.c texfat.c thfs.c tload.c tlog.c tmbrcode.c tntfs.c toptions.c tpartwr.c
testdisk_ncurses_H = addpart.h addpartn.h adv.h askloc.h chgarch.h chgarchn.h chgtype.h chgtypen.h dimage.h dirn.h dirpart.h diskacc.h diskcapa.h edit.h ext2_sb.h ext2_sbn.h fat1x.h fat32.h fat_adv.h fat_cluster.h fatn.h geometry.h geometryn.h godmode.h hiddenn.h intrface.h intrfn.h nodisk.h ntfs_adv.h ntfs_fix.h ntfs_udl.h partgptn.h parti386n.h partmacn.h partsunn.h partxboxn.h tanalyse.h tdelete.h tdiskop.h tdisksel.h texfat.h thfs.h tload.h tlog.h tmbrcode.h tntfs.h toptions.h tpartwr.h testdisk_ncurses_H = addpart.h addpartn.h adv.h askloc.h chgarch.h chgarchn.h chgtype.h chgtypen.h dimage.h dirn.h dirpart.h diskacc.h diskcapa.h edit.h ext2_sb.h ext2_sbn.h fat1x.h fat32.h fat_adv.h fat_cluster.h fatn.h geometry.h geometryn.h godmode.h hiddenn.h intrface.h intrfn.h nodisk.h ntfs_adv.h ntfs_fix.h ntfs_udl.h partgptn.h parti386n.h partmacn.h partsunn.h partxboxn.h tanalyse.h tdelete.h tdiskop.h tdisksel.h texfat.h thfs.h tload.h tlog.h tmbrcode.h tntfs.h toptions.h tpartwr.h

View file

@ -30,13 +30,15 @@
#include "types.h" #include "types.h"
#include "common.h" #include "common.h"
#include "analyse.h" #include "analyse.h"
#include "fat.h"
#include "exfat.h"
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
#include "apfs.h"
#include "bfs.h" #include "bfs.h"
#include "bsd.h" #include "bsd.h"
#include "btrfs.h" #include "btrfs.h"
#include "cramfs.h" #include "cramfs.h"
#include "exfat.h"
#include "ext2.h" #include "ext2.h"
#include "fat.h"
#include "fatx.h" #include "fatx.h"
#include "f2fs_fs.h" #include "f2fs_fs.h"
#include "f2fs.h" #include "f2fs.h"
@ -61,8 +63,9 @@
#include "wbfs.h" #include "wbfs.h"
#include "xfs.h" #include "xfs.h"
#include "zfs.h" #include "zfs.h"
#include "log.h" #endif
#include "parti386.h" #include "parti386.h"
#include "log.h"
int search_NTFS_backup(unsigned char *buffer, disk_t *disk, partition_t *partition, const int verbose, const int dump_ind) int search_NTFS_backup(unsigned char *buffer, disk_t *disk, partition_t *partition, const int verbose, const int dump_ind)
{ {
@ -70,10 +73,12 @@ int search_NTFS_backup(unsigned char *buffer, disk_t *disk, partition_t *partiti
return -1; return -1;
{ {
const struct ntfs_boot_sector *ntfs_header=(const struct ntfs_boot_sector*)buffer; const struct ntfs_boot_sector *ntfs_header=(const struct ntfs_boot_sector*)buffer;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
/* NTFS recovery using backup sector */ /* NTFS recovery using backup sector */
if(le16(ntfs_header->marker)==0xAA55 && if(le16(ntfs_header->marker)==0xAA55 &&
recover_NTFS(disk, ntfs_header, partition, verbose, dump_ind, 1)==0) recover_NTFS(disk, ntfs_header, partition, verbose, dump_ind, 1)==0)
return 1; return 1;
#endif
} }
return 0; return 0;
} }
@ -82,6 +87,7 @@ int search_HFS_backup(unsigned char *buffer, disk_t *disk, partition_t *partitio
{ {
if(disk->pread(disk, buffer, 0x400, partition->part_offset)!= 0x400) if(disk->pread(disk, buffer, 0x400, partition->part_offset)!= 0x400)
return -1; return -1;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
{ {
const hfs_mdb_t *hfs_mdb=(const hfs_mdb_t *)buffer; const hfs_mdb_t *hfs_mdb=(const hfs_mdb_t *)buffer;
const struct hfsp_vh *vh=(const struct hfsp_vh *)buffer; const struct hfsp_vh *vh=(const struct hfsp_vh *)buffer;
@ -99,6 +105,7 @@ int search_HFS_backup(unsigned char *buffer, disk_t *disk, partition_t *partitio
return 1; return 1;
} }
} }
#endif
return 0; return 0;
} }
@ -135,6 +142,7 @@ int search_FAT_backup(unsigned char *buffer, disk_t *disk, partition_t *partitio
int search_type_0(const unsigned char *buffer, disk_t *disk, partition_t *partition, const int verbose, const int dump_ind) int search_type_0(const unsigned char *buffer, disk_t *disk, partition_t *partition, const int verbose, const int dump_ind)
{ {
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
/* Expect a buffer filled with 8k to handle the SWAP detection */ /* Expect a buffer filled with 8k to handle the SWAP detection */
const pv_disk_t *pv=(const pv_disk_t *)buffer; const pv_disk_t *pv=(const pv_disk_t *)buffer;
const struct cramfs_super *cramfs=(const struct cramfs_super *)buffer; const struct cramfs_super *cramfs=(const struct cramfs_super *)buffer;
@ -149,6 +157,7 @@ int search_type_0(const unsigned char *buffer, disk_t *disk, partition_t *partit
const struct xfs_sb *xfs=(const struct xfs_sb *)buffer; const struct xfs_sb *xfs=(const struct xfs_sb *)buffer;
const union swap_header *swap_header=(const union swap_header *)buffer; const union swap_header *swap_header=(const union swap_header *)buffer;
const struct ReFS_boot_sector *refs_header=(const struct ReFS_boot_sector *)buffer; const struct ReFS_boot_sector *refs_header=(const struct ReFS_boot_sector *)buffer;
const nx_superblock_t *apfs=(const nx_superblock_t *)buffer;
static const uint8_t LUKS_MAGIC[LUKS_MAGIC_L] = {'L','U','K','S', 0xba, 0xbe}; static const uint8_t LUKS_MAGIC[LUKS_MAGIC_L] = {'L','U','K','S', 0xba, 0xbe};
// assert(sizeof(union swap_header)<=8*DEFAULT_SECTOR_SIZE); // assert(sizeof(union swap_header)<=8*DEFAULT_SECTOR_SIZE);
// assert(sizeof(pv_disk_t)<=8*DEFAULT_SECTOR_SIZE); // assert(sizeof(pv_disk_t)<=8*DEFAULT_SECTOR_SIZE);
@ -162,6 +171,9 @@ int search_type_0(const unsigned char *buffer, disk_t *disk, partition_t *partit
log_trace("search_type_0 lba=%lu\n", log_trace("search_type_0 lba=%lu\n",
(long unsigned)(partition->part_offset/disk->sector_size)); (long unsigned)(partition->part_offset/disk->sector_size));
} }
if(le32(apfs->nx_magic)== 0x4253584e &&
recover_APFS(disk, apfs, partition, verbose, dump_ind)==0)
return 1;
if((memcmp(swap_header->magic.magic, "SWAP", 4)==0 || if((memcmp(swap_header->magic.magic, "SWAP", 4)==0 ||
memcmp(swap_header->magic8k.magic, "SWAP", 4)==0) && memcmp(swap_header->magic8k.magic, "SWAP", 4)==0) &&
recover_Linux_SWAP(swap_header, partition)==0) recover_Linux_SWAP(swap_header, partition)==0)
@ -212,16 +224,20 @@ int search_type_0(const unsigned char *buffer, disk_t *disk, partition_t *partit
if(cramfs->magic==le32(CRAMFS_MAGIC) && if(cramfs->magic==le32(CRAMFS_MAGIC) &&
recover_cramfs(disk, cramfs, partition, verbose, dump_ind)==0) recover_cramfs(disk, cramfs, partition, verbose, dump_ind)==0)
return 1; return 1;
#endif
#if !defined(SINGLE_PARTITION_TYPE) || defined(SINGLE_PARTITION_I386)
/* Try to locate logical partition that may host truecrypt encrypted filesystem */ /* Try to locate logical partition that may host truecrypt encrypted filesystem */
if(buffer[0x1fe]==0x55 && buffer[0x1ff]==0xAA && if(buffer[0x1fe]==0x55 && buffer[0x1ff]==0xAA &&
recover_i386_logical(disk, buffer, partition)==0 && recover_i386_logical(disk, buffer, partition)==0 &&
partition->upart_type==UP_UNK) partition->upart_type==UP_UNK)
return 1; return 1;
#endif
return 0; return 0;
} }
int search_type_1(const unsigned char *buffer, const disk_t *disk, partition_t *partition, const int verbose, const int dump_ind) int search_type_1(const unsigned char *buffer, const disk_t *disk, partition_t *partition, const int verbose, const int dump_ind)
{ {
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
const struct disklabel *bsd_header=(const struct disklabel *)(buffer+0x200); const struct disklabel *bsd_header=(const struct disklabel *)(buffer+0x200);
const struct disk_super_block *beos_block=(const struct disk_super_block*)(buffer+0x200); const struct disk_super_block *beos_block=(const struct disk_super_block*)(buffer+0x200);
const struct cramfs_super *cramfs=(const struct cramfs_super *)(buffer+0x200); const struct cramfs_super *cramfs=(const struct cramfs_super *)(buffer+0x200);
@ -256,11 +272,13 @@ int search_type_1(const unsigned char *buffer, const disk_t *disk, partition_t *
if(le32(sunlabel->magic_start) == SUN_LABEL_MAGIC_START && if(le32(sunlabel->magic_start) == SUN_LABEL_MAGIC_START &&
recover_sun_i386(disk, sunlabel, partition, verbose, dump_ind)==0) recover_sun_i386(disk, sunlabel, partition, verbose, dump_ind)==0)
return 1; return 1;
#endif
return 0; return 0;
} }
int search_type_2(const unsigned char *buffer, disk_t *disk, partition_t *partition, const int verbose, const int dump_ind) int search_type_2(const unsigned char *buffer, disk_t *disk, partition_t *partition, const int verbose, const int dump_ind)
{ {
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
const hfs_mdb_t *hfs_mdb=(const hfs_mdb_t *)(buffer+0x400); const hfs_mdb_t *hfs_mdb=(const hfs_mdb_t *)(buffer+0x400);
const struct hfsp_vh *vh=(const struct hfsp_vh *)(buffer+0x400); const struct hfsp_vh *vh=(const struct hfsp_vh *)(buffer+0x400);
const struct ext2_super_block *sb=(const struct ext2_super_block*)(buffer+0x400); const struct ext2_super_block *sb=(const struct ext2_super_block*)(buffer+0x400);
@ -286,6 +304,7 @@ int search_type_2(const unsigned char *buffer, disk_t *disk, partition_t *partit
if(sb_f2fs->magic == le32(F2FS_SUPER_MAGIC) && if(sb_f2fs->magic == le32(F2FS_SUPER_MAGIC) &&
recover_f2fs(disk, sb_f2fs, partition)==0) recover_f2fs(disk, sb_f2fs, partition)==0)
return 1; return 1;
#endif
return 0; return 0;
} }
@ -298,6 +317,7 @@ int search_type_8(unsigned char *buffer, disk_t *disk,partition_t *partition,con
} }
if(disk->pread(disk, buffer, 4096, partition->part_offset + 4096) != 4096) if(disk->pread(disk, buffer, 4096, partition->part_offset + 4096) != 4096)
return -1; return -1;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
{ /* MD 1.2 */ { /* MD 1.2 */
const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer; const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer;
if(le32(sb1->major_version)==1 && if(le32(sb1->major_version)==1 &&
@ -307,6 +327,7 @@ int search_type_8(unsigned char *buffer, disk_t *disk,partition_t *partition,con
return 1; return 1;
} }
} }
#endif
return 0; return 0;
} }
@ -320,6 +341,7 @@ int search_type_16(unsigned char *buffer, disk_t *disk,partition_t *partition,co
/* 8k offset */ /* 8k offset */
if(disk->pread(disk, buffer, 3 * DEFAULT_SECTOR_SIZE, partition->part_offset + 16 * 512) != 3 * DEFAULT_SECTOR_SIZE) if(disk->pread(disk, buffer, 3 * DEFAULT_SECTOR_SIZE, partition->part_offset + 16 * 512) != 3 * DEFAULT_SECTOR_SIZE)
return -1; return -1;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
{ {
const struct ufs_super_block *ufs=(const struct ufs_super_block *)buffer; const struct ufs_super_block *ufs=(const struct ufs_super_block *)buffer;
const struct vdev_boot_header *zfs=(const struct vdev_boot_header*)buffer; const struct vdev_boot_header *zfs=(const struct vdev_boot_header*)buffer;
@ -332,6 +354,7 @@ int search_type_16(unsigned char *buffer, disk_t *disk,partition_t *partition,co
recover_ZFS(disk, zfs, partition, verbose, dump_ind)==0) recover_ZFS(disk, zfs, partition, verbose, dump_ind)==0)
return 1; return 1;
} }
#endif
return 0; return 0;
} }
@ -345,6 +368,7 @@ int search_type_64(unsigned char *buffer, disk_t *disk,partition_t *partition,co
/* 32k offset */ /* 32k offset */
if(disk->pread(disk, buffer, 3 * DEFAULT_SECTOR_SIZE, partition->part_offset + 63 * 512) != 3 * DEFAULT_SECTOR_SIZE) if(disk->pread(disk, buffer, 3 * DEFAULT_SECTOR_SIZE, partition->part_offset + 63 * 512) != 3 * DEFAULT_SECTOR_SIZE)
return -1; return -1;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
{ {
const struct jfs_superblock* jfs=(const struct jfs_superblock*)(buffer+0x200); const struct jfs_superblock* jfs=(const struct jfs_superblock*)(buffer+0x200);
/* Test JFS */ /* Test JFS */
@ -352,6 +376,7 @@ int search_type_64(unsigned char *buffer, disk_t *disk,partition_t *partition,co
recover_JFS(disk, jfs, partition, verbose, dump_ind)==0) recover_JFS(disk, jfs, partition, verbose, dump_ind)==0)
return 1; return 1;
} }
#endif
return 0; return 0;
} }
@ -364,6 +389,7 @@ int search_type_128(unsigned char *buffer, disk_t *disk, partition_t *partition,
} }
if(disk->pread(disk, buffer, 11 * DEFAULT_SECTOR_SIZE, partition->part_offset + 126 * 512) != 11 * DEFAULT_SECTOR_SIZE) if(disk->pread(disk, buffer, 11 * DEFAULT_SECTOR_SIZE, partition->part_offset + 126 * 512) != 11 * DEFAULT_SECTOR_SIZE)
return -1; return -1;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
{ {
const unsigned char *buffer_1024=buffer+0x400; const unsigned char *buffer_1024=buffer+0x400;
const struct reiserfs_super_block *rfs=(const struct reiserfs_super_block *)buffer_1024; const struct reiserfs_super_block *rfs=(const struct reiserfs_super_block *)buffer_1024;
@ -389,6 +415,7 @@ int search_type_128(unsigned char *buffer, disk_t *disk, partition_t *partition,
recover_gfs2(disk, gfs2, partition, dump_ind)==0) recover_gfs2(disk, gfs2, partition, dump_ind)==0)
return 1; return 1;
} }
#endif
return 0; return 0;
} }
@ -401,17 +428,20 @@ int search_type_2048(unsigned char *buffer, disk_t *disk, partition_t *partition
} }
if(disk->pread(disk, buffer, 2*DEFAULT_SECTOR_SIZE, partition->part_offset + 2048 * 512) != 2*DEFAULT_SECTOR_SIZE) if(disk->pread(disk, buffer, 2*DEFAULT_SECTOR_SIZE, partition->part_offset + 2048 * 512) != 2*DEFAULT_SECTOR_SIZE)
return -1; return -1;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
{ {
const struct vmfs_volume *sb_vmfs=(const struct vmfs_volume *)buffer; const struct vmfs_volume *sb_vmfs=(const struct vmfs_volume *)buffer;
if(le32(sb_vmfs->magic)==0xc001d00d && if(le32(sb_vmfs->magic)==0xc001d00d &&
recover_VMFS(disk, sb_vmfs, partition, verbose, dump_ind)==0) recover_VMFS(disk, sb_vmfs, partition, verbose, dump_ind)==0)
return 1; return 1;
} }
#endif
return 0; return 0;
} }
int check_linux(disk_t *disk, partition_t *partition, const int verbose) int check_linux(disk_t *disk, partition_t *partition, const int verbose)
{ {
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
if(check_JFS(disk, partition)==0 || if(check_JFS(disk, partition)==0 ||
check_rfs(disk, partition, verbose)==0 || check_rfs(disk, partition, verbose)==0 ||
check_EXT2(disk, partition, verbose)==0 || check_EXT2(disk, partition, verbose)==0 ||
@ -423,6 +453,7 @@ int check_linux(disk_t *disk, partition_t *partition, const int verbose)
check_gfs2(disk, partition)==0 || check_gfs2(disk, partition)==0 ||
check_ZFS(disk, partition)==0) check_ZFS(disk, partition)==0)
return 0; return 0;
#endif
return 1; return 1;
} }

105
src/apfs.c Normal file
View file

@ -0,0 +1,105 @@
/*
File: apfs.c
Copyright (C) 2021 Christophe GRENIER <grenier@cgsecurity.org>
This software is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <time.h>
#include "types.h"
#include "common.h"
#include "apfs.h"
#include "fnctdsk.h"
#include "log.h"
#include "guid_cpy.h"
static void set_APFS_info(const nx_superblock_t *sb, partition_t *partition)
{
partition->upart_type=UP_APFS;
}
int check_APFS(disk_t *disk_car, partition_t *partition)
{
unsigned char *buffer=(unsigned char*)MALLOC(APFS_SUPERBLOCK_SIZE);
const nx_superblock_t* sb=(const nx_superblock_t *)buffer;
if(disk_car->pread(disk_car, buffer, APFS_SUPERBLOCK_SIZE, partition->part_offset) != APFS_SUPERBLOCK_SIZE)
{
free(buffer);
return 1;
}
if(test_APFS(sb, partition)!=0)
{
free(buffer);
return 1;
}
set_APFS_info(sb, partition);
free(buffer);
return 0;
}
int recover_APFS(const disk_t *disk, const nx_superblock_t *sb, partition_t *partition, const int verbose, const int dump_ind)
{
if(test_APFS(sb, partition)!=0)
return 1;
if(dump_ind!=0)
{
if(partition!=NULL && disk!=NULL)
log_info("\nAPFS magic value at %u/%u/%u\n",
offset2cylinder(disk,partition->part_offset),
offset2head(disk,partition->part_offset),
offset2sector(disk,partition->part_offset));
/* There is a little offset ... */
dump_log(sb,DEFAULT_SECTOR_SIZE);
}
if(partition==NULL)
return 0;
set_APFS_info(sb, partition);
partition->part_type_i386=P_LINUX;
partition->part_type_mac=PMAC_LINUX;
partition->part_type_sun=PSUN_LINUX;
partition->part_type_gpt=GPT_ENT_TYPE_MAC_APFS;
partition->part_size=le32(sb->nx_block_size) * le64(sb->nx_block_count);
guid_cpy(&partition->part_uuid, (const efi_guid_t *)&sb->nx_uuid);
if(verbose>0)
{
log_info("\n");
}
partition->sborg_offset=0;
partition->sb_size=le32(sb->nx_block_size);
partition->sb_offset=0;
if(verbose>0)
{
log_info("recover_APFS: s_blocksize=%u\n", partition->blocksize);
log_info("recover_APFS: s_blocks_count %lu\n", (long unsigned int)le64(sb->nx_block_count));
if(disk==NULL)
log_info("recover_APFS: part_size %lu\n", (long unsigned)(partition->part_size / DEFAULT_SECTOR_SIZE));
else
log_info("recover_APFS: part_size %lu\n", (long unsigned)(partition->part_size / disk->sector_size));
}
return 0;
}

48
src/apfs.h Normal file
View file

@ -0,0 +1,48 @@
/*
File: apfs.h
Copyright (C) 2021 Christophe GRENIER <grenier@cgsecurity.org>
This software is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _APFS_H
#define _APFS_H
#include "apfs_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/*@
@ requires \valid(disk_car);
@ requires valid_disk(disk_car);
@ requires \valid(partition);
@ requires \separated(disk_car, partition);
@*/
int check_APFS(disk_t *disk_car, partition_t *partition);
/*@
@ requires \valid(disk_car);
@ requires valid_disk(disk_car);
@ requires \valid_read(sb);
@ requires \valid(partition);
@ requires \separated(disk_car, partition);
@*/
int recover_APFS(const disk_t *disk_car, const nx_superblock_t *sb, partition_t *partition, const int verbose, const int dump_ind);
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif

76
src/apfs_common.c Normal file
View file

@ -0,0 +1,76 @@
/*
File: apfs_common.c
Copyright (C) 2021 Christophe GRENIER <grenier@cgsecurity.org>
This software is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "types.h"
#include "common.h"
#include "apfs_common.h"
#include "log.h"
static uint64_t fletcher64(const uint32_t *data, const size_t cnt, const uint64_t init)
{
size_t k;
uint64_t sum1 = init & 0xFFFFFFFFU;
uint64_t sum2 = (init >> 32);
for (k = 0; k < cnt; k++)
{
sum1 = (sum1 + le32(data[k]));
sum2 = (sum2 + sum1);
}
sum1 = sum1 % 0xFFFFFFFF;
sum2 = sum2 % 0xFFFFFFFF;
return (sum2 << 32) | sum1;
}
static uint64_t VerifyBlock(const void *block, const size_t size)
{
uint64_t cs;
const uint32_t *data = (const uint32_t *)block;
const size_t size4 = size / sizeof(uint32_t);
cs = fletcher64(data + 2, size4 - 2, 0);
cs = fletcher64(data, 2, cs);
return cs;
}
int test_APFS(const nx_superblock_t *sb, const partition_t *partition)
{
if(le32(sb->nx_magic)!=0x4253584e)
return 1;
if(le32(sb->nx_xp_desc_blocks) + le32(sb->nx_xp_data_blocks) > le64(sb->nx_block_count))
return 2;
if(le32(sb->nx_block_size) < NX_MINIMUM_BLOCK_SIZE ||
le32(sb->nx_block_size) > NX_MAXIMUM_BLOCK_SIZE)
return 3;
if(VerifyBlock(sb, 4096) != 0)
return 4;
return 0;
}

104
src/apfs_common.h Normal file
View file

@ -0,0 +1,104 @@
#ifndef _APFS_COMMON_H
#define _APFS_COMMON_H
#ifdef __cplusplus
extern "C"
{
#endif
#define MAX_CKSUM_SIZE 8
#define NX_EPH_INFO_COUNT 4
#define NX_EPH_INFO_VERSION_1 1
#define NX_EPH_MIN_BLOCK_COUNT 8
#define NX_MAGIC 'BSXN'
#define NX_MAX_FILE_SYSTEM_EPH_STRUCTS 4
#define NX_MAX_FILE_SYSTEMS 100
#define NX_MAXIMUM_BLOCK_SIZE 65536
#define NX_MINIMUM_BLOCK_SIZE 4096
#define NX_TX_MIN_CHECKPOINT_COUNT 4
typedef enum {
NX_CNTR_OBJ_CKSUM_SET
= 0,
NX_CNTR_OBJ_CKSUM_FAIL = 1,
NX_NUM_COUNTERS = 32
} nx_counter_id_t;
typedef uint64_t oid_t;
typedef uint64_t xid_t;
struct obj_phys {
uint8_t o_cksum[MAX_CKSUM_SIZE];
oid_t o_oid;
xid_t o_xid;
uint32_t o_type;
uint32_t o_subtype;
} __attribute__((gcc_struct, __packed__));
typedef struct obj_phys obj_phys_t;
typedef int64_t paddr_t;
struct prange {
paddr_t pr_start_paddr;
uint64_t pr_block_count;
} __attribute__((gcc_struct, __packed__));
typedef struct prange prange_t;
typedef unsigned char apfs_uuid_t[16];
struct nx_superblock {
obj_phys_t nx_o;
uint32_t nx_magic;
uint32_t nx_block_size;
uint64_t nx_block_count;
uint64_t nx_features;
uint64_t nx_readonly_compatible_features;
uint64_t nx_incompatible_features;
apfs_uuid_t nx_uuid;
oid_t nx_next_oid;
xid_t nx_next_xid;
uint32_t nx_xp_desc_blocks;
uint32_t nx_xp_data_blocks;
paddr_t nx_xp_desc_base;
paddr_t nx_xp_data_base;
uint32_t nx_xp_desc_next;
uint32_t nx_xp_data_next;
uint32_t nx_xp_desc_index;
uint32_t nx_xp_desc_len;
uint32_t nx_xp_data_index;
uint32_t nx_xp_data_len;
oid_t nx_spaceman_oid;
oid_t nx_omap_oid;
oid_t nx_reaper_oid;
uint32_t nx_test_type;
uint32_t nx_max_file_systems;
oid_t nx_fs_oid[NX_MAX_FILE_SYSTEMS];
uint64_t nx_counters[NX_NUM_COUNTERS];
prange_t nx_blocked_out_prange;
oid_t nx_evict_mapping_tree_oid;
uint64_t nx_flags;
paddr_t nx_efi_jumpstart;
apfs_uuid_t nx_fusion_uuid;
prange_t nx_keylocker;
uint64_t nx_ephemeral_info[NX_EPH_INFO_COUNT];
oid_t nx_test_oid;
oid_t nx_fusion_mt_oid;
oid_t nx_fusion_wbc_oid;
prange_t nx_fusion_wbc;
uint64_t nx_newest_mounted_version;
prange_t nx_mkb_locker;
};
typedef struct nx_superblock nx_superblock_t;
//#define APFS_SUPERBLOCK_SIZE (sizeof(nx_superblock_t))
#define APFS_SUPERBLOCK_SIZE 4096
/*@
@ requires \valid_read(sb);
@ requires partition==\null || (\valid_read(partition) && valid_partition(partition));
@ requires \separated(sb, partition);
@ assigns \nothing;
@ */
int test_APFS(const nx_superblock_t *sb, const partition_t *partition);
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif

View file

@ -245,6 +245,7 @@ struct efi_guid_s
enum upart_type { enum upart_type {
UP_UNK=0, UP_UNK=0,
UP_APFS,
UP_BEOS, UP_BEOS,
UP_BTRFS, UP_BTRFS,
UP_CRAMFS, UP_CRAMFS,
@ -458,8 +459,9 @@ struct param_disk_struct
disk->sector_size > 0 disk->sector_size > 0
)); ));
*/ */
/*@ predicate valid_list_disk(list_disk_t* root) = ((root == \null) || (\valid_read(root))); */
/*@ predicate valid_list_disk{L}(list_disk_t* root) = /* TODO predicate valid_list_disk{L}(list_disk_t* root) =
\forall list_disk_t *node; \valid(node) && ld_reachable(root,node) ==> \valid(node->disk); \forall list_disk_t *node; \valid(node) && ld_reachable(root,node) ==> \valid(node->disk);
*/ */

View file

@ -20,7 +20,7 @@
*/ */
#if !defined(SINGLE_PARTITION_TYPE) || defined(SINGLE_PARTITION_GPT)
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif #endif
@ -56,6 +56,7 @@
#include "chgtype.h" #include "chgtype.h"
#include "partgpt.h" #include "partgpt.h"
#include "savehdr.h" #include "savehdr.h"
#include "apfs.h"
#include "bfs.h" #include "bfs.h"
#include "exfat.h" #include "exfat.h"
#include "fat.h" #include "fat.h"
@ -80,6 +81,7 @@ static int check_part_gpt(disk_t *disk, const int verbose, partition_t *partitio
/*@ /*@
@ requires \valid(disk_car); @ requires \valid(disk_car);
@ ensures valid_list_part(\result);
@*/ @*/
static list_part_t *read_part_gpt(disk_t *disk_car, const int verbose, const int saveheader); static list_part_t *read_part_gpt(disk_t *disk_car, const int verbose, const int saveheader);
@ -100,7 +102,6 @@ static void set_next_status_gpt(const disk_t *disk_car, partition_t *partition);
/*@ /*@
@ requires list_part == \null || \valid_read(list_part); @ requires list_part == \null || \valid_read(list_part);
@ assigns \nothing;
@*/ @*/
static int test_structure_gpt(const list_part_t *list_part); static int test_structure_gpt(const list_part_t *list_part);
@ -201,6 +202,11 @@ arch_fnct_t arch_gpt=
.is_part_known=&is_part_known_gpt .is_part_known=&is_part_known_gpt
}; };
/*@
@ requires \valid(disk_car);
@ requires valid_disk(disk_car);
@*/
// ensures valid_list_part(\result);
static list_part_t *read_part_gpt_aux(disk_t *disk_car, const int verbose, const int saveheader, const uint64_t hdr_lba) static list_part_t *read_part_gpt_aux(disk_t *disk_car, const int verbose, const int saveheader, const uint64_t hdr_lba)
{ {
struct gpt_hdr *gpt; struct gpt_hdr *gpt;
@ -398,7 +404,10 @@ list_part_t *add_partition_gpt_cli(const disk_t *disk_car, list_part_t *list_par
new_partition=partition_new(&arch_gpt); new_partition=partition_new(&arch_gpt);
new_partition->part_offset=disk_car->sector_size; new_partition->part_offset=disk_car->sector_size;
new_partition->part_size=disk_car->disk_size-new_partition->part_offset; new_partition->part_size=disk_car->disk_size-new_partition->part_offset;
/*@ loop invariant valid_read_string(*current_cmd); */ /*@
@ loop invariant valid_list_part(list_part);
@ loop invariant valid_read_string(*current_cmd);
@ */
while(1) while(1)
{ {
skip_comma_in_command(current_cmd); skip_comma_in_command(current_cmd);
@ -434,19 +443,23 @@ list_part_t *add_partition_gpt_cli(const disk_t *disk_car, list_part_t *list_par
{ {
int insert_error=0; int insert_error=0;
list_part_t *new_list_part=insert_new_partition(list_part, new_partition, 0, &insert_error); list_part_t *new_list_part=insert_new_partition(list_part, new_partition, 0, &insert_error);
/*@ assert valid_list_part(new_list_part); */
if(insert_error>0) if(insert_error>0)
{ {
free(new_partition); free(new_partition);
/*@ assert valid_list_part(new_list_part); */
return new_list_part; return new_list_part;
} }
new_partition->status=STATUS_PRIM; new_partition->status=STATUS_PRIM;
if(test_structure_gpt(list_part)!=0) if(test_structure_gpt(list_part)!=0)
new_partition->status=STATUS_DELETED; new_partition->status=STATUS_DELETED;
/*@ assert valid_list_part(new_list_part); */
return new_list_part; return new_list_part;
} }
else else
{ {
free(new_partition); free(new_partition);
/*@ assert valid_list_part(list_part); */
return list_part; return list_part;
} }
} }
@ -549,6 +562,12 @@ static int check_part_gpt(disk_t *disk, const int verbose,partition_t *partition
if(ret!=0) if(ret!=0)
screen_buffer_add("No HFS or HFS+ structure\n"); screen_buffer_add("No HFS or HFS+ structure\n");
} }
else if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_MAC_APFS)==0)
{
ret=check_APFS(disk, partition);
if(ret!=0)
screen_buffer_add("No valid APFS structure\n");
}
else if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_BEOS_BFS)==0) else if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_BEOS_BFS)==0)
{ {
ret=check_BeFS(disk, partition); ret=check_BeFS(disk, partition);
@ -576,6 +595,7 @@ static const char *get_gpt_typename(const efi_guid_t part_type_gpt)
for(i=0; gpt_sys_types[i].name!=NULL; i++) for(i=0; gpt_sys_types[i].name!=NULL; i++)
if(guid_cmp(gpt_sys_types[i].part_type, part_type_gpt)==0) if(guid_cmp(gpt_sys_types[i].part_type, part_type_gpt)==0)
return gpt_sys_types[i].name; return gpt_sys_types[i].name;
#ifndef __FRAMAC__
log_info("%8x %04x %04x %02x %02x %02x %02x %02x %02x %02x %02x\n", log_info("%8x %04x %04x %02x %02x %02x %02x %02x %02x %02x %02x\n",
part_type_gpt.time_low, part_type_gpt.time_low,
part_type_gpt.time_mid, part_type_gpt.time_mid,
@ -588,6 +608,7 @@ static const char *get_gpt_typename(const efi_guid_t part_type_gpt)
part_type_gpt.node[3], part_type_gpt.node[3],
part_type_gpt.node[4], part_type_gpt.node[4],
part_type_gpt.node[5]); part_type_gpt.node[5]);
#endif
return NULL; return NULL;
} }
@ -595,3 +616,4 @@ static const char *get_partition_typename_gpt(const partition_t *partition)
{ {
return get_gpt_typename(partition->part_type_gpt); return get_gpt_typename(partition->part_type_gpt);
} }
#endif

View file

@ -39,6 +39,9 @@
#include "analyse.h" #include "analyse.h"
#include "lang.h" #include "lang.h"
#include "intrf.h" #include "intrf.h"
#include "fat_common.h"
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
#include "apfs.h"
#include "bfs.h" #include "bfs.h"
#include "bsd.h" #include "bsd.h"
#include "btrfs.h" #include "btrfs.h"
@ -46,7 +49,6 @@
#include "exfat.h" #include "exfat.h"
#include "ext2.h" #include "ext2.h"
#include "fat.h" #include "fat.h"
#include "fat_common.h"
#include "fatx.h" #include "fatx.h"
#include "f2fs_fs.h" #include "f2fs_fs.h"
#include "f2fs.h" #include "f2fs.h"
@ -73,6 +75,7 @@
#include "vmfs.h" #include "vmfs.h"
#include "wbfs.h" #include "wbfs.h"
#include "zfs.h" #include "zfs.h"
#endif
#include "log.h" #include "log.h"
/*@ /*@
@ -91,6 +94,8 @@ static int get_geometry_from_nonembr(const unsigned char *buffer, const int verb
/*@ /*@
@ requires \valid(disk_car); @ requires \valid(disk_car);
@ requires valid_disk(disk_car);
@ ensures valid_list_part(\result);
@*/ @*/
static list_part_t *read_part_none(disk_t *disk_car, const int verbose, const int saveheader); static list_part_t *read_part_none(disk_t *disk_car, const int verbose, const int saveheader);
@ -143,6 +148,7 @@ static unsigned int get_part_type_none(const partition_t *partition);
static const char *get_partition_typename_none(const partition_t *partition); static const char *get_partition_typename_none(const partition_t *partition);
static const struct systypes none_sys_types[] = { static const struct systypes none_sys_types[] = {
{UP_APFS, "APFS"},
{UP_BEOS, "BeFS"}, {UP_BEOS, "BeFS"},
{UP_BTRFS, "btrfs"}, {UP_BTRFS, "btrfs"},
{UP_CRAMFS, "CramFS"}, {UP_CRAMFS, "CramFS"},
@ -257,6 +263,7 @@ static list_part_t *read_part_none(disk_t *disk, const int verbose, const int sa
partition=partition_new(&arch_none); partition=partition_new(&arch_none);
buffer_disk=(unsigned char *)MALLOC(16*DEFAULT_SECTOR_SIZE); buffer_disk=(unsigned char *)MALLOC(16*DEFAULT_SECTOR_SIZE);
partition->part_size=disk->disk_size; partition->part_size=disk->disk_size;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
if(recover_MD_from_partition(disk, partition, verbose)==0) if(recover_MD_from_partition(disk, partition, verbose)==0)
res=1; res=1;
else else
@ -341,6 +348,7 @@ static list_part_t *read_part_none(disk_t *disk, const int verbose, const int sa
} }
} }
} }
#endif
free(buffer_disk); free(buffer_disk);
if(res<=0) if(res<=0)
partition_reset(partition,&arch_none); partition_reset(partition,&arch_none);
@ -350,7 +358,9 @@ static list_part_t *read_part_none(disk_t *disk, const int verbose, const int sa
partition->status=STATUS_PRIM; partition->status=STATUS_PRIM;
screen_buffer_reset(); screen_buffer_reset();
check_part_none(disk, verbose,partition,saveheader); check_part_none(disk, verbose,partition,saveheader);
#ifndef __FRAMAC__
aff_part_buffer(AFF_PART_ORDER|AFF_PART_STATUS,disk, partition); aff_part_buffer(AFF_PART_ORDER|AFF_PART_STATUS,disk, partition);
#endif
list_part=insert_new_partition(NULL, partition, 0, &insert_error); list_part=insert_new_partition(NULL, partition, 0, &insert_error);
if(insert_error>0) if(insert_error>0)
free(partition); free(partition);
@ -395,8 +405,12 @@ static void init_structure_none(const disk_t *disk_car,list_part_t *list_part, c
static int check_part_none(disk_t *disk_car,const int verbose,partition_t *partition, const int saveheader) static int check_part_none(disk_t *disk_car,const int verbose,partition_t *partition, const int saveheader)
{ {
int ret=0; int ret=0;
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
switch(partition->upart_type) switch(partition->upart_type)
{ {
case UP_APFS:
ret=check_APFS(disk_car, partition);
break;
case UP_BEOS: case UP_BEOS:
ret=check_BeFS(disk_car,partition); ret=check_BeFS(disk_car,partition);
break; break;
@ -525,6 +539,7 @@ static int check_part_none(disk_t *disk_car,const int verbose,partition_t *parti
case UP_UNK: case UP_UNK:
break; break;
} }
#endif
return ret; return ret;
} }