ImHex

From ModdingWiki
Jump to navigation Jump to search
ImHex
ImHex.png
PlatformWindows, Linux, macOS, online
Release date2020/11/12
HomepageHomepage
DownloadDownload links on the homepage
GamesNone

ImHex is a powerful hex editor with many additional tools built it.

In addition to editing binary files, it features a powerful pattern language, which allows you to mark up parts of the file, to help you visualise the structure of the file. Particularly useful when trying to understand an undocumented file format.

It also has many additional features built-in including data visualization, a graphing calculator, LLVM demangler, diffing and disassemblers for 18 different architectures.

It's still a work in progress, and has some quirks, but it is under active development. Your mileage might vary

Platform and license

It's written in c++ and is completely free and open source under the GPLv2.

Documentation

Main documentation

Pattern Language documentation

Disassembler architectures

  • ARM
  • AArch64
  • MIPS
  • Intel x86
  • PowerPC
  • SPARC
  • SystemZ
  • XCore
  • Motorola 68K
  • TMS320C64x
  • M680x
  • Ethereum Virtual Machine
  • MOS Technology 65xx
  • WebAssembly
  • Berkeley Packet Filter
  • RISC-V
  • SuperH
  • Tricore

Example pattern script

This script which can be run in ImHex, marks up the individual files in a DAT Format (Fury of the Furries) file, and decompresses each into a separate file within a virtual file system in the ImHex editor.

/*!
    Deconstruct a Fury of the Furries DAT archive

    Running this pattern will extract and decompress the archive into the Virtual Filesystem
*/

#pragma endian little
#pragma pattern_limit 2000000

import std.string;
import std.core;
import std.io;
import hex.core;
import type.base;

std::mem::Section currentSection;
s32 currentOffset = 0;

// Is the archived file compressed
enum Compressed : u8 {
    Yes,
    No
};

fn addSpace() {
  u8 space[18] @ 0 in currentSection;
  for (u8 i = 0, i < 18, i = i + 1) {
    space[i] = 0x20;
  }
};

// Copy a run length section from earlier in the compressed file
fn outputRunLengthCopy(auto length, auto offset) {
  while (length > 0) {
    std::mem::copy_section_to_section(currentSection, offset + 18, currentSection, currentOffset + 18, 1);
    offset = offset + 1;
    currentOffset = currentOffset + 1;
    length = length - 1;
  }
};

// A pair of bytes representing a run length
struct CompressionPair {
  u16 shadowPair [[hidden, no_unique_address]];
  u8 length = ((shadowPair >> 8) & 0x0f) + 3;
  u16 offset = ((shadowPair >> 12) << 8) + (shadowPair & 0xff) + 18;
  s32 fullOffset = offset + (currentOffset & 0xfffff000);
  if (fullOffset >= currentOffset) {
    fullOffset = fullOffset - 4096;
  }
  str comment = std::string::to_string(length) + " bytes @ " + type::impl::format_number(fullOffset, "0x{:02X}");
  
  u16 pair [[comment(comment)]];
  
  outputRunLengthCopy(length, fullOffset);
} [[inline]];

// A single byte literal
struct CompressionLiteral {
  u8 byte;
} [[inline]];

// Either a single byte literal, or a run-length pair
struct CompressionItem {
  u8 index = std::core::array_index();
  u8 flag = (parent.flags >> index) & 0x01;
  if (flag) {
    CompressionLiteral byte;
    std::mem::copy_value_to_section(byte, currentSection, currentOffset + 18);
    currentOffset = currentOffset + 1;
  }
  else {
    CompressionPair pair;
  }
  if ($ >= parent.parent.parent.end) {
    break;
  }
} [[inline]];

// A set up up to 8 compression items controlled by a flag bitfield
struct CompressionBlock {
  u8 flags [[color("008800")]];
  CompressionItem parts[8] [[inline]];
};

// A compressed data file
struct CompressedData {
  CompressionBlock data[while($ < parent.end)];
} [[inline]];

// A single file within the archive
struct File {
  char name[13] [[color("00ff00")]];
  u32 uncompressedSize [[color("00ee00")]];
  u32 compressedSize [[color("00dd00")]];
  
  // create decompression section
  Compressed isCompressed [[color("00cc00")]];
  currentSection = std::mem::create_section(name);
  std::mem::set_section_size(currentSection, uncompressedSize + 18);
  addSpace();
  u8 buffer[uncompressedSize] @ 18 in currentSection;
  currentOffset = 0;
  
  u32 end = $ + compressedSize;
  if (isCompressed == 0) {
      CompressedData compressedData [[color("000000")]];
  }
  else {
    u8 data[compressedSize] [[color("000000")]];
    std::mem::copy_value_to_section(data, currentSection, 18);
  }    
  str fileName = std::string::to_string(name);
  hex::core::add_virtual_file(fileName, buffer);
  std::mem::delete_section(currentSection);
} [[name(name)]];

// A dat archive
struct Archive {
  u16 fileCount [[color("ff0000"), name("Number of files")]];
  File files[fileCount] [[inline]];
} [[inline]];

Archive archive @ 0x00;