Introducción a la programación de drivers en Windows

Iniciado por Hendrix, 12 Octubre 2008, 20:43 PM

0 Miembros y 7 Visitantes están viendo este tema.

LixKeÜ

 La version de DDK para windows server 2003 mas precisamente esta http://www.microsoft.com/whdc/devtools/ddk/default.mspx
Funciona para windows XP...

Hendrix

#11
2. Introducción

2.1 Hola mundo desde el driver

Una vez leído el apartado de nociones básicas, vamos a centrarnos en lo que es la programación de drivers.

Como en todo programa, tiene que haber un punto de partida, un main. El de los drivers es así:


NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
//Codigo
}


Como vemos, al DriverEntry se le pasan 2 parámetros, el primero es un puntero a la estructura DRIVER_OBJECT, más adelante veremos como usar esto. El segundo es un puntero a una cadena Unicode donde esta guardada la ruta del registrot con el que se cargó el driver.

Una vez realizadas las tareas en el DriverEntry, tenemos que retornar un valor, si no ha habido ningún error retornaremos STATUS_SUCCESS, de lo contrario, el código de error pertinente.

Cabe decir que retornando el valor no se descarga el driver, ya que para poderse descargar se tiene que crear una rutina, pecisamente esta rutina se crea a partir del puntero al primer parametro. Veamos como se hace:


void Salir(PDRIVER_OBJECT DriverObject)
{
   //Codigo de salida
}

NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
   DriverObject->DriverUnload=Salir;
   return STATUS_SUCCESS;
}


Como vemos aquí, creamos una rutina para que al descargar el driver se llame a la rutina, dentro podemos escribir un mensaje de "Cerrando driver..." o algo asi, o en su caso, unhookear las apis, ya que si cerramos el driver si unhookear la SSDT nos va a mostrar una bonita pantalla azul, ya que se va a llamar una zona de memoria donde no hay nada.

El comando para poder escribir datos al DebugView es el comando DbgPrint y funciona exactamente igual que el printf de C/C++. Si nos miramos la información, vemos que esta dentro de Ntddk.h, asi que la tenemos que incluir. El programa que nos dirá hola mundo al iniciarse y Adiós al cerrarse nos quedaría así:

#include <ntddk.h>

void Salir(PDRIVER_OBJECT DriverObject)
{
   DbgPrint("Adiós");
}

NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
   DriverObject->DriverUnload=Salir;
DbgPrint("Hola mundo!!!");
return STATUS_SUCCESS;
}


Una vez echo esto ya podemos abrir el DebugView, le habilitamos la opción para capturar mensajes del Kernel y lo podemos ejecutar. Este ejemplo lo e probado yo mismo y pueden ejecutarlo en el PC, aunque es recomendable siempre hacer pruebas en una maquina virtual, ya que un error en el driver provocaría un reinicio de sistema.

2.2 Comunicación entre Modo kernel y modo usuario

Este tema ya es algo mas lioso. Hay varias formas de pasar información desde modo usuario a modo kernel y viceversa,  la que yo voy a utilizar es el metodo que utiliza la API DeviceIoControl.

Esto me permite enviar un mensaje desde modo usuario (MU desde ahora en adelante) hacia modo kernel (MK). Además, me retorna un puntero hacia un buffer de salida (de MK a MU) y la longitud de este, si la longitud es igual a 0 no hay datos, de lo contrario si.

La estructura donde se fundamenta la comunicación entre MU y MK es la estructura IRP. Para poder manejarla, en el driver tendremos que crear una funcion que maneje esta estructura. Esta función se declarará igual que declaramos la función de salida en el DriverEntry. Aqui un ejemplo:

NTSTATUS Control(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
//Codigo
}

NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
   DriverObject->DriverUnload=Salir;
   for(i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
   DriverObject->MajorFunction[i]=Control;

return STATUS_SUCESS;
}


Aquí declaramos esa estructura como control.

El for que hay en el programa es para indicarle que todas la funciones de control las redirija a esa función. Pueden ver todas las funciones aquí.

Para poder crear un handle desde MU hacia MK, necesitamos usar la API CreateFile, aunque antes tenemos que crear el objeto del driver. Para esto hacer esto se usa la API IoCreateDevice.

Si leen la información de esta API, veran que se le tiene que pasar una cadena en formato unicode, esto es importante, al igual que el paso que le sigue, el de crear un vinculo para poderse abrir desde MU. Este paso se hace con la API IoCreateSymbolicLink, al que se le pasa una cadena que sera usada en MU. Aqui un ejemplo de lo hablado hasta ahora.


//Variables globales
const WCHAR   Device[]=L"\\device\\driver5";
const WCHAR sLink[]=L"\\??\\midriver5";
UNICODE_STRING Dev,lnk;
//Fin variables globales

NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS s;
unsigned int i;

   DriverObject->DriverUnload=Salir;
   for(i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
   DriverObject->MajorFunction[i]=Control;

   RtlInitUnicodeString(&Dev,Device);
   RtlInitUnicodeString(&lnk,sLink);
   s=IoCreateDevice(DriverObject,0,&Dev,FILE_DEVICE_UNKNOWN,0,0,&DriverObject->DeviceObject);
   
if (NT_SUCCESS(s))
   {
s=IoCreateSymbolicLink(&lnk,&Dev);
if(!NT_SUCCESS(s))
   {
IoDeleteDevice(DriverObject->DeviceObject);
DbgPrint("Error Link");
}else
DbgPrint("Cargado");
}else
DbgPrint("Error IoCreate");

return  s;
}



Si no ha habido error, nos podemos centrar en la función control.

En la función de control, normalmente se analiza el tipo de mensaje que se transfiere con un IoControlCode, por ejemplo:

Hookear Datos --> 1
UnHookear --> 2

Esto se filtra mediante un switch/case. En el ejemplo usaremos un filtro para escribir que nos inventemos, yo le e llamado escribe. Para usarlo en la consola, tienen que agregar la librería winioctl.h.


NTSTATUS Control(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
   NTSTATUS            s=STATUS_SUCCESS;
   PIO_STACK_LOCATION  Stack;
unsigned int    escritos;
char *iBuffer;
   char *oBuffer;
   char *Mensaje = "Hola desde el kernel!";
   unsigned int Tam = sizeof("Hola desde el kernel!");

   Stack=IoGetCurrentIrpStackLocation(Irp);

switch(Stack->Parameters.DeviceIoControl.IoControlCode)
   {
case Escribe:
DbgPrint("Funcion escribir llamada");
   DbgPrint("Asociando buffers...");
   iBuffer = oBuffer = Irp->AssociatedIrp.SystemBuffer;
   if(oBuffer && oBuffer)
   {
DbgPrint("OK");
      if(Stack->Parameters.DeviceIoControl.InputBufferLength !=0)
  {
DbgPrint("Datos desde modo usuario: %s",iBuffer);

if(Stack->Parameters.DeviceIoControl.OutputBufferLength>= Tam)
           {
DbgPrint("Enviando datos...");
               RtlCopyMemory(oBuffer, Mensaje, Tam);
Irp->IoStatus.Information = Tam;
               s = STATUS_SUCCESS;
           }else{
DbgPrint("NO ENVIAMOS LOS DATOS");
Irp->IoStatus.Information = 0;
               s = STATUS_BUFFER_TOO_SMALL;
           }
      }
   }
else DbgPrint("ERROR");
break;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
   IoCompleteRequest(Irp, IO_NO_INCREMENT);
   return s;
}


Al inicio declaramos los buffers de entrara y salida y los mensajes que vamos a enviar a MU.

Stack=IoGetCurrentIrpStackLocation(Irp);

Esta linea sirve para poder localizar los datos que vamos a usar posteriormente, Stack esta declarada como un puntero a IO_STACK_LOCATION.

iBuffer = oBuffer = Irp->AssociatedIrp.SystemBuffer;

Assignamos los buffers de E/S, si no hay error proseguimos.


RtlCopyMemory(oBuffer, Mensaje, Tam);
Irp->IoStatus.Information = Tam;


En la primera linea copiamos los datos al buffer de salida, en la segunda, ajustamos el tamaño del buffer de salida, esto es importante, ya que si no se configura no se transmitiran datos.


Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return s;


Completamos y salimos.

Ahora vamos a ver la aplicación de consola en MU:


#include <stdio.h>
#include <windows.h>
#include <winioctl.h>

#define Escribe CTL_CODE(FILE_DEVICE_UNKNOWN, 0x00000001, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)

int main()
{
DWORD a;
HANDLE hDevice = CreateFile("\\\\.\\midriver5",GENERIC_READ | GENERIC_WRITE ,FILE_SHARE_READ | FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
char    iBuffer[30];
char    oBuffer[1024];

   if (hDevice!=INVALID_HANDLE_VALUE)
   {
printf("Conectado");
strcpy(iBuffer,"Hola desde Modo Usuario!!!");
if(DeviceIoControl(hDevice,(DWORD)Escribe,iBuffer,(DWORD)sizeof(iBuffer),(LPVOID)oBuffer,(DWORD)sizeof(oBuffer),&a,NULL)==true)
{
printf("\n%d Bytes\n%s\n%s",a,iBuffer,oBuffer);
}else
printf("0x%08x",GetLastError());
}

system("pause");
return 0;
}


La verdad es que no hay mucho que explicar de este código.

En este ejemplo se transfieren cadenas, pero se puede transferir todo lo que nosotros queramos.

Dicho esto doy por zanjado este segundo capítulo, si alguien tiene alguna duda comentenlo.

PD: Tengo que decir que parte de estos codigo son de lympex, de un codigo que publicó, lo e modificado un poco ya que usaba mas funciona, pero los nombres de las variables y eso no lo e cambiado.

Un Saludo  :)

"Todos los días perdemos una docena de genios en el anonimato. Y se van. Y nadie sabe de ellos, de su historia, de su peripecia, de lo que han hecho, de sus angustias, de sus alegrías. Pero al menos una docena de genios se van todos los días sin que sepamos de ellos". - Juan Antonio Cebrián

~~

#12
Muy buen rtuto Hendrix, me va a venir de lujo, solo un par de cosas, en la primera parte en enlace del DebugView está mal, es este:
http://download.sysinternals.com/Files/DebugView.zip

El enlace a la DDK compatible con win XP, 2000 y 2003 es este que viene bien que lo pongas que lo suyo me costó encontrarlo xDD
http://www.microsoft.com/whdc/DevTools/ddk/default.mspx

Por el resto todo perfecto, estoy leyéndome ahora la segunda parte ;)
Salu2

Hendrix

3. El Kernel de Windows

Una vez se esta en modo kernel tenemos que pensar un poco diferente de como lo haríamos en MU, tenemos que hacernos la idea de que nuestro driver no es un proceso, los procesos en MU tienen un espacio propio definido, y remarco el propio, ya que en MK es algo diferente, cierto que nuestro driver se aloja en una posición de memoria definida, pero al hacer llamadas a funciones, tenemos que saber que podemos leer y escribir en toda la memoria (luego veremos que en ciertas areas, se nos esta restringido escribir, pero modificando un registro lo podemos des habilitar). Esto equivale a que si por ejemplo, queremos modificar el contenido de una de las varias tablas que residen en el kernel de windows, lo podemos hacer sin necesidad de llamar a ninguna API ni nada de eso.

En este capítulo nos centraremos en una tabla en concreto, la System Service Descriptor Table (SSDT).

3.1 La SSDT o System Service Descriptor Table

La SSDT es usada por el Kernel para almacenar las direcciones de las API's nativas. Para hacer la transacción entre MU y MK se usa el SYSENTER (en XP, ya que en en las anteriores se utilizaba la interrupción 0x2E). Esto lo que hace es recibir el ordinal de la función y llama a la dirección que se encuentra en la SSDT pasandole los parámetros que se recibieron. Aunque esto no es muy importante, esta bien conocer lo que hace el SYSENTER.

Para que puedan tocar y ver la SSDT, les voy a enseñar este ejemplo que me paso Mek para ver la SSDT con el WinDbg.

Abren el WinDbg y lo ponen como Kernel Debug > local.

Una vez dentro, escribir esto:

Citarlkd> dd KeserviceDescriptorTable
80552fa0  80501b8c 00000000 0000011c 80502000

Esto nos da la dirección de KiServiceTable, que es un puntero hacia la SSDT, así que escribimos

Citarlkd> dd 80501b8c
80501b8c  80599948 805e6db6 805ea5fc 805e6de8
80501b9c  805ea636 805e6e1e 805ea67a 805ea6be
80501bac  8060bdfe 8060cb50 805e21b4 ae7ac81a
80501bbc  805cade6 805cad96 8060c424 805ab5ae
80501bcc  8060ba3c 8059ddbe 805a5a00 805cc8c4
80501bdc  804ff828 8060cb42 8056bcd6 8053500e
80501bec  806050d4 ae7acdc6 805eab36 80619e56
80501bfc  805ef028 8059a036 8061a0aa ae7ae82a

Aquí tenemos la SSDT, como vemos, la mayoría de valores empiezan por 80, ese valor es un valor superior al de la base del kernel (se puede sacar la base del Kernel escribiendo dc nt). Si hay valores que están mucho mas arriba (los que empiezan por ae en mi caso) significa que esta dirección esta hookeada. En mi caso es correcto, ya que tengo el Kaspersky y este hookea algunas API's en la SSDT.

La SSDT se encuentra guardada en el disco en el archivo ntoskrnl.exe, algunos programas que se encargan de restaurar la SSDT lo hacen desde hay.

Para el hookeo de la SSDT lo que tenemos que hacer es colocar en el array anterior la dirección de una función nuestra, para que nos llamen a nosotros en lugar de a la API nativa. El código que les paso ahora es muy conocido, esta en el libro Rootkits: Subverting the Windwos Kernel que a su vez fueron sacados del codigo del Regmon de Sysinternals y es realmente útil.

#define SYSTEMSERVICE(_func) \

  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_func+1)]

#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)

#define HOOK_SYSCALL(_Function, _Hook, _Orig )       \

        _Orig = (PVOID) InterlockedExchange( (PLONG) \

        &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

#define UNHOOK_SYSCALL(_Func, _Hook, _Orig )  \

        InterlockedExchange((PLONG)           \

        &MappedSystemCallTable[SYSCALL_INDEX(_Func)], (LONG) _Hook)


Para Hookear se utiliza la macro HOOK_SYSCALL y para eliminar el Hook el UNHOOK_SYSCALL.

El modo de uso es el siguiente:

HOOK_SYSCALL(API, NuestraFuncion, Direccióninicial);

El primer argumento es el nombre de la API, previamente declarada en el código, el segundo es la dirección hacia la función donde queremos redirecciónar la llamada, y la tercera es la dirección original de la API. (Conviene guardarla, ya que para restaurar el Hook la vamos a necesitar). Para el Unhook son los mismos argumentos pero con distinto orden:

UNHOOK_SYSCALL(API, Direccióninicial, NuestraFuncion);

Aunque si colocamos esto en nuestro dirver tal cual nos daría error, el error se corrige leyendo el siguiente sub-apartado.

3.2 Memoria protegida

La memoria en el sistema operativo esta dividida por paginas, como un libro. Algunas de estas paginas están protegidas por seguridad para que solamente sean de lectura. No todas las estructuras que se puedan modificar están protegidas de este modo, algunas no se tiene que modificar nada y manipularlas directamente. La SSDT esta alojada en una pagina con esta protección habilitada. Todo lo que tenemos que hacer para permitir escribir en esas paginas es modificar el registro CR0 que es el que se encarga de la protección de solo lectura.

El registro CR0 contiene un bit llamado write protect que es el encargado de señalar si una pagina es de solo lectura o no. Para eliminar esta propiedad tenemos que poner a cero este bit. El código en ensamblador para hacer esto es el siguiente:

Código (asm) [Seleccionar]
__asm

      {

            push eax

            mov  eax, CR0

            and  eax, 0FFFEFFFFh

            mov  CR0, eax

            pop  eax

      }


Una vez echas las modificaciones, tenemos que volver a colocar el bit WP tal y como estaba. Aquí el código en ensamblador:

Código (asm) [Seleccionar]
__asm

      {

            push eax

            mov  eax, CR0

            or   eax, NOT 0FFFEFFFFh

            mov  CR0, eax

            pop  eax

      }


Aunque este método es muy poco ortodoxo, hay un método que esta mejor documentado y es el que yo personalmente uso.

Este método utiliza una Memory Descriptor List (MDL). Básicamente lo que vamos a hacer sera modificar los flags de la MDL para habilitar la escritura. El código esta sacado del libro Rootkits para poder crear esa MDL.

// Declarations

#pragma pack(1)

typedef struct ServiceDescriptorEntry {

        unsigned int *ServiceTableBase;

        unsigned int *ServiceCounterTableBase;

        unsigned int NumberOfServices;

        unsigned char *ParamTableBase;

} SSDT_Entry;

#pragma pack()

__declspec(dllimport) SSDT_Entry KeServiceDescriptorTable;



PMDL  g_pmdlSystemCall;

PVOID *MappedSystemCallTable;

// Code

// save old system call locations



// Map the memory into our domain to change the permissions on // the MDL

g_pmdlSystemCall = MmCreateMdl(NULL,

                   KeServiceDescriptorTable.ServiceTableBase,

                   KeServiceDescriptorTable.NumberOfServices*4);

if(!g_pmdlSystemCall)

   return STATUS_UNSUCCESSFUL;

MmBuildMdlForNonPagedPool(g_pmdlSystemCall);

// Change the flags of the MDL

g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags |

                             MDL_MAPPED_TO_SYSTEM_VA;



MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);


Una vez echo esto ya podemos utilizar las macros para hookear y unhookear las API's.

Antes de descargar el driver tenemos que unhookear las API's y eliminar la MDL para que no nos de errores. Para eliminar la MDL podemos usar este código:

if(g_pmdlSystemCall)
   {
      MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
      IoFreeMdl(g_pmdlSystemCall);
   }


A continuación les pasare un código para hookear la API ZwOpenProcess, a fin de poder bloquear el acceso al proceso con el PID que le especifiquemos.

#include "ntddk.h"

typedef struct ServiceDescriptorEntry {
        unsigned int *ServiceTableBase;
        unsigned int *ServiceCounterTableBase;
        unsigned int NumberOfServices;
        unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
__declspec(dllimport)  ServiceDescriptorTableEntry_t KeServiceDescriptorTable;

#define SYSTEMSERVICE(_function)  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]

typedef DWORD (ULONG);
PMDL  g_pmdlSystemCall;
PVOID *MappedSystemCallTable;

#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)

#define HOOK_SYSCALL(_Function, _Hook, _Orig )  \
       _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

#define UNHOOK_SYSCALL(_Function, _Hook, _Orig )  \
       InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

//Declaramos la API para poder trabajar con ella.
NTSYSAPI NTSTATUS NTAPI ZwOpenProcess (OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
   

typedef NTSTATUS (*TypZwOpenProc)(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
TypZwOpenProc ZwOpenProcessIni;

NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)
{
   HANDLE PID;

   __try //Utilizamos el bloque try para evitar BSOD
   {
PID = ClientId->UniqueProcess;
}

__except(EXCEPTION_EXECUTE_HANDLER)
{
return STATUS_INVALID_PARAMETER;
}

DbgPrint("PID: 0x%x",PID);

//Verificamos el pid
if (PID == (HANDLE)1234) return STATUS_ACCESS_DENIED; //Retornamos acceso denegado
else return ZwOpenProcessIni(ProcessHandle, DesiredAccess,ObjectAttributes, ClientId); //Llamamos a la API nativa y retornamos el resultado correcto
}

VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
   DbgPrint("Descargando driver...");

   //Unhookeamos
   UNHOOK_SYSCALL( ZwOpenProcess, ZwOpenProcessIni, NewZwOpenProcess );

   //Eliminamos la MDL
   if(g_pmdlSystemCall)
   {
      MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
      IoFreeMdl(g_pmdlSystemCall);
   }
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING theRegistryPath)
{
   DriverObject->DriverUnload  = OnUnload;
   
   DbgPrint("Driver cargado");
   ZwOpenProcessIni =(TypZwOpenProc)(SYSTEMSERVICE(ZwOpenProcess));

   //Creamos la MDL para deshabilitar la protección de memoria
   g_pmdlSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
   if(!g_pmdlSystemCall)
      return STATUS_UNSUCCESSFUL;

   MmBuildMdlForNonPagedPool(g_pmdlSystemCall);

   g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;

   MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);

   
   DbgPrint("Hookeando...");
   HOOK_SYSCALL( ZwOpenProcess, NewZwOpenProcess, ZwOpenProcessIni );
   
   return STATUS_SUCCESS;
}


El código bloquea el acceso al proceso cuyo pid sea 1234, evidentemente podeis modificarlo para bloquear el que querais.

Echo esto, me veo obligado a modificar el indice para incluir el capítulo 4 en este, asi que se va a reducir en un capítulo.

Un Saludo  :)

"Todos los días perdemos una docena de genios en el anonimato. Y se van. Y nadie sabe de ellos, de su historia, de su peripecia, de lo que han hecho, de sus angustias, de sus alegrías. Pero al menos una docena de genios se van todos los días sin que sepamos de ellos". - Juan Antonio Cebrián

Karcrack

Muy buen manual :o, al intentar compilar el ultimo codigo (El de bloquear el acceso a un PID) me sale esto... asi que supongo que algo falla :xD:

CitarBUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Object root set to: ==> objfre_wxp_x86
BUILD: Compile and Link for i386
BUILD: Examining c:\documents and settings\administrador\escritorio directory fo
r files to compile.
BUILD: Compiling (NoSync) c:\documents and settings\administrador\escritorio dir
ectory
errors in directory c:\documents and settings\administrador\escritorio
NMAKE : warning U4006: special macro undefined : '$<'
Compiling - objfre_wxp_x86\i386 for all platforms
NMAKE : warning U4006: special macro undefined : '$<'
Compiling - objfre_wxp_x86\i386 for all platforms
NMAKE : warning U4006: special macro undefined : '$<'
Compiling - objfre_wxp_x86\i386 for all platforms
BUILD: Compile errors: not linking c:\documents and settings\administrador\escri
torio directory
BUILD: Done

    3 files compiled - 3 Errors

Saludos y gracias ;)

~~

Pon los archivos en una ruta mas  corta, por ejemplo C:\rootkit o algo por el estilo y prueba :P

sch3m4

Cita de: E0N en 16 Octubre 2008, 00:41 AM
Pon los archivos en una ruta mas  corta, por ejemplo C:\rootkit o algo por el estilo y prueba :P

la ruta de trabajo no debe contener espacios
SafetyBits

"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.(..

Rozor

Hendrix mamoencete podrias haber avisado de la publicacion ajjajaj.

Esta guapo :D

No sabia yo que ahi se podia usar cr0 tambien se puede usar cli y sti ¿?.


A por cierto problemas de compilacion y creo que tengo todo bien :S

build -cZ
BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Compile and Link for i386
BUILD: Done

:/

Thank :)
out in the streets they call it murder....

Hendrix

Cita de: Rozor en 16 Octubre 2008, 14:53 PM
A por cierto problemas de compilacion y creo que tengo todo bien :S

build -cZ
BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Compile and Link for i386
BUILD: Done

Cita de: sch3m4 en 16 Octubre 2008, 03:33 AM
la ruta de trabajo no debe contener espacios

:D
"Todos los días perdemos una docena de genios en el anonimato. Y se van. Y nadie sabe de ellos, de su historia, de su peripecia, de lo que han hecho, de sus angustias, de sus alegrías. Pero al menos una docena de genios se van todos los días sin que sepamos de ellos". - Juan Antonio Cebrián

Karcrack

#19
Cita de: Hendrix en 16 Octubre 2008, 18:21 PM
Cita de: Rozor en 16 Octubre 2008, 14:53 PM
A por cierto problemas de compilacion y creo que tengo todo bien :S

build -cZ
BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Compile and Link for i386
BUILD: Done

Cita de: sch3m4 en 16 Octubre 2008, 03:33 AM
la ruta de trabajo no debe contener espacios

:D

Me da el mismo problema, y no tiene ningun espacio, la ruta es C:\Drivers

Y me devuelve esto:

C:\Drivers>build -cZ
BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Object root set to: ==> objfre_wxp_x86
BUILD: Compile and Link for i386
BUILD: Done


Tal vez se deba a la arquitectura de mi PC? Es 32bits... Win2 XP SP3...

No se a que se debe :-\.. justo ahora que habia hecho mi primer intento de driver :laugh:





FALLO SOLUCIONADO
Bueno, el problema era que tenia los ficheros SOURCES y MAKEFILE mal :¬¬ :xD
Perdon por las molestias :-[ :xD

Saludos ;D