Skip to content

Instantly share code, notes, and snippets.

@countingpine
Last active December 19, 2021 18:06
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save countingpine/0fd04b99058ebc910fefc910606ae6d0 to your computer and use it in GitHub Desktop.
Save countingpine/0fd04b99058ebc910fefc910606ae6d0 to your computer and use it in GitHub Desktop.
ext2scan: scan for ext2/ext3/ext4 partitions
/* ext2scan:
* Scans an input stream, sector by sector, for something that looks like an ext{2,3,4} partition.
* It does this by looking for a magic WORD, 0xEF53, at a known offset into the sector.
* For random data, this will occur by chance around once per 32MB of data, so we also
* check whether the first two sectors are all zeros, which is commonly true for ext partitions.
*
* Compile with:
* gcc ./ext2scan.c -o ./ext2scan
*
* Example usage:
* dd if=/dev/sda [skip=$START_SECTOR] | ./ext2scan [$START_SECTOR]
*
* Or for more speed, use a larger block size:
* dd if=/dev/sda bs=1M [iflag=skip_bytes skip=$(($START_SECTOR*512))] | ./ext2scan [$START_SECTOR]
*
* References:
* - http://unix.stackexchange.com/questions/103919/how-do-i-find-the-offset-of-an-ext4-filesystem
* - http://uranus.chrysocome.net/explore2fs/es2fs.htm
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int arg_c, char **arg_v) {
unsigned char const MAGIC[2] = {0x53, 0xef};
unsigned char const ZEROS[512] = {0};
char buf[4][512];
int empty1, empty2;
long long int sector = 0;
long long int offset = 0;
if (arg_c == 2) sscanf(arg_v[1], "%lld", &offset);
while (read(STDIN_FILENO, buf[sector&3], 512) > 0) {
if (!memcmp(buf[sector&3] + 0x38, MAGIC, 2)) {
printf("Found a possible ext partition at sector %lld", offset+sector-2);
empty1 = !memcmp(buf[(sector-2)&3], ZEROS, 512);
empty2 = !memcmp(buf[(sector-1)&3], ZEROS, 512);
if (empty1 && empty2) printf(" (first two sectors are empty :)\n");
else if (empty1) printf(" (first sector only is empty)\n");
else if (empty2) printf(" (second sector only is empty)\n");
else printf(" (first two sectors are non-empty)\n");
}
sector++;
}
}
@pjones
Copy link

pjones commented Nov 17, 2018

@countingpine Thank you so much. This saved me a ton of time tonight!

@futpib
Copy link

futpib commented Dec 19, 2019

This helped me so much! I had an MBR partition table and a couple of NTFS partitions created over my originally GPT + ext4 hard drive, so I had to find out the original first sector, and no valid superblock could be found by fsck.ext when I tried probable values (like the default suggested by gdisk, 1024, 2048, etc.).

Running this gave me a lot of potential backup superblocks to search through, so I had to read up on ext4 superblocks to narrow this set down.

I ended up modifying this to check that superblock creator OS is Linux. This gave me, in just a few seconds of scanning, a bunch of backup superblocks backups exactly (no false positives whatsoever). I derived the proper first sector of the file system from that, created a partition starting at this sector using gdisk, got a proper partition size from dumpe2fs -o superblock=XXX, recreated the partition with gdisk using the discovered partition length, and could successfully run fsck.ext4 on it afterwards. My data is back! (Well, most of it).

Here is the forked version I used https://gist.github.com/futpib/cfb04a9843b8f176d5c2ed3364fc8527. Hope it helps someone! Note that it prints superblock backup offsets in 4096 byte sectors, not in 512 byte sectors like the original.

@jsarenik
Copy link

@futpib What does "gdisked it" mean? (more comments at your modified gist, thanks!)

@futpib
Copy link

futpib commented Dec 19, 2021

@jsarenik I edited my comment. Hope it helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment