|
2 Novembre
2007
In questo post mi scollego solo temporaneamente col flusso di byte generato nei post precedenti per annotare una semplice informazione utile per programmi di monitoraggio. A volte mi capita di leggere nei forum la richiesta non rara di intercettare la cancellazione di un file e eventualmente annullarla (o in ogni caso loggare l'evento, etc...). Avrò guardato nei posti sbagliati ma non ho mai trovato qualcuno che abbia dato la risposta a queto semplice quesito (sicuramente ho cercato poco). Comunque la risposta è semplicissima: l'api più bassa che viene chiamata nell'ambito della procedura di eliminazione di un file è l'api Native (esportata da ntdll.dll) NtSetInfomationFile
type
FILE_INFORMATION_CLASS = ( FileFiller0, FileDirectoryInformation, // 1 FileFullDirectoryInformation, // 2 FileBothDirectoryInformation, // 3 FileBasicInformation, // 4 wdm FileStandardInformation, // 5 wdm FileInternalInformation, // 6 FileEaInformation, // 7 FileAccessInformation, // 8 FileNameInformation, // 9 FileRenameInformation, // 10 FileLinkInformation, // 11 FileNamesInformation, // 12 FileDispositionInformation, // 13 FilePositionInformation, // 14 wdm FileFullEaInformation, // 15 FileModeInformation, // 16 FileAlignmentInformation, // 17 FileAllInformation, // 18 FileAllocationInformation, // 19 FileEndOfFileInformation, // 20 wdm FileAlternateNameInformation, // 21 FileStreamInformation, // 22 FilePipeInformation, // 23 FilePipeLocalInformation, // 24 FilePipeRemoteInformation, // 25 FileMailslotQueryInformation, // 26 FileMailslotSetInformation, // 27 FileCompressionInformation, // 28 FileObjectIdInformation, // 29 FileCompletionInformation, // 30 FileMoveClusterInformation, // 31 FileQuotaInformation, // 32 FileReparsePointInformation, // 33 FileNetworkOpenInformation, // 34 FileAttributeTagInformation, // 35 FileTrackingInformation, // 36 FileMaximumInformation);
IO_STATUS_BLOCK = record Status: integer; Information: Cardinal; end; PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
//tipologia dell'informazione passata //(dato puntato dal parametro FileInformation //di NtSetInformationFile) FILE_DISPOSITION_INFORMATION = record DeleteFile: Boolean; //True se si vuole eliminare il file end; PFILE_DISPOSITION_INFORMATION = ^FILE_DISPOSITION_INFORMATION;
function NtSetInformationFile( FileHandle : Cardinal; //handle al file IoStatusBlock : PIO_STATUS_BLOCK; //puntatore al record contenente le informazioni //che voglio settare sul file FileInformation : Pointer; FileInformationLength : Cardinal; FileInformationClass : FILE_INFORMATION_CLASS ): integer; stdcall; external 'ntdll.dll';
Nel caso di eliminazione di un file, l'api NtSetInformationFile verrà chiamata con i seguenti parametri
1) FileInformation punta ad un record di tipo FILE_DISPOSITION_INFORMATION con il campo DeleteFile settato a True
2) FileInformationClass ha valore FileDispositionInformation (valore 13)
Tutto qui: in un programma che esegue un hook system wide sull'api NtSetInformationFile, basta verificare se ci sono queste impostazioni ed allora significa che si sta effettuando la cancellazione di un file (il primo parametro ossia FileHandle è appunto l'handle a tale file). Per ottenere il nome del file con percorso completo a partire dall'handle si può usare il codice seguente
type
OBJECT_INFORMATION_CLASS = ( ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectAllTypesInformation, ObjectHandleInformation);
PUNICODE_STRING = ^UNICODE_STRING; UNICODE_STRING = record Length: word; MaximumLength: word; Buffer: pWideChar; end;
OBJECT_NAME_INFORMATION = record Name: UNICODE_STRING; end; POBJECT_NAME_INFORMATION = ^OBJECT_NAME_INFORMATION;
function NtQueryObject( ObjectHandle : Cardinal; ObjectInformationClass : OBJECT_INFORMATION_CLASS; ObjectInformation : Pointer; ObjectInformationLength : Cardinal; ReturnLength : pCardinal ): Integer; stdcall; external 'ntdll.dll'
function QueryDosDeviceA( lpDeviceName: pAnsiChar; lpTargetPath: pAnsiChar; ucchMax: Cardinal ): Cardinal; stdcall; external 'kernel32.dll'
//dato un handle ad un file o ad una directory, ne resituisce il //percorso completo function DirNameFromHandle(dirHandle: THandle): string; const BUFSIZE = 1024; var pObjectNameInformation: POBJECT_NAME_INFORMATION;//Pointer; ObjectNameInformationLength: cardinal; NomeDir: string; pResponse: cardinal; rl: Cardinna;
devName: string; x: Cardinal; DrvLetter: char; Drv: string; begin
GetMem(pObjectNameInformation, 10000); NtQueryObject(dirHandle, ObjectNameInformation, pObjectNameInformation, 10000, @rl); NomeDir := WideCharToString(pObjectNameInformation.Name.Buffer); Freemem(pObjectNameInformation);
for DrvLetter:= 'A' to 'Z' do begin Drv:= DrvLetter+':'; SetLength(devName, BufSize); x:= QueryDosDeviceA(pchar(Drv), pchar(devName), BufSize-1); SetLength(devName, x-2); if Pos(devName, NomeDir) <> 0 then begin NomeDir:= StringReplace(NomeDir, devName, Drv, []); break; end; end;
result := NomeDir;
end;
L'utilizzo della NtSetInformationFile con i suddetti parametri in caso di cancellazione di un file è argomento trattato nel famoso libro Undocumented Windows Native API di Gary Nebbet. Tuttavia per questo e altre analisi sugli internals di Windows, una fonte di informazione di primaria importanza sono i sorgenti di ReactOS . La versione attuale (3 Novembre 2007) è la 0.3.3. Partendo dall'implementazione della CommandLine (cmd.exe) nella cartella base\shell\cmd, iniziando dal main in cmd.c si passa all'implementazione del comando del in del.c: tale implementazione chiama l'api win32 DeleteFile implementata in dll\win32\kernel32\file\delete.c ; l'implementazione è la seguente
/* $Id: delete.c 21253 2006-03-08 21:33:04Z audit $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: lib/kernel32/file/delete.c * PURPOSE: Deleting files * PROGRAMMER: Ariadne (ariadne@xs4all.nl) * UPDATE HISTORY: * Created 01/11/98 */
/* INCLUDES ****************************************************************/
#include <k32.h>
#define NDEBUG #include "../include/debug.h"
/* FUNCTIONS ****************************************************************/
/* * @implemented */ BOOL STDCALL DeleteFileA ( LPCSTR lpFileName ) { PWCHAR FileNameW;
if (!(FileNameW = FilenameA2W(lpFileName, FALSE))) return FALSE;
return DeleteFileW (FileNameW); }
/* * @implemented */ BOOL STDCALL DeleteFileW ( LPCWSTR lpFileName ) { FILE_DISPOSITION_INFORMATION FileDispInfo; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING NtPathU; HANDLE FileHandle; NTSTATUS Status;
DPRINT("DeleteFileW (lpFileName %S)\n",lpFileName);
if (!RtlDosPathNameToNtPathName_U (lpFileName, &NtPathU, NULL, NULL)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; }
DPRINT("NtPathU \'%wZ\'\n", &NtPathU);
InitializeObjectAttributes(&ObjectAttributes, &NtPathU, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtCreateFile (&FileHandle, DELETE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0);
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer);
if (!NT_SUCCESS(Status)) { CHECKPOINT; SetLastErrorByStatus (Status); return FALSE; }
FileDispInfo.DeleteFile = TRUE;
Status = NtSetInformationFile (FileHandle, &IoStatusBlock, &FileDispInfo, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation); if (!NT_SUCCESS(Status)) { CHECKPOINT; NtClose (FileHandle); SetLastErrorByStatus (Status); return FALSE; }
Status = NtClose (FileHandle); if (!NT_SUCCESS (Status)) { CHECKPOINT; SetLastErrorByStatus (Status); return FALSE; }
return TRUE; }
/* EOF */
Si vede chiaramente come il fulcro di tutto sia la chiamata a NtSetInformationFile con le impostazioni descritte in precedenza
|