Vlad Ioan Topan

My playground

Using NtDeleteFile from Delphi

with 2 comments

The NtDeleteFile API

The native API NtDeleteFile performs the same task as the user-mode API DeleteFile, but interrestingly enough, the user-mode API does not call the native API to perform it’s task. As explained here, normally files are deleted through calls to NtSetInformationFile. The main difference in behavior comes from the fact that NtDeleteFile does not wait for handles on the file to close before deleting it (note that if the file is “open for normal I/O or as a memory-mapped file”, it still can’t be deleted, so only read-only handles will be ignored).

Data structures

The required structures (not defined in Delphi) are UNICODE_STRING and OBJECT_ATTRIBUTES.


The steps to remove the file are:

  • convert the “DOS” path name to an “NT” path name (basically prepend “\??\” to whatever the plain path is) using RtlDosPathNameToNtPathName_U;
  • fill in the OBJECT_ATTRIBUTES structure;
  • call NtDeleteFile.

Importing the APIs

Using the afore-linked-to type definitions, we still need to import the native APIs:

function NtDeleteFile(ObjectAttributes:POBJECT_ATTRIBUTES):DWORD; stdcall; external 'ntdll.dll';
function RtlDosPathNameToNtPathName_U(DosName:PWChar; var NtName:UNICODE_STRING; DosFilePath:PPChar; NtFilePath:PUNICODE_STRING):BOOL; stdcall; external 'ntdll.dll';

Do note that this statically links the imported functions, making the whole application unable to load if the undocumented APIs are not present on the system (at least for Windows 2000 and XP they should be).

Converting DOS paths to native paths using RtlDosPathNameToNtPathName_U

Converting the DOS path name to a native path is done by the RtlDosPathNameToNtPathName_U API, which takes in a PWChar argument containing the DOS path and a pre-allocated UNICODE_STRING structure of MAX_PATH WideChars and returns True if it has successfully converted the path.

Source code

The code looks something like this:

function _DeleteFile(fileName:string):DWORD;
   ws1, ws2:WideString;
   Result := $C0000001; // STATUS_UNSUCCESSFUL, "generic" error 
   ws1 := fileName; // automatic String -> WideString conversion
   SetLength(ws2, MAX_PATH);
   us.Length := MAX_PATH;
   us.MaximumLength := MAX_PATH;
   us.Buffer := @ws2[1];
   if not RtlDosPathNameToNtPathName_U(@ws1[1], us, nil, nil)
      then Exit;
   oa.Length := SizeOf(OBJECT_ATTRIBUTES);
   oa.RootDirectory := 0;
   oa.ObjectName := @us;
   oa.Attributes := $40; // case insensitive
   oa.SecurityDescriptor := nil;
   oa.SecurityQualityOfService := nil;
   Result := NtDeleteFile(@oa); // pass on the NTSTATUS

Written by vtopan

May 26, 2009 at 3:43 PM

Posted in Delphi, Snippets

Tagged with , ,

2 Responses

Subscribe to comments with RSS.

  1. As a sidenote: even though opened files can not be deleted, they can be renamed. This is a handy trick when trying to get rid of some malware.


    May 27, 2009 at 6:10 AM

  2. True. And speaking of malware, to completely avoid hooks from user mode, the NtDeleteFile call can be replaced by a SYSENTER or INT 2Eh with the appropriate ID (based on the OS ver.). This technique also works for other native APIs; a nice paper with explanation & details from SecurityFocus: http://www.securityfocus.com/infocus/1844/


    May 27, 2009 at 9:51 PM

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: