TestDisk: detect APFS partition
This commit is contained in:
parent
8f11e3a897
commit
cd7b7032f6
9 changed files with 416 additions and 13 deletions
|
@ -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 \
|
||||
-warn-left-shift-negative \
|
||||
-warn-right-shift-negative \
|
||||
|
@ -36,13 +36,13 @@ endif
|
|||
bin_PROGRAMS = testdisk photorec fidentify $(QPHOTOREC)
|
||||
EXTRA_PROGRAMS = photorecf fuzzerfidentify
|
||||
|
||||
smallbase_C = common.c crc.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_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 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_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_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_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 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_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
|
||||
|
|
|
@ -30,13 +30,15 @@
|
|||
#include "types.h"
|
||||
#include "common.h"
|
||||
#include "analyse.h"
|
||||
#include "fat.h"
|
||||
#include "exfat.h"
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
#include "apfs.h"
|
||||
#include "bfs.h"
|
||||
#include "bsd.h"
|
||||
#include "btrfs.h"
|
||||
#include "cramfs.h"
|
||||
#include "exfat.h"
|
||||
#include "ext2.h"
|
||||
#include "fat.h"
|
||||
#include "fatx.h"
|
||||
#include "f2fs_fs.h"
|
||||
#include "f2fs.h"
|
||||
|
@ -61,8 +63,9 @@
|
|||
#include "wbfs.h"
|
||||
#include "xfs.h"
|
||||
#include "zfs.h"
|
||||
#include "log.h"
|
||||
#endif
|
||||
#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)
|
||||
{
|
||||
|
@ -70,10 +73,12 @@ int search_NTFS_backup(unsigned char *buffer, disk_t *disk, partition_t *partiti
|
|||
return -1;
|
||||
{
|
||||
const struct ntfs_boot_sector *ntfs_header=(const struct ntfs_boot_sector*)buffer;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
/* NTFS recovery using backup sector */
|
||||
if(le16(ntfs_header->marker)==0xAA55 &&
|
||||
recover_NTFS(disk, ntfs_header, partition, verbose, dump_ind, 1)==0)
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
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)
|
||||
return -1;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
{
|
||||
const hfs_mdb_t *hfs_mdb=(const hfs_mdb_t *)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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
/* Expect a buffer filled with 8k to handle the SWAP detection */
|
||||
const pv_disk_t *pv=(const pv_disk_t *)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 union swap_header *swap_header=(const union swap_header *)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};
|
||||
// assert(sizeof(union swap_header)<=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",
|
||||
(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 ||
|
||||
memcmp(swap_header->magic8k.magic, "SWAP", 4)==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) &&
|
||||
recover_cramfs(disk, cramfs, partition, verbose, dump_ind)==0)
|
||||
return 1;
|
||||
#endif
|
||||
#if !defined(SINGLE_PARTITION_TYPE) || defined(SINGLE_PARTITION_I386)
|
||||
/* Try to locate logical partition that may host truecrypt encrypted filesystem */
|
||||
if(buffer[0x1fe]==0x55 && buffer[0x1ff]==0xAA &&
|
||||
recover_i386_logical(disk, buffer, partition)==0 &&
|
||||
partition->upart_type==UP_UNK)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 disk_super_block *beos_block=(const struct disk_super_block*)(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 &&
|
||||
recover_sun_i386(disk, sunlabel, partition, verbose, dump_ind)==0)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 struct hfsp_vh *vh=(const struct hfsp_vh *)(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) &&
|
||||
recover_f2fs(disk, sb_f2fs, partition)==0)
|
||||
return 1;
|
||||
#endif
|
||||
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)
|
||||
return -1;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
{ /* MD 1.2 */
|
||||
const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer;
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -320,6 +341,7 @@ int search_type_16(unsigned char *buffer, disk_t *disk,partition_t *partition,co
|
|||
/* 8k offset */
|
||||
if(disk->pread(disk, buffer, 3 * DEFAULT_SECTOR_SIZE, partition->part_offset + 16 * 512) != 3 * DEFAULT_SECTOR_SIZE)
|
||||
return -1;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
{
|
||||
const struct ufs_super_block *ufs=(const struct ufs_super_block *)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)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -345,6 +368,7 @@ int search_type_64(unsigned char *buffer, disk_t *disk,partition_t *partition,co
|
|||
/* 32k offset */
|
||||
if(disk->pread(disk, buffer, 3 * DEFAULT_SECTOR_SIZE, partition->part_offset + 63 * 512) != 3 * DEFAULT_SECTOR_SIZE)
|
||||
return -1;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
{
|
||||
const struct jfs_superblock* jfs=(const struct jfs_superblock*)(buffer+0x200);
|
||||
/* 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)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
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)
|
||||
return -1;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
{
|
||||
const unsigned char *buffer_1024=buffer+0x400;
|
||||
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)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
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)
|
||||
return -1;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
{
|
||||
const struct vmfs_volume *sb_vmfs=(const struct vmfs_volume *)buffer;
|
||||
if(le32(sb_vmfs->magic)==0xc001d00d &&
|
||||
recover_VMFS(disk, sb_vmfs, partition, verbose, dump_ind)==0)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_linux(disk_t *disk, partition_t *partition, const int verbose)
|
||||
{
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
if(check_JFS(disk, partition)==0 ||
|
||||
check_rfs(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_ZFS(disk, partition)==0)
|
||||
return 0;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
105
src/apfs.c
Normal file
105
src/apfs.c
Normal 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
48
src/apfs.h
Normal 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
76
src/apfs_common.c
Normal 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
104
src/apfs_common.h
Normal 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
|
|
@ -245,6 +245,7 @@ struct efi_guid_s
|
|||
|
||||
enum upart_type {
|
||||
UP_UNK=0,
|
||||
UP_APFS,
|
||||
UP_BEOS,
|
||||
UP_BTRFS,
|
||||
UP_CRAMFS,
|
||||
|
@ -458,8 +459,9 @@ struct param_disk_struct
|
|||
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);
|
||||
*/
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(SINGLE_PARTITION_TYPE) || defined(SINGLE_PARTITION_GPT)
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
@ -56,6 +56,7 @@
|
|||
#include "chgtype.h"
|
||||
#include "partgpt.h"
|
||||
#include "savehdr.h"
|
||||
#include "apfs.h"
|
||||
#include "bfs.h"
|
||||
#include "exfat.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);
|
||||
@ ensures valid_list_part(\result);
|
||||
@*/
|
||||
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);
|
||||
@ assigns \nothing;
|
||||
@*/
|
||||
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
|
||||
};
|
||||
|
||||
/*@
|
||||
@ 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)
|
||||
{
|
||||
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->part_offset=disk_car->sector_size;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
free(new_partition);
|
||||
/*@ assert valid_list_part(new_list_part); */
|
||||
return new_list_part;
|
||||
}
|
||||
new_partition->status=STATUS_PRIM;
|
||||
if(test_structure_gpt(list_part)!=0)
|
||||
new_partition->status=STATUS_DELETED;
|
||||
/*@ assert valid_list_part(new_list_part); */
|
||||
return new_list_part;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(new_partition);
|
||||
/*@ assert valid_list_part(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)
|
||||
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)
|
||||
{
|
||||
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++)
|
||||
if(guid_cmp(gpt_sys_types[i].part_type, part_type_gpt)==0)
|
||||
return gpt_sys_types[i].name;
|
||||
#ifndef __FRAMAC__
|
||||
log_info("%8x %04x %04x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
part_type_gpt.time_low,
|
||||
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[4],
|
||||
part_type_gpt.node[5]);
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include "analyse.h"
|
||||
#include "lang.h"
|
||||
#include "intrf.h"
|
||||
#include "fat_common.h"
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
#include "apfs.h"
|
||||
#include "bfs.h"
|
||||
#include "bsd.h"
|
||||
#include "btrfs.h"
|
||||
|
@ -46,7 +49,6 @@
|
|||
#include "exfat.h"
|
||||
#include "ext2.h"
|
||||
#include "fat.h"
|
||||
#include "fat_common.h"
|
||||
#include "fatx.h"
|
||||
#include "f2fs_fs.h"
|
||||
#include "f2fs.h"
|
||||
|
@ -73,6 +75,7 @@
|
|||
#include "vmfs.h"
|
||||
#include "wbfs.h"
|
||||
#include "zfs.h"
|
||||
#endif
|
||||
#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(disk_car);
|
||||
@ ensures valid_list_part(\result);
|
||||
@*/
|
||||
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 struct systypes none_sys_types[] = {
|
||||
{UP_APFS, "APFS"},
|
||||
{UP_BEOS, "BeFS"},
|
||||
{UP_BTRFS, "btrfs"},
|
||||
{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);
|
||||
buffer_disk=(unsigned char *)MALLOC(16*DEFAULT_SECTOR_SIZE);
|
||||
partition->part_size=disk->disk_size;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
if(recover_MD_from_partition(disk, partition, verbose)==0)
|
||||
res=1;
|
||||
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);
|
||||
if(res<=0)
|
||||
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;
|
||||
screen_buffer_reset();
|
||||
check_part_none(disk, verbose,partition,saveheader);
|
||||
#ifndef __FRAMAC__
|
||||
aff_part_buffer(AFF_PART_ORDER|AFF_PART_STATUS,disk, partition);
|
||||
#endif
|
||||
list_part=insert_new_partition(NULL, partition, 0, &insert_error);
|
||||
if(insert_error>0)
|
||||
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)
|
||||
{
|
||||
int ret=0;
|
||||
#if !defined(__FRAMAC__) && !defined(MAIN_photorec)
|
||||
switch(partition->upart_type)
|
||||
{
|
||||
case UP_APFS:
|
||||
ret=check_APFS(disk_car, partition);
|
||||
break;
|
||||
case UP_BEOS:
|
||||
ret=check_BeFS(disk_car,partition);
|
||||
break;
|
||||
|
@ -525,6 +539,7 @@ static int check_part_none(disk_t *disk_car,const int verbose,partition_t *parti
|
|||
case UP_UNK:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue