diff --git a/README.md b/README.md index 119b140..2bcea97 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,14 @@ Hex patterns, include patterns and magic files for the use with the ImHex Hex Ed | ZIP | `application/zip` | `patterns/zip.hexpat` | End of Central Directory Header, Central Directory File Headers | | PCAP | `application/vnd.tcpdump.pcap` | `patterns/pcap.hexpat` | pcap header and packets | | SPIRV | | `patterns/spirv.hexpat` | SPIR-V header and instructions | +| AFE2 | | `patterns/afe2.hexpat` | Nintendo Switch Atmosphère CFW Fatal Error log | +| AR | `application/x-archive` | `patterns/ar.hexpat` | Static library archive files | ### Pattern Libraries | Name | Path | Description | |------|------|-------------| -| cstdint | `includes/cstdint.hexpat` | C standard style fixed width integer types | +| libstd | `includes/libstd/*` | Pattern Language Standard Libaray | ### Magic files diff --git a/includes/libstd/bit.pat b/includes/libstd/bit.pat new file mode 100644 index 0000000..dda1bd8 --- /dev/null +++ b/includes/libstd/bit.pat @@ -0,0 +1,35 @@ +namespace std::bit { + + fn popcount(u128 x) { + x = (x & (std::limits::u128_max() / 3)) + ((x >> 1) & (std::limits::u128_max() / 3)); + x = (x & (std::limits::u128_max() / 5)) + ((x >> 2) & (std::limits::u128_max() / 5)); + x = (x & (std::limits::u128_max() / 17)) + ((x >> 4) & (std::limits::u128_max() / 17)); + + return x % 0xFF; + }; + + fn has_single_bit(u128 x) { + return x != 0 && (x & (x - 1)) == 0; + }; + + fn bit_ceil(u128 x) { + if (x == 0) return 0; + + u8 i; + while ((1 << i) < x) + i = i + 1; + + return 1 << i; + }; + + fn bit_floor(u128 x) { + if (x == 0) return 0; + + u8 i; + while ((x >> i) > 0) + i = i + 1; + + return 1 << (i - 1); + }; + +} \ No newline at end of file diff --git a/includes/libstd/ctype.pat b/includes/libstd/ctype.pat new file mode 100644 index 0000000..20659bb --- /dev/null +++ b/includes/libstd/ctype.pat @@ -0,0 +1,51 @@ +namespace std::ctype { + + fn isdigit(char c) { + return c >= '0' && c <= '9'; + }; + + fn isxdigit(char c) { + return std::ctype::isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + }; + + fn isupper(char c) { + return c >= 'A' && c <= 'Z'; + }; + + fn islower(char c) { + return c >= 'a' && c <= 'z'; + }; + + fn isalpha(char c) { + return std::ctype::isupper(c) || std::ctype::islower(c); + }; + + fn isalnum(char c) { + return std::ctype::isalpha(c) || std::ctype::isdigit(c); + }; + + fn isspace(char c) { + return (c >= 0x09 && c <= 0x0D) || c == 0x20; + }; + + fn isblank(char c) { + return c == 0x09 || c == ' '; + }; + + fn isprint(char c) { + return c >= '0' && c <= '~'; + }; + + fn iscntrl(char c) { + return !std::ctype::isprint(c); + }; + + fn isgraph(char c) { + return std::ctype::isprint(c) && !std::ctype::isspace(c); + }; + + fn isgraph(char c) { + return std::ctype::isgraph(c) && !std::ctype::isalnum(c); + }; + +} \ No newline at end of file diff --git a/includes/libstd/fxpt.pat b/includes/libstd/fxpt.pat new file mode 100644 index 0000000..10ed900 --- /dev/null +++ b/includes/libstd/fxpt.pat @@ -0,0 +1,29 @@ +namespace std::fxpt { + + using fixed = s128; + + fn to_float(fixed fxt, u32 precision) { + return double(fxt) / double((1 << precision)); + }; + + fn to_fixed(double flt, u32 precision) { + return fixed((flt * (1 << precision))); + }; + + fn add(fixed a, fixed b, u32 precision) { + return a + b; + }; + + fn subtract(fixed a, fixed b, u32 precision) { + return a - b; + }; + + fn multiply(fixed a, fixed b, u32 precision) { + return (a * b) / (1 << precision); + }; + + fn divide(fixed a, fixed b, u32 precision) { + return (a << precision) / b; + }; + +} \ No newline at end of file diff --git a/includes/libstd/limits.pat b/includes/libstd/limits.pat new file mode 100644 index 0000000..693ddcf --- /dev/null +++ b/includes/libstd/limits.pat @@ -0,0 +1,83 @@ +namespace std::limits { + + fn u8_min() { + return u8(0); + }; + + fn u8_max() { + return u8(-1); + }; + + fn s8_min() { + return -s8((std::limits::u8_max() / 2)) - 1; + }; + + fn s8_max() { + return s8((u8_max() / 2)); + }; + + fn u16_min() { + return u16(0); + }; + + fn u16_max() { + return u16(-1); + }; + + fn s16_min() { + return -s16((std::limits::u16_max() / 2)) - 1; + }; + + fn s16_max() { + return s16((u16_max() / 2)); + }; + + fn u32_min() { + return u32(0); + }; + + fn u32_max() { + return u32(-1); + }; + + fn s32_min() { + return -s32((std::limits::u32_max() / 2)) - 1; + }; + + fn s32_max() { + return s32((u32_max() / 2)); + }; + + fn u64_min() { + return u64(0); + }; + + fn u64_max() { + return u64(-1); + }; + + fn s64_min() { + return -s64((std::limits::u64_max() / 2)) - 1; + }; + + fn s64_max() { + return s64((u64_max() / 2)); + }; + + fn u128_min() { + return u128(0); + }; + + fn u128_max() { + return u128(-1); + }; + + fn s128_min() { + return -s128((std::limits::u128_max() / 2)) - 1; + }; + + fn s128_max() { + return s128((u128_max() / 2)); + }; + +} \ No newline at end of file diff --git a/includes/libstd/math.pat b/includes/libstd/math.pat new file mode 100644 index 0000000..51cd8e4 --- /dev/null +++ b/includes/libstd/math.pat @@ -0,0 +1,20 @@ +namespace std::math { + + fn min(auto a, auto b) { + return a < b ? a : b; + }; + + fn max(auto a, auto b) { + return a > b ? a : b; + }; + + fn clamp(auto x, auto min, auto max) { + return (x < min) ? min : ((x > max) ? max : x); + }; + + fn abs(auto x) { + if (x < 0) return -x; + else return x; + }; + +} \ No newline at end of file diff --git a/includes/libstd/rustint.pat b/includes/libstd/rustint.pat new file mode 100644 index 0000000..efae61b --- /dev/null +++ b/includes/libstd/rustint.pat @@ -0,0 +1,14 @@ +namespace std { + + using i8 = s8; + using i16 = s16; + using i32 = s32; + using i64 = s64; + using i128 = s128; + + using f32 = float; + using f64 = double; + + using usize = u64; + using isize = s64; +} \ No newline at end of file diff --git a/includes/libstd/stdint.pat b/includes/libstd/stdint.pat new file mode 100644 index 0000000..8ed34d1 --- /dev/null +++ b/includes/libstd/stdint.pat @@ -0,0 +1,21 @@ +namespace std { + + using uint8_t = u8; + using uint16_t = u16; + using uint32_t = u32; + using uint64_t = u64; + using uint128_t = u128; + + using int8_t = s8; + using int16_t = s16; + using int32_t = s32; + using int64_t = s64; + using int128_t = s128; + + using float32_t = float; + using float64_t = double; + + using size_t = u64; + using ssize_t = s64; + +} \ No newline at end of file diff --git a/includes/libstd/string.pat b/includes/libstd/string.pat new file mode 100644 index 0000000..82c7380 --- /dev/null +++ b/includes/libstd/string.pat @@ -0,0 +1,30 @@ +namespace std::string { + + fn to_string(auto x) { + return std::format("{}", x); + }; + + fn starts_with(str string, str part) { + return std::string::substr(string, 0, std::string::length(part)) == part; + }; + + fn ends_with(str string, str part) { + return std::string::substr(string, std::string::length(string) - std::string::length(part), std::string::length(part)) == part; + }; + + fn contains(str a, str b) { + s32 a_len, b_len; + a_len = std::string::length(a); + b_len = std::string::length(b); + + s32 i; + while (i < a_len - b_len) { + if (std::string::substr(a, i, b_len) == b) + return true; + i = i + 1; + } + + return false; + }; + +} \ No newline at end of file diff --git a/patterns/afe2.hexpat b/patterns/afe2.hexpat new file mode 100644 index 0000000..80b882c --- /dev/null +++ b/patterns/afe2.hexpat @@ -0,0 +1,55 @@ +#pragma endian little + +#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC "AFE2" +#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 "AFE1" +#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 "AFE0" + +#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20 +#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100 +#define AMS_FATAL_ERROR_TLS_SIZE 0x100 + +struct gprs_named { + u64 gprs[29]; + u64 fp; + u64 lr; + u64 sp; +}; + +union gprs { + u64 gprs[32]; + gprs_named named; +}; + +struct atmosphere_fatal_error_ctx { + char magic[4]; + u32 error_desc; + u64 program_id; + gprs gprs; + u64 pc; + u64 module_base; + u32 pstate; + u32 afsr0; + u32 afsr1; + u32 esr; + u64 far; + u64 report_identifier; /* Normally just system tick */ + u64 stack_trace_size; + u64 stack_dump_size; + u64 stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE]; + u8 stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP]; + u8 tls[AMS_FATAL_ERROR_TLS_SIZE]; +}; + +atmosphere_fatal_error_ctx ctx @ 0x00; + +std::assert(ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC || + ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 || + ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0, + "File is not a valid Atmosphere fatal error binary!"); + +std::assert_warn(ctx.magic == ATMOSPHERE_REBOOT_TO_FATAL_MAGIC, + "Atmosphere fatal error binary is for an older version!"); + + +std::print("Error Description: 0x{:04X}", ctx.error_desc); +std::print("Program ID: {:016X}", ctx.program_id); \ No newline at end of file diff --git a/patterns/ar.hexpat b/patterns/ar.hexpat new file mode 100644 index 0000000..840b59c --- /dev/null +++ b/patterns/ar.hexpat @@ -0,0 +1,21 @@ +#pragma MIME application/x-archive + +struct ARFile { + char file_name[16]; + char modification_timestamp[12]; + char owner_id[6]; + char group_id[6]; + char file_mode[8]; + char file_size[10]; + u16 end_marker; + + if (end_marker == 0x0A60) { + u8 data[std::string::parse_int(this.file_size, 10)]; + padding[sizeof(data) & 1]; + } +} [[static]]; + +char signature[8] @ 0x00; +std::assert(signature == "!\n", "File is not a valid archive!"); + +ARFile files[while($ < std::mem::size())] @ $; \ No newline at end of file diff --git a/patterns/elf.hexpat b/patterns/elf.hexpat index 223d177..0c09f13 100644 --- a/patterns/elf.hexpat +++ b/patterns/elf.hexpat @@ -1,52 +1,261 @@ #pragma MIME application/x-executable #pragma MIME application/x-sharedlib -#define EI_NIDENT 16 +namespace elf { -using Elf32_Addr = u32; -using Elf32_Half = u16; -using Elf32_Off = u32; -using Elf32_Sword = s32; -using Elf32_Word = u32; + enum Class : u8 { + _32Bit = 1, + _64Bit = 2 + }; + + enum Endian : u8 { + Little = 1, + Big = 2 + }; + + enum OSABI : u8 { + SystemV = 0x00, + HP_UX = 0x01, + NetBSD = 0x02, + Linux = 0x03, + GNU_Hurd = 0x04, + Solaris = 0x06, + AIX = 0x07, + IRIX = 0x08, + FreeBSD = 0x09, + Tru64 = 0x0A, + NovellModesto = 0x0B, + OpenBSD = 0x0C, + OpenVMS = 0x0D, + NonStop_Kernel = 0x0E, + AROS = 0x0F, + FenixOS = 0x10, + CloudABI = 0x11, + Stratus_Technologies_OpenVOS = 0x12 + }; + + enum Type : u16 { + NONE = 0x00, + REL = 0x01, + EXEC = 0x02, + DYN = 0x03, + CORE = 0x04, + LOOS = 0xFE00, + HIOS = 0xFEFF, + LOPROC = 0xFF00, + HIPROC = 0xFFFF + }; + + enum Machine : u16 { + Undefined = 0x00, + ATNT_WE_32100 = 0x01, + SPARC = 0x02, + x86 = 0x03, + Motorola_68000 = 0x04, + Motorola_88000 = 0x05, + IntelMCU = 0x06, + Intel_80860 = 0x07, + MIPS = 0x08, + IBM_System370 = 0x09, + MIPS_RS300_LE = 0x0A, + /* 0x0B - 0x0D: Reserved */ + HP_PA_RISC = 0x0E, + /* 0x0F: Reserved */ + Intel_80960 = 0x13, + PowerPC = 0x14, + PowerPC_64 = 0x15, + S390 = 0x16, + IBM_SPU_SPC = 0x17, + /* 0x18 - 0x23: Reserved */ + NEC_V800 = 0x24, + Fujitsu_FR20 = 0x25, + TRW_RH_32 = 0x26, + Motorola_RCE = 0x27, + ARM32 = 0x28, + Digital_Alpha = 0x29, + SuperH = 0x2A, + SPARCv9 = 0x2B, + Siemens_TriCore = 0x2C, + Argonaut_RISC_Core = 0x2D, + Hitachi_H8_300 = 0x2E, + Hitachi_H8_300H = 0x2F, + Hitachi_H8S = 0x30, + Hitachi_H8_500 = 0x31, + IA_64 = 0x32, + Standford_MIPS_X = 0x33, + Motorola_ColdFire = 0x34, + Motorola_M68HC12 = 0x35, + Fujitsu_MMA = 0x36, + Siemens_PCP = 0x37, + Sony_nCPU = 0x38, + Denso_NDR1 = 0x39, + Motorola_StarCore = 0x3A, + Toyota_ME16 = 0x3B, + STMicroelectronics_ST100 = 0x3C, + Advanced_Logic_Corp_TinyJ = 0x3D, + AMD_x86_64 = 0x3E, + TMS320C6000 = 0x8C, + MCST_Elbrus_e2k = 0xAF, + ARM64 = 0xB7, + RISC_V = 0xF3, + Berkeley_Packet_Filter = 0xF7, + WDC_65C816 = 0x101 + }; + + struct Identity { + char magic[4]; + Class class; + Endian endian; + u8 version; + OSABI os_abi; + + if (os_abi == elf::OSABI::Linux) + u8 dynamic_linker_version; + else + u8 abi_version; + }; -using Elf64_Addr = u64; -using Elf64_Half = u16; -using Elf64_Off = u64; -using Elf64_Sword = s32; -using Elf64_Word = u32; + struct Header { + Identity identity; + padding[7]; + Type type; + Machine machine; + u32 version; + + if (identity.class == elf::Class::_32Bit) { + u32 entry_point; + u32 program_header_offset; + u32 section_header_offset; + } else { + u64 entry_point; + u64 program_header_offset; + u64 section_header_offset; + } + + u32 flags; + + u16 elf_header_size; + u16 program_header_entry_size; + u16 program_header_entry_count; + u16 section_header_entry_size; + u16 section_header_entry_count; + u16 section_name_entry_id; + }; -struct Elf32_Ehdr { - u8 e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; -}; +} -struct Elf64_Ehdr { - u8 e_ident[EI_NIDENT]; - Elf64_Half e_type; - Elf64_Half e_machine; - Elf64_Word e_version; - Elf64_Addr e_entry; - Elf64_Off e_phoff; - Elf64_Off e_shoff; - Elf64_Word e_flags; - Elf64_Half e_ehsize; - Elf64_Half e_phentsize; - Elf64_Half e_phnum; - Elf64_Half e_shentsize; - Elf64_Half e_shnum; - Elf64_Half e_shstrndx; -}; +namespace program { -Elf64_Ehdr header @ 0x00; + enum Type : u32 { + NULL = 0x00, + LOAD = 0x01, + DYNAMIC = 0x02, + INTERP = 0x03, + NOTE = 0x04, + SHLIB = 0x05, + PHDR = 0x06, + TLS = 0x07, + LOOS = 0x60000000, + HIOS = 0x6FFFFFFF, + LOPROC = 0x70000000, + HIPROC = 0x7FFFFFFF + }; + + struct Header { + Type type; + + if (parent.header.identity.class == elf::Class::_32Bit) { + u32 offset; + u32 virtual_address; + u32 physical_address; + u32 file_size; + u32 memory_size; + u32 flags; + u32 alignment; + } else { + u32 flags; + u64 offset; + u64 virtual_address; + u64 physical_address; + u64 file_size; + u64 memory_size; + u64 alignment; + } + } [[static]]; + +} + +namespace section { + + struct String { + char characters[]; + }; + + enum Type : u32 { + NULL = 0x00, + PROGBITS = 0x01, + SYMTAB = 0x02, + STRTAB = 0x03, + RELA = 0x04, + HASH = 0x05, + DYNAMIC = 0x06, + NOTE = 0x07, + NOBITS = 0x08, + REL = 0x09, + SHLIB = 0x0A, + DYNSYM = 0x0B, + INIT_ARRAY = 0x0E, + FINI_ARRAY = 0x0F, + PREINIT_ARRAY = 0x10, + GROUP = 0x11, + SYMTAB_SHNDX = 0x12, + NUM = 0x13, + LOOS = 0x60000000 + }; + + bitfield Flags { + WRITE : 1; + ALLOC : 1; + EXECINSTR : 1; + pad1 : 1; + MERGE : 1; + STRINGS : 1; + INFO_LINK : 1; + LINK_ORDER : 1; + OS_NONCONFORMING : 1; + GROUP : 1; + TLS : 1; + }; + + struct Header { + u32 name_offset; + section::Type type; + Flags flags; + padding[2]; + + if (parent.header.identity.class == elf::Class::_32Bit) { + u32 address; + u32 offset; + u32 size; + u32 link; + u32 info; + u32 address_alignment; + u32 entry_size; + } else { + padding[4]; + u64 address; + u64 offset; + u64 size; + u32 link; + u32 info; + u64 address_alignment; + u64 entry_size; + } + } [[static]]; + +} + + +elf::Header header @ 0x00; +program::Header program_headers[header.program_header_entry_count] @ header.program_header_offset; +section::Header section_headers[header.section_header_entry_count] @ header.section_header_offset; \ No newline at end of file