Click here to Skip to main content
Click here to Skip to main content

Introduction to Vista Transactional NTFS (TxF) with Detours

, 27 Jan 2007
Rate this:
Please Sign up or sign in to vote.
Explaining Windows Vista new Transactional NTFS (TxF) APIS using Detours library.

Contents

Introduction

Windows vista has many interesting new features at the kernel and the core operating system level. In this article you will learn about Transactional NTFS (abbreviated TxF) which is implemented on top of Kernel Transaction Manager (KTM), a new feature that allows programs developed for windows vista to use transactions. You will also learn about Microsoft Detours which is a library for intercepting arbitrary Win32 binary functions on x86 machines.

Prerequisites

This article was developed for Windows Vista RC2 but should work also for the final release.

What is a transaction?

A transaction is defined as a series of operations which either all occur, or all do not occur. To give you a very simple example, consider you are updating a program which consists of one executable (.exe) file and three dynamic link libraries (.dll) with a newer version of the program. In most cases, the update script (could be the program installer) will take a backup version of all the files first, and then try to update the files. If one file failed to be updated, the script will restore all the files it had backed up and will notify you that the update failed.

The script did this behavior to prevent the program from being in an inconsistent state, since if one of the files failed to be updated, the new version will fail to run and the old version files were overwritten.

So what the update script did here is a very simple transaction.

  1. begin transaction: take a backup of old file (however most real word transactions run in an isolated environment instead of backing up the original environment)
  2. Execute operations: copy the newer files.
  3. Commit or rollback transaction: if any file failed to be updated, restore backup file (rollback). If all operations succeeded, delete backup files (commit).

Of course, this only shows the very basic concept of transaction. A real world transaction has must have a set of properties which are abbreviated as ACID. ACID is a very familiar term especially for database developers since most of database operations need to be transacted.

  • Atomicity
  • Consistency
  • Isolation
  • Durability

I will not go much into the details of explaining each of those terms since you can find lots of resources covering this topic in details over the web.

Windows Vista Kernel Transaction Manager

Kernel Transaction Manager (KTM) is a part of the vista kernel that allows both user mode (programs) and kernel mode (device drivers) applications to use transactions which is to define a set of operations which either all occur, or all do not occur. Vista ships with two modules built on the top of KTM which are Transactional NTFS (TxF) and Transactional Registry (TxR).

This topic will mainly concentrate on Transactional NTFS (TxF) although Transactional Registry is the same concept applied on windows registry manipulation.

Transactional NTFS (TxF)

The definition of Transactional NTFS in the windows platform SDK documentation is "Transactional NTFS allows file operations on an NTFS file system volume to be performed in a transaction."

That means Windows Vista applications on NTFS (The disk file structure used by Window NT and Windows 2000) partitions, can do files manipulation as atomic transactions. So you can for example open many files for editing in one atomic transaction and at any time you can commit all your changes (transaction successes) or rollback all the changes (transaction failure) based on own program logic and TxF will handle all the transaction ACID properties for you (Atomicity ,consistency, isolation and durability).

All what you basically need to do is to use CreateTransaction method to create a transaction object, then use CreateFileTransacted which takes a Handle (pointer) to a transaction object as one of its parameters to manipulate a file as a transacted operation.

Finally you should use CommitTransaction or RollbackTransaction to either save or undo all the changes to the file.

This is indeed a very cool feature, but the problem is that programs have to be written to explicitly use TxF to benefit from it. What if older programs such as notepad or winrar (yes Smile | :) ) can use TxF? That would have been even cooler. Here is where I introduce you Detours.

Microsoft Research Detours Package

Microsoft Detours library is a sophisticated Windows API hooking framework. But first what is API hooking?

API hooking is a really old concept back from the old days of DOS (disk operating system).

DOS developers used DOS interrupts to call OS system functionalities. For example programmers had to call INT 21 (interrupt 21) to do most of IO operations. INT 21 means to call the procedure which address you will get from entry number 21 in the interrupt vector table (IVT).

So all of the operating system procedures addresses were kept in the IVT and you only choose which interrupt to call and the system will look it up from its IVT.

One way for developers to intercept calls to system interrupts, was to modify the IVT and replace system procedures addresses with the address of their own procedures. In many cases, they saved the address of the original procedure, in case they want to call it from their own procedures.

What is this for? For example if you want all data on a disk to be encrypted, and only your computer can open this disk. What you can do is to override the interrupt of write data to disk with your own procedure, encrypt all the data which are sent originally to this interrupt, then call the original interrupt passing it the encrypted data. Reverse-wise in case of reading from disk to decrypt back the data.

Of course interrupts still exists in Windows, but you they are not exposed to user mode programs.You will mostly use APIs for most system functions. So what if you want to run your own code when a program calls a certain Window API? Here is where the term API hooking takes its place.

There are many different ways to hook Windows APIs which are out the scope of this article.

One trick that Detours library uses is runtime memory modification of the Import Address Table, which is where PE (portable executable, the format of windows executable) stores the imported dll functions addresses in memory. For simplicity, it's like an IVT but for each program.

So here is our plan, we want to inject our code into a specific program so that we initially create a transaction object, and every time the program call CreateFileW which is the API method to create or open a file, we want to use CreateFileTransactedW instead, passing to it the original parameters plus our transaction object.

*CreateFileW is the Unicode version which is mostly used in new programs, Older, pre windows 2000, programs use the ANSI encoding version CreateFileA where you can use CreateFileTransactedA.

Fortunately, this task is pretty simple using detours. We will create a Detours-based DLL which modifies the CreateFileW API to call CreateFileTransactedW instead.

So here is the code that I initially wrote (which is based on simple Detours dll skeleton):

//definition of our transation handle
HANDLE trans = NULL;

//function pointer to CreateFileW API function
static HANDLE (WINAPI * TrueCreateFile)
(
 LPCWSTR lpFileName,
 DWORD dwDesiredAccess,
 DWORD dwShareMode,
 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
 DWORD dwCreationDisposition,
 DWORD dwFlagsAndAttributes,
 HANDLE hTemplateFile
 ) = CreateFileW;

//Our function where will call CreateFileTransactedW instead of CreateFileW
HANDLE WINAPI TransCreateFile
(
 LPCWSTR lpFileName,
 DWORD dwDesiredAccess,
 DWORD dwShareMode,
 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
 DWORD dwCreationDisposition,
 DWORD dwFlagsAndAttributes,
 HANDLE hTemplateFile
 )
{
    //call CreateFileTransactedW with our transaction object
    return CreateFileTransactedW(
        lpFileName,
        dwDesiredAccess,
        dwShareMode,
        lpSecurityAttributes,
        dwCreationDisposition,
        dwFlagsAndAttributes,
        hTemplateFile,
        trans,
        NULL,
        0
        );

}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
    LONG error;
    (void)hinst;
    (void)reserved;

    //Detour initialiazation
    if (dwReason == DLL_PROCESS_ATTACH) {

        DetourRestoreAfterWith();

        //we create our transaction object
        trans = CreateTransaction(NULL,0,0,0,0,NULL,NULL);

        if (INVALID_HANDLE_VALUE == trans)
        {
            MessageBox(NULL, "Transaction creation failed", "Error", 0);
        }
        else
        {
            MessageBox(NULL, "Program running in Transacted NTFS mode.", 
                       "", 0);
        }

        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)TrueCreateFile, TransCreateFile);
        error = DetourTransactionCommit();

        if (error != NO_ERROR)
        { 
            MessageBox(NULL, "Detouring failed", "Error", 0);
        }
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
        //ask the user if he wants to commit the transaction on exit
        if (IDYES == MessageBox(NULL, "Commit transaction?", 
                                "Transaction Pending", MB_YESNO))
        {
            //commit the transaction
            CommitTransaction(trans);
        }

        //close transaction handle
        CloseHandle(trans);

        //detour unintialization
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueCreateFile, TransCreateFile);
        error = DetourTransactionCommit();
    }

    return TRUE;
}

To my surprise, this code did NOT work, failing with a strange error "Implicit transactions are not supported."

After some failed investigations to find the cause of the error, an engineer working at Microsoft, kindly enough, hinted me the cause of the problem.

CreateFileTransacted calls under the cover CreateFile API, and since we hooked CreateFile with CreateFileTransacted, now CreateFileTransacted calls itself. Due to some function check, this did not end up with the program crashing due to stack overflow, but did give me this weird error.In fact, later I knew that implicit transaction support did exist in Vista's early betas but was removed for some reasons (most probably security or incompatibility).

Here is a code snippet on how TxF was used in early vista betas from TxF msdn blogs:

hReaderTrans = CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL);
SetCurrentTransaction(hReaderTrans);

hReaderFile = CreateFile(szFileName,
                         GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE|
                         FILE_SHARE_DELETE,
                         NULL,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL);

SetCurrentTransaction(NULL);
CloseHandle(hReaderFile);

This means that it is possible by running a program in TxF which it was not designed for, you might run at the risk of breaking the software license or the program functionality. Of course this article is mainly an educational resource to demonstrate the vista TxF APIs and Detours.

So back to our program, here is how it was fixed

//Our function that will call CreateFileTransactedW instead of CreateFileW

HANDLE WINAPI TransCreateFile
(
 LPCWSTR lpFileName,
 DWORD dwDesiredAccess,
 DWORD dwShareMode,
 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
 DWORD dwCreationDisposition,
 DWORD dwFlagsAndAttributes,
 HANDLE hTemplateFile
 )
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&(PVOID&)TrueCreateFile, TransCreateFile);
    DetourTransactionCommit();

    HANDLE h;

    //call CreateFileTransactedW with our transaction object

    h = CreateFileTransactedW(
        lpFileName,
        dwDesiredAccess,
        dwShareMode,
        lpSecurityAttributes,
        dwCreationDisposition,
        dwFlagsAndAttributes,
        hTemplateFile,
        trans,
        NULL,
        0
        );

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)TrueCreateFile, TransCreateFile);
    DetourTransactionCommit();

    return h;
}

So every time our TransCreateFile function is called, before we call CreateFileTransactedW, we will restore CreateFileW to its original API function then hook it again after the call is completed.

Now when CreateFileTransactedW calls CreateFileW internally, it goes to the right function.

TxF Miniversions

Another interesting feature in TxF is Miniversions.You can save the state of the current changes as a Miniversion (Checkpoint).

CreateFileTransacted optionally takes a miniversion in its arguments to open a file to a specific saved checkpoint (A function very handy to implement Undo and Redo buffers).

However to save a miniversion, you need to tell the KTM directly to do so. But remember the KTM is a kernel module so how to tell it to create a miniversion?

Communication with kernel module is not done directly in windows, it is done through the IO manager. You send the IO manager a control code, specify which module to pass the control code to, and you can exchange input and output buffers (there are different modes for buffer exchange which are not in the scope of this article). The IO manager then routes the control command to the kernel module and does the buffer exchange between user mode programs and kernel mode programs.

To communicate with the IO manager, use DeviceIoControl on the open handle of the file and FSCTL_TXFS_CREATE_MINIVERSION control code to save the miniversion. The TXFS_CREATE_MINIVERSION_INFO structure will be returned in the output buffer containing information about the created miniversion.

TXFS_CREATE_MINIVERSION_INFO txfMiniversion;

BOOL result = DeviceIoControl(
  h,                             // handle to file 
  FSCTL_TXFS_CREATE_MINIVERSION, // control code creates a new miniversion
  NULL,                          // input buffer (null)
  0,                             // input buffer size
  (LPVOID) &txfMiniversion,      // miniversion info structure
  (DWORD) sizeof(TXFS_CREATE_MINIVERSION_INFO),  // size of output buffer
  (LPDWORD) lpBytesReturned,     // number of bytes returned
  (LPOVERLAPPED) NULL            // OVERLAPPED structure (null for 
                                 // synchronous operation)
);

Remember the miniversions are discarded in case you either commit or rollback your transaction.

How to run the example?

You need to have installed Microsoft Windows SDK and Detours Express 2.1. Launch the Windows SDK CMD Shell and go to \Detours Express 2.1\src and type nmake to compile the detoured dll. Also compile \Detours Express 2.1\samples\withdll and \txf which you download with this article (extract the folder under \samples in Detours directory) .

All the Detours compiled files are in \Detours Express 2.1\bin

To run notepad in a txf mode use the command as following

withdll.exe /d:txf.dll /p:detoured.dll notepad txftest.txt

You will be notified that notepad is running in Transacted NTFS mode and txftest.txt if not already existing will be created. Now you can edit txtftest.txt and save it then close notepad.

You will be prompted whether you want to commit the transaction or not. If you choose no, all changes you made to the file will be discarded and file will be rolled back to its initial state, otherwise file is saved to disk.

Final Considerations

This is not by anyway a perfect hook , you will notice that this method may cause problems with some applications or in some cases since there are many other file operations needs to be hooked. To name some:

  • CopyFileTransacted
  • CreateDirectoryTransacted
  • CreateHardLinkTransacted
  • CreateSymbolicLinkTransacted
  • DeleteFileTransacted
  • FindFirstFileNameTransactedW
  • FindFirstFileTransacted
  • FindFirstStreamTransactedW
  • GetCompressedFileSizeTransacted <code>
  • GetFileAttributesTransacted
  • GetFullPathNameTransacted
  • GetLongPathNameTransacted
  • MoveFileTransacted
  • RemoveDirectoryTransacted
  • SetFileAttributesTransacted

Those functions can all be hooked the same way we hooked CreateFileTransacted, but making this code 100% compatible to be hooked with all applications is not the target of this article.

Also as a friend of mine notified me, this sample code is not thread safe. That means if you are hooking a multithread application, further changes should be made to the code for thread synchronization if more than one thread could possibly call CreateFile at the same time.

You learned now about transactions, KTM, TxF and Detours and now you can start the real the fun.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Tamer Safwat Aziz
Software Developer
Egypt Egypt
B.Sc. Computer Science.
Independent Developer.
Interested in C/C++/C#/Win32API/Bluray Java development
 
Author of MagicMerge and Phonashera applications.

Comments and Discussions

 
QuestionWhat about Windows 9+ ? Pinmember109fe006bc5537efaa52059a322bc62830-May-12 23:13 
Question2Tb Virtual Disk Pinmembernhchmg27-Jun-11 20:18 
NewsOut of date PinmemberUL-Tomten28-Sep-08 0:37 
QuestionTxF with XP? PinmemberBartosz Bien16-May-07 21:47 
AnswerRe: TxF with XP? PinmemberTamer S. Aziz16-May-07 23:06 
GeneralRe: TxF with XP? PinmemberBartosz Bien16-May-07 23:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 27 Jan 2007
Article Copyright 2007 by Tamer Safwat Aziz
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid