Haciendo nuestros ficheros imborrables


En esta entrada estaremos compartiendo una de las muchísimas cosas que podemos hacer en nuestro SO a través del “API hooking” o “Detouring” como también se conoce esta técnica. Veremos un un ejemplo que consiste en cómo podemos hacer que un fichero o grupo de ficheros que deseemos, sean prácticamente “imborrables” desde Windows. En esta ocasión no trabajaremos con drivers ni nada a bajo nivel, solo de una DLL que usaremos desde nivel de usuario y es la que nos hará el trabajo. Veamos de qué se trata.

Por donde empezamos

Vamos a asumir que tenemos algún fichero, ya sea en el disco duro, flash, etc y que queremos proteger frente a cualquier intento de eliminación. Obviamente le echaríamos la culpa a la API DeleteFileA/W de llevar a cabo esta tarea y por consiguiente a la API nativa Nt/ZwDeleteFile, pero no estaríamos más lejos de la verdad, ya que Nt/ZwDeleteFile no interviene en este caso, asi que tendremos que adivinar otra vez.

Si pusiéramos la API DeleteFileA/W en un desensamblador y la miráramos por dentro un poco no veriamos por ningún lado a Nt/ZwDeleteFile sino a Nt/ZwSetInformationFile, esta última es la API nativa detrás de la cortina y su definición es la siguiente:

ZwSetInformationFile: Establece la informacion que afecta a un objecto tipo fichero

NTSYSAPI
NTSTATUS
NTAPI
ZwSetInformationFile(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    IN PVOID FileInformation,
    IN ULONG FileInformationLength,
    IN FILE_INFORMATION_CLASS FileInformationClass
);

Parametros

FileHandle: Un identificador de un tipo object. El archivo debe conceder FILE_WRITE_DATA, FILE_WRITE_EA, FILE_WRITE_ATTRIBUTES o eliminar el acceso a algunas clases de información.

IoStatusBlock: Apunta a una variable que recibe el estado de terminación final y la información acerca de la operación solicitada.

FileInformation: Apunta a un búfer asignado por el llamador o variable que contiene el archivo información que se fijará.

FileInformationLength: El tamaño en bytes de FileInformation, que debe establecer la llamada de acuerdo con la FileInformationClass dado.

FileInformationClass: Especifica el tipo de información de archivo que se set. Los valores permitidos son el subconjunto de la enumeración FILE_INFORMATION_CLASS.

Valores de Retorno

Retorna STATUS_SUCCESS o un estado de error, como STATUS_ACCESS_DENIED, STATUS_INVALID_HANDLE, STATUS_INVALID_INFO_CLASS, o STATUS_INFO_LENGTH_MISMATCH.

La definicion del enum FILE_INFORMATION_CLASS es la siguiente:

typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1, // 1
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4
FileStandardInformation, // 5
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14
FileModeInformation = 16, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20
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
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

De todas ellas la que usa la API DeleteFileA/W a traves de Nt/ZwSetInformationFile es FileDispositionInformation, cuya definición y estructura son las siguientes:

FileDispositionInformation: Por lo general, establece el miembro de DeleteFile de un FILE_DISPOSITION_INFORMATION en TRUE, por lo que el archivo puede ser borrado cuando ZwClose es llamado para liberar el último identificador abierto al objeto de archivo. La persona que llama debe haber abierto el archivo con el indicador DELETE en el parámetro DesiredAccess.

typedef struct _FILE_DISPOSITION_INFORMATION { // Class 13
    BOOLEAN DeleteFile;  // Indica si el fichero debe ser eliminado
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;

Muy bien, con esta informacion ya podemos ir haciéndonos una idea de cómo tenemos que actuar. Si lo que queremos es que algún fichero o grupo de ficheros queden protegidos contra eliminacion, tendremos que engancharnos en la API nativa Nt/ZwSetInformationFile, la cual es usada por DeleteFileA/W y verificamos si se esta pasando por parámetro FileDispositionInformation con DeleteFile=TRUE, en caso afirmativo verificamos que se trate del fichero o grupo de ficheros que estarán protegidos, si este caso también es afirmativo, pues devolvemos STATUS_ACCESS_DENIED y nunca se ejecuta la API real. En caso de que las premisas anteriores sean falsas, llamamos a la API verdadera como si nada pasara.

Usando DetourXS

DetoursXS es una librería creada por Sinner en http://www.gamedeception.net/index.php?threadsdetourxs.10649/ y que nos permite crear API’s Hooking de una forma sencilla y rápida, una de sus principales características es la posibilidad de especificar diferentes tipos de “detour jumps”. En el adjunto al final de esta entrada se encuentra una copia de la misma con las fuentes del proyecto.

El código

El código de la DLL que inyectaremos en el proceso(s) para proteger nuestros ficheros quedaría como sigue en CodeGear C++:

//------------------------------------------------------------
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include "./DetourXS/include/detourxs.h"
#pragma comment (lib,"./DetourXS/lib/detourxs.lib")
#pragma hdrstop

// Redefinitions of header ntstatus.h
#define STATUS_SUCCESS              ((NTSTATUS)0x00000000L)
#define STATUS_ACCESS_DENIED        ((NTSTATUS)0xC0000022L)
#define STATUS_INVALID_INFO_CLASS   ((NTSTATUS)0xC0000003L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
//-------------------------------------------------------------
DWORD (WINAPI *_NtQueryInformationFile)(
    HANDLE,
    PVOID,
    PVOID,
    DWORD,
    DWORD
);

typedef struct _FILE_DISPOSITION_INFORMATION { // Class 13
    BOOLEAN DelFile; // Indica si el fichero debe ser eliminado.
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;

typedef struct _FILE_NAME_INFORMATION { // Classes 9 and 21
    ULONG FileNameLength;
    WCHAR FileName[MAX_PATH];
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION,
FILE_ALTERNATE_NAME_INFORMATION, *PFILE_ALTERNATE_NAME_INFORMATION;

// Prototipo de la API para el Hook
typedef NTSTATUS (WINAPI* _RealZwSetInformationFile)(
   HANDLE FileHandle,
   DWORD *IoStatusBlock,
   PVOID FileInformation,
   ULONG FileInformationLength,
   FILE_INFORMATION_CLASS FileInformationClass
);_RealZwSetInformationFile RealZwSetInformationFile;

//-------------------------------------------------------------
// Obtiene nombre del fichero por su manejador
void GetFileName(HANDLE hFile, wchar_t *file_name)
{
   *(FARPROC *)&_NtQueryInformationFile = GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationFile");
   if (_NtQueryInformationFile == NULL)
      return;

    FILE_NAME_INFORMATION fni = {0};
    DWORD iob[2];

    _NtQueryInformationFile(hFile, iob, &fni, sizeof(FILE_NAME_INFORMATION), 9 );

    wchar_t *posptr = fni.FileName + wcslen(fni.FileName);
    while(posptr[-1] != '\')
       posptr--;

    wcscpy(file_name, posptr);
    return;
}
//-------------------------------------------------------------
// Nuestra propia función filtro
DWORD WINAPI MyZwSetInformationFile(HANDLE FileHandle, DWORD *IoStatusBlock,
                                    PVOID FileInformation, ULONG FileInformationLength,
                                    FILE_INFORMATION_CLASS FileInformationClass)
{
    wchar_t file_name[MAX_PATH] = {0};
    wchar_t prefix[] = L"nodel_"; // El prefijo del fichero a proteger

    if(FileInformationClass == /*FileDispositionInformation*/13)
    {
       GetFileName(FileHandle, file_name); // Obtenemos el nombre del fichero

       if((((PFILE_DISPOSITION_INFORMATION)FileInformation)->DelFile == true) &&
          (wcsncmp(file_name, prefix, wcslen(prefix)) == 0))
       {
          // aqui puede ir cualquier código que queramos
          return STATUS_ACCESS_DENIED; // retornamos como si no se tuviera acceso
       }
    }
    // En caso contrario ejecutamos la API real normalmente
    return RealZwSetInformationFile(FileHandle, IoStatusBlock, FileInformation,
                                    FileInformationLength, FileInformationClass);
}

//-------------------------------------------------------------
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
   if (fwdreason == DLL_PROCESS_ATTACH)
      // Creamos el gancho
      RealZwSetInformationFile = (_RealZwSetInformationFile)DetourCreate("ntdll.dll", "ZwSetInformationFile", &MyZwSetInformationFile, DETOUR_TYPE_JMP);

   if (fwdreason == DLL_PROCESS_DETACH)
      // Quitamos el gancho
      DetourRemove(RealZwSetInformationFile);

   return 1;
}
//-------------------------------------------------------------

Si probamos inyectándonos en algún proceso, digamos “explorer.exe” e intentamos eliminar un fichero con el prefijo “nodel_” esto es lo que pasará

Captura

Captura2

Si quisiéramos protección adicional para que tampoco se puedan renombrar los archivos y de esta forma alguien pueda saltar nuestra protección, tendríamos que mirar la clase FileRenameInformation y filtrarla también en nuestro gancho.

Adjunto el código fuente completo + binarios
Descarga: nodel_hook

Anuncios

4 Respuestas a “Haciendo nuestros ficheros imborrables

    • Pues claro que pudieras 😛
      Recuerda que la tecnica se basa en establecer filtros en las API’s de Windows, por lo que si no estas desde ese SO, los ficheros pueden ser borrados sin problemas. Te digo mas, si aun lograras descargar la dll inyectada de la memoria del proceso, con eso bastaria.
      Saludos y gracias por comentar 😉

      Me gusta

    • Bueno, pues se podria hacer hooking/filtrado en la API encargada de cargar las DLL en los procesos a mas bajo nivel:

      LoadLibraryA () -> LoadLibraryW () -> LoadLibraryExW () -> LdrLoadDll () -> ring0

      Por lo que filtrando LdrLoadDll seria suficiente, luego ya permites o no la carga de una determinada DLL por alguna lista negra/blanca

      Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s