Skip to content

Instantly share code, notes, and snippets.

@dmazin
Created July 19, 2023 13:25
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dmazin/ed608be38e4b0ec7ed2db65a74bc123a to your computer and use it in GitHub Desktop.
Save dmazin/ed608be38e4b0ec7ed2db65a74bc123a to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <ext2fs/ext2fs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#define EXT4_N_BLOCKS 15
#define BUF_LEN 4096
typedef uint16_t __le16;
typedef uint32_t __le32;
typedef uint16_t __u16;
typedef uint32_t __u32;
// copied from https://github.com/torvalds/linux/blob/fdf0eaf11452d72945af31804e2a1048ee1b574c/fs/ext4/ext4.h#L769
struct ext4_inode
{
__le16 i_mode;
__le16 i_uid;
__le32 i_size_lo;
__le32 i_atime;
__le32 i_ctime;
__le32 i_mtime;
__le32 i_dtime;
__le16 i_gid;
__le16 i_links_count;
__le32 i_blocks_lo;
__le32 i_flags;
union
{
struct
{
__le32 l_i_version;
} linux1;
struct
{
__u32 h_i_translator;
} hurd1;
struct
{
__u32 m_i_reserved1;
} masix1;
} osd1;
__le32 i_block[EXT4_N_BLOCKS];
__le32 i_generation;
__le32 i_file_acl_lo;
__le32 i_size_high;
__le32 i_obso_faddr;
union
{
struct
{
__le16 l_i_blocks_high;
__le16 l_i_file_acl_high;
__le16 l_i_uid_high;
__le16 l_i_gid_high;
__le16 l_i_checksum_lo;
__le16 l_i_reserved;
} linux2;
struct
{
__le16 h_i_reserved1;
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct
{
__le16 h_i_reserved1;
__le16 m_i_file_acl_high;
__u32 m_i_reserved2[2];
} masix2;
} osd2;
__le16 i_extra_isize;
__le16 i_checksum_hi;
__le32 i_ctime_extra;
__le32 i_mtime_extra;
__le32 i_atime_extra;
__le32 i_crtime;
__le32 i_crtime_extra;
__le32 i_version_hi;
__le32 i_projid;
} __attribute__((__packed__));
/*
Scans through the actual inode table of the filesystem, seeing if any of their
checksums match the checksum of the candidate inode.
Returns 0 if no inodes match.
Returns the inode number if an inode matches.
*/
int get_inode_num(struct ext4_inode *inode, ext2_filsys fs)
{
ext2_ino_t ino;
struct ext2_inode ext2_inode;
for (ino = 1; ino <= fs->super->s_inodes_count; ++ino)
{
if (ext2fs_read_inode(fs, ino, &ext2_inode))
{
fprintf(stderr, "Error reading inode\n");
return 0;
}
if (ext2_inode.osd2.linux2.l_i_checksum_lo == inode->osd2.linux2.l_i_checksum_lo)
{
return ino;
}
}
return 0;
}
void print_inode_info(struct ext4_inode *inode, int ino)
{
printf("Inode: %d Mode: %04o\n", ino, inode->i_mode & 0xfff);
printf("User: %u Group: %u Size: %u\n", inode->i_uid, inode->i_gid, inode->i_size_lo);
printf("Links: %u Blockcount: %u\n", inode->i_links_count, inode->i_blocks_lo);
printf("Inode checksum: 0x0%x\n", inode->osd2.linux2.l_i_checksum_lo + (inode->i_checksum_hi << 16));
}
int main(int argc, char **argv)
{
if (argc < 3)
{
fprintf(stderr, "Usage: %s <disk file> <offset>\n", argv[0]);
return 1;
}
ext2_filsys fs;
if (ext2fs_open(argv[1], EXT2_FLAG_RW, 0, 0, unix_io_manager, &fs))
{
fprintf(stderr, "Error opening filesystem\n");
return 1;
}
int fd = open(argv[1], O_RDONLY);
if (fd == -1)
{
perror("Error opening disk file");
ext2fs_close(fs);
return 1;
}
off_t offset = strtoull(argv[2], NULL, 0);
if (lseek(fd, offset, SEEK_SET) == -1)
{
perror("Error seeking to offset");
ext2fs_close(fs);
close(fd);
return 1;
}
struct ext4_inode candidate_inode;
ssize_t read_size = read(fd, &candidate_inode, sizeof(struct ext4_inode));
if (read_size != sizeof(struct ext4_inode))
{
perror("Error reading inode from disk file");
ext2fs_close(fs);
close(fd);
return 1;
}
int ino = get_inode_num(&candidate_inode, fs);
if (ino)
{
print_inode_info(&candidate_inode, ino);
}
else
{
printf("No valid inode found at offset: %li\n", offset);
}
close(fd);
ext2fs_close(fs);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment