X-Ways Forensics Image I/O API Documentation

Introduction

The X-Ways Forensics Image I/O API is a programming interface that allows you to extend the array of disk image file formats supported by the computer software products X-Ways Forensics, X-Ways Investigator and WinHex Lab Edition. This API is available in v19.5 and later. It is meant for physical sector-wise images of storage devices, i.e. not logical collections of individual files (to support those kinds of images, you could use the X-Tensions API instead).

You just need to compile a DLL and export three specific functions to add support for another image file format. The DLL has to match the filename mask “Image*.dll” (it is suggested to name it „ImageIO XYZ.dll”, where XYZ is the typical filename extension of the images that you support), and it has to be placed directly into the installation directory of X-Ways Forensics (in the \x64 subdirectory if it's a 64-bit DLL). If a user adds such a DLL from a trusted source to the installation directory, that signifies consent to get it executed by X-Ways Forensics. One DLL can implement support for one or multiple image types. Up to two such DLLs in the installation directory are currently supported. Once loaded, the DLL remains in memory until the X-Ways Forensics session ends. The DLL is loaded with the so-called alternate search order / altered search path strategy as described here and here. In essence it means that if your DLL statically links further dependent DLLs, those will be found if they are contained in the same directory as the main DLL.

FYI, all exported functions have to use stdcall calling conventions under 32 bit. You have to export all functions with undecorated names, or else they will not be found by X-Ways Forensics! This concept is described for example here and here. In Microsoft Visual Studio that might mean that you should not declare your functions with __declspec(dllexport), but extern "C" __declspec(dllexport) instead. To check the actual exported function names, you can view your own DLL in X-Ways Forensics with the viewer component.

Demo DLLs (32-bit and 64-bit, with Delphi source code)

Documentation last updated on Oct 17, 2017. Related API: X-Ways Forensics X-Tensions API


Preparation

PVOID IIO_Init(
   HANDLE hMainWnd,
   LPWSTR lpFilePath,
   PVOID pHeaderBuf,
   DWORD nHeaderBufSize,
   struct ImageInfo* pImgInfo,
   PVOID pReserved
);

#pragma pack(2)
struct ImageInfo {
   DWORD nSize,
   INT64 nSectorCount,
   DWORD nSectorSize,
   DWORD nFlags,
   LPWSTR lpTextualDescr
};

Will be called when X-Ways Forensics is about to interpret a presumed image file. You are given a pointer to a null-terminated file path in UTF-16 Unicode (lpFilePath) and a pointer to a buffer (lpFilePath) with the first few (nHeaderBufSize) bytes of the file contents. You can deduct directly from the filename or filename extension  and/or those few bytes or indirectly (by reading more from the file contents or looking for other files in the same directory) whether your DLL supports this kind of image. If so, you return a pointer to a private structure of yours, by which you identify that image in future function calls, plus you have to fill the structure pointed to by pImgInfo to give the caller some information about the image so that it can be treated correctly. If you do not support the specified file type, you simply return NULL. In that case the structure pointed to by pImgInfo will be ignored, and X-Ways Forensics will try to interpret the image without your DLL being involved.

If you return a non-NULL pointer, but the caller is not happy with how you filled the ImageInfo structure (for example, unsupported value of nSize returned or flag IIO_INIT_READ mysteriously not set), you can still expect a quick call of your IIO_Done function so that you can at least tidy up after yourself, but you will not be actually called for any I/O work for that image. The ImageInfo structure is initially guaranteed to be zeroed except for the nSize part.

If you open a file handle to the image file, you have to choose a sharing mode for that, not an exclusive mode.

hMainWnd: Handle of the main application window in case you need it, for example as a parent window for a message box or dialog box that asks the user for preferences or credentials etc.

nSize: The size of the ImageInfo structure. This (and only this) member variable is already set by the caller, and its value is currently 4+8+4+4+8=28 bytes in x64 (4+8+4+4+4=24 in x86), but it may be larger in future versions of X-Ways Forensics. You do not currently have to check the value (only if you are curious), but you have to overwrite it with the size that you support. Should a future version of X-Ways Forensics work with a larger structure, the original part remains the same and newly added member variables are appended, but the caller will not expect you to have filled them if the nSize that you set is still the smaller size of older versions of this API.

nSectorCount: The number of sectors represented by the image.

nSectorSize: Size of one sector in bytes. Currently supported sector sizes in X-Way Forensics are 512, 1024, 2048, 4096, and 8192 bytes.

nFlags: A combination of the below flags.
#define IIO_INIT_READ 0x0001 // return this to signal read support for that image type
#define IIO_INIT_WRITE 0x0002 // return this to signal write support for that image type
#define IIO_INIT_DISK 0x0010 // image is known to represent a physical storage device, possibly partitioned
#define IIO_INIT_VOLUME 0x0020 // image is known to represent a volume/partition
#define IIO_INIT_THREADSAFE 0x0100 // your functions are thread-safe (currently a must)
#define IIO_INIT_UNALIGNED_OK 0x0200 // your functions support sector-unaligned I/O requests (not a must)
#define IIO_INIT_ERROR_MILD 0x1000 // errors in the image have been detected, but it can presumable be read completely
#define IIO_INIT_ERROR_SEVERE 0x2000 // errors in the image have been detected, it cannot be fully read
#define IIO_INIT_ERROR_GIVE_UP 0x4000 // image type generally supported, but unable to open this particular file, lpTextualDescr may be NULL or may point to an error message, X-Ways Forensics will not try other means to interpret the image

It is OK to set neither IIO_INIT_DISK not IIO_INIT_VOLUME if you do not know what kind of image it is. In that case X-Ways Forensics will make its own determination or might prompt the user.

lpTextualDescr: Optional. Either a null-terminated string that contains textual metadata about the image, such as who created it, how and when, using which tool, with which compression option, etc., or (if the flag IIO_INIT_ERROR_GIVE_UP is set) an error message for the user to see. You can allocate that buffer in memory with any compiler-specific memory allocation function or Windows API function of your choice. You will be asked to release it when your IIO_Done() function is called.

nAvailSectorCount: Not part of the structure, just a hypothetical extension. Do not use. The actually available/readable number of sectors in the image counted from the first sector. Usually should be the same as nSectorCount, but could be smaller if you detect that the image is incomplete (e.g. some file segments missing) or corrupt. 

Reading/Writing

INT64 IIO_Work(
   PVOID lpImage,
   INT64 nOfs,
   INT64 nSize,
   PVOID lpBuffer,
   PDWORD pFlags
);

This function is called to read nSize bytes from offset nOfs in the image identified by your own pointer lpImage, i.e. the result of IIO_Init(), into the memory buffer pointer to by lpBuffer. It might also be called to write that many bytes at the specified offset, provided that you have signaled write capability in IIO_Init().

pFlags: Some flags are set by the caller, some additional flags may be set by the callee. In a simple implementation, if you did not signal write capability and do not provide sparse support, you can simply ignore this flags field altogether.
#define IIO_WRITE 0x01 // in, write instead of read
#define IIO_CHECK_FOR_SPARSE 0x20 // in, callee should check for and signal sparse areas if possible
#define IIO_SPARSE_DETECTED 0x40 // out, requested range of data is entirely sparse

Please return the number of successfully read/written bytes. IIO_SPARSE_DETECTED should not be returned if IIO_CHECK_FOR_SPARSE was not set. If IIO_CHECK_FOR_SPARSE was set and you return IIO_CHECK_FOR_SPARSE, you don't have to fill the buffer at all, but you are still supposed to return the total number of bytes covered/requested. The caller takes the IIO_CHECK_FOR_SPARSE flag as a hint that the data in the requested range is ignorable and will save time by not processing it.

Cleaning Up

DWORD IIO_Done(
   PVOID lpImage,
   LPWSTR lpTextualDescr
);

When the image is closed/no further I/O is required, X-Ways Forensics calls this function to give you a chance to free up any resources such as file handles, that you were keeping open since the Init function was called. You are given the pointer that your IIO_Init() function had returned, and you are also asked to release the buffer for the textual description if you had provided one. When done, please return 1.