Red de conocimiento informático - Conocimiento del nombre de dominio - ¿Qué tal escribir un controlador para WINDOWS?

¿Qué tal escribir un controlador para WINDOWS?

Para alguien que escribe programas con frecuencia, escribir un controlador no es una tarea difícil. Debido a que hay muchos códigos ya preparados en Internet, si desea implementar una determinada función, simplemente presione Ctrl C y Ctrl V para resolver el problema. Pero si el controlador escrito se puede cargar en el kernel es otra cuestión. Para ser precisos, si puede existir en el disco duro de otra persona es otra cuestión.

Debido a que muchos software antivirus (especialmente el software antivirus no técnico como 360) eliminarán directamente los archivos con el sufijo sys cuando los vean,

ni siquiera llamarán a NtLoadDriver. Para software normal, simplemente describe la solución

. Pero con los programas maliciosos no se puede dar una declaración. Como resultado, muchos autores de malware han tomado el camino contrario y han aprovechado los controladores firmados digitalmente y escritos por grandes empresas para hacer el trabajo sucio.

Algunas personas dicen, ¿cómo se puede utilizar a un buen conductor de una gran empresa para hacer cosas malas? En realidad, esto es fácil de entender.

Muchos software de seguridad o optimización del sistema, e incluso software independiente del sistema, como Thunderbolt, vienen con controladores.

Estos drivers tienen cierto grado de versatilidad. El internauta q_lai_a_qu dijo en su blog: "ComputerZ.sys... está bien

Reverse es el controlador de LU Master. ¡Descubrí que este controlador es completamente funcional y no tiene verificación de llamadas! Puedes leerlo y escriba el registro Msr

, y también puede leer y escribir el puerto con comandos de entrada y salida, así como la longitud completa de datos char/short/long "..Esto es

<. p> personal! Por favor, haga su propia suposición sobre su credibilidad. Aquí hay un ejemplo más creíble: un virus usó una vez 360's

AntiRK.dll para eliminar archivos de software antivirus (busque en Google "360 antirk.dll", encontrará resultados inesperados.

AntiRK.dll no es un controlador, pero se explota ilegalmente). Los virus que pueden descifrar el software antivirus se han considerado infantiles.

De hecho, ¡el uso de ciertos controladores también puede dañar el hardware! He estado investigando el hardware de mi computadora portátil y algunos amigos míos me recomendaron algunos programas: SetFSB, ThrottleStop, NvFlash y WinFlash que pueden modificar la frecuencia externa de la CPU, configurar el multiplicador de la CPU y ajustar el voltaje de la CPU. Son software para modificar el FSB de la CPU, configurar el multiplicador de la CPU (ajustar el voltaje de la CPU), leer y escribir el BIOS de la tarjeta gráfica y leer y escribir el BIOS de la placa base. En resumen,

Todos son compatibles con NT x86/x64 y sus controladores están correctamente firmados digitalmente (especialmente los dos últimos, que están firmados digitalmente por Nvidia y Asus respectivamente).

Lo más importante es que sus controladores no añaden flores ni conchas, ni tienen llamadores de suma de comprobación.

Si aprovechas estos controladores y haces un poco de ingeniería inversa, puedes crear virus destructivos (lo siguiente es un extracto de mi publicación en el

Foro de programación Crystal de Purple Water):

1.SetFSB puede ajustar el FSB del procesador. Si ajusta directamente el FSB a 600 MHz, la computadora se apagará instantáneamente, lo que puede

dañar la CPU o la placa base;

p>

2.ThrottleStop puede ajustar el multiplicador de la CPU (si la CPU no bloquea el multiplicador, si ajusta directamente el multiplicador a 31,

la computadora). se apagará instantáneamente, lo que puede dañar la CPU o la placa base; ThrottleStop también puede ajustar el voltaje del núcleo de la CPU. Si el voltaje del núcleo de la CPU se ajusta a 3 V, quemará directamente la CPU. o incluso la placa base;

3. NvFlash, WinFlash y otro software pueden leer y escribir BIOS directamente (BIOS de tarjeta gráfica y BIOS de placa base), podemos escribir todo

BIOS a cero. ;

4. Si crea un virus, primero escribirá el BIOS de la tarjeta gráfica y el BIOS de la placa base, y luego ajustará el voltaje para quemar la tarjeta gráfica y la CPU

(la placa base pueden dañarse juntos);

Solución

Se puede ver que la persona que llama no está verificada. De hecho, existen grandes peligros ocultos en el conductor. Recientemente, una escuela me encargó crear un software que requería un controlador (el controlador estaría firmado digitalmente). Para evitar que ocurra la tragedia anterior, decidí resolver el problema de cómo evitar que el controlador sea explotado maliciosamente antes de escribirlo oficialmente. Una vez hice esta pregunta en el foro de programación de Amethyst y escuché varias respuestas, pero se puede dividir aproximadamente en tres categorías: la primera es la verificación de mensajes, como la aplicación

Secuencia para El controlador envía un mensaje para verificar si el mensaje es propio; el segundo es empaquetar, como agregar firmas digitales a controladores y aplicaciones

.

El segundo es la protección contra shells, como agregar un shell muy poderoso al controlador y la aplicación, y usar VMP para cifrar la parte de comunicación (similar al método de XueTr que otros han propuesto aplicaciones híbridas, que combinan<); /p>

el primer y segundo enfoque.

Estas tres ideas parecen buenas, pero no creo que sean factibles.

El primero: otros solo necesitan revertir todos los controladores.

El segundo: aunque la protección VMP y la adición de una capa protectora hacen que sea difícil de romper, no hace que sea más fácil de romper. crack se vuelve imposible.

VMP y Shell reducirán la eficiencia de ejecución del programa, lo cual no me gusta mucho. Lo peor de todo es que el software antivirus informará sobre todos los programas con cubiertas protectoras (incluso UPX) y VMP, lo cual no vale la pena. Entonces pensé en la tercera forma: verificar las características

de la persona que llama. Si coincide, se ejecuta la instrucción de función; de lo contrario, no se ejecuta. ¿Cómo calibrar el código de firma de la persona que llama? Mucha gente piensa

que se puede utilizar CRC32 o MD5, pero yo tengo mis propias ideas.

Mi idea es diseñar mi propio algoritmo de verificación con las siguientes reglas: 1. Obtener el EPROCESS de la persona que llama. Obtenerlo a través de la ruta EPROCESS de la persona que llama. al archivo de la persona que llama

3. Obtenga todo el contenido del archivo de la persona que llama y colóquelo en el búfer de bytes

4. Sume y reste todos los elementos en el búfer (fb1 fb2 - fb3). ...) para obtener y1

5. Aísle todos los elementos en el búfer en secuencia (0 XOR fb1 XOR fb2 XOR fb3... Si desea obtener y2, puede usar PsGetCurrentProcess() para comparar. y1 e y2 con los valores calculados. Si son iguales, el código de función se puede ejecutar. Si no son iguales, el código de función no se puede

ejecutar

Utilice PsGetCurrentProcess. () para obtener el EPROCESS de la persona que llama y obtener la ruta del archivo de la persona que llama

La ruta es más problemática. Puede usar mi PsGetCurrentProcess() anterior.

La ruta es más problemática. Puede usar el código que compré de un experto antes (está encapsulado en una función y es fácil de llamar):

// Obtenga la ruta completa del proceso según. EPROCESS

VOID GetFullPathByEprocess (proceso electrónico ULONG, PCHAR ProcessImageName)

{

Objeto ULONG Buffer = NULL

FilePath.Length = 0;

* ProcessImageName = 0;

//Eprocess-gt; secciónobject(offset_SectionObject)

if(MmIsAddressValid((PULONG)(eprocess offset_SectionObject) ))

{

object=(*(PULONG)(eprocess offset_SectionObject));

//KdPrint(("[GetProcessFileName] objeto de sección: 0xx\ n", objeto"));

p>

if(MmIsAddressValid((PULONG)((ULONG)objeto 0x014)))

{

object=*(PULONG)((ULONG) objeto 0x014))

//KdPrint(("[GetProcessFileName] Segmento: 0xx\n", objeto)

if (MmIsAddressValid((PULONG)((ULONG)objeto 0x0)))

{

objeto=*(PULONG)((ULONG_PTR)objeto 0x0);

//KdPrint(("[GetProcessFileName]

ControlAera: 0xx\n", objeto));

if(MmIsAddressValid((PULONG)((ULONG)objeto 0x024)) )

{

objeto=*(PULONG)((ULONG)objeto 0x024);

if (NtBuildNumber gt;= 6000) objeto=((ULONG )objeto amp;

0xffffff8);

//KdPrint(("[GetProcessFileName]

FilePointer: 0xx \n", objeto));

}

else

regresar

}

else

regresar

else

regresar; p>

}

else

regresar

}

else

regresar

else

regresar; p>

}

else

return;

FileObject=(PFILE_OBJECT)objeto;

FilePath.Buffer = ExAllocatePool( paginadoP

ool, 0x200);

FilePath.MaximumLength = 0x200;

//KdPrint(("[GetProcessFileName]

FilePointer: wZ\n", amp; FilePointer-gt;FileName));

ObReferenceObjectByPointer((PVOID)FileObject, 0, NULL, KernelMode);

RtlVolumeDeviceToDosName(FileObject-gt;DeviceObject, amp; DosName);

RtlCopyUnicodeString(amp;FilePath,amp;DosName);

RtlAppendUnicodeStringToString(amp;FilePath,amp;FileObject-gt;FileName);

ObDereferenceObject(FileObject) ;

RtlUnicodeStringToAnsiString(y AnsiString, y FilePath.TRUE);

if (AnsiString.Length >= 216)

{

memcpy(ProcessImageName, AnsiString.Buffer, 0x100u);

*(ProcessImageName 215) = 0;

}

else

{

memcpy(ProcessImageName, AnsiString.Buffer, AnsiString.Length);

ProcessImageName[AnsiString.Length] = 0;

}

RtlFreeAnsiString(amp; AnsiString);

ExFreePool(DosName.Buffer);

ExFreePool(FilePath.Buffer);

}

El código anterior requiere tres códigos físicos, que son NtBuildNumber (número de versión del sistema), el desplazamiento del elemento

SectionObject y el elemento UniqueProcessId en EPROCESS. Mi sistema operativo de prueba es Windows 2003, así que

Lo defino en el código de la siguiente manera:

#define offset_SectionObject 0x124

#define offset_UniqueProcessId 0x94

ULONG NtBuildNumber=3790;

Una vez que tenga la ruta del proceso, verifique el código de la función.

Como el proceso es claro, el código se dará directamente:

VOID CalcChar(PUNICODE_STRING logFileUnicodeString, LONG *XorChar, LONG

*AnSChar)

{< / p>

OBJECT _ATTRIBUTES objectAttributes;

IO_STATUS_BLOCK iostatus;

HANDLE hfile;

NTSTATUS ntStatus;

FILE_STANDARD_ INFORMACIÓN fsi

PUCHAR pBuffer;

ULONG i=0, y1=0, y2=0;

//Inicializar atributos de objeto

Inicializar atributos de objeto ( amp.objectAttributes,

InitializeObjectAttributes(amp.objectAttributes,

InitializeObjectAttributes(amp.objectAttributes(amp.objectAttributes)/0))objectAttributes,

logFileUnicodeString,

OBJ_CASE_INSENSITIVE, //distingue entre mayúsculas y minúsculas

NULL,

NULL);

//crear archivo

ntStatus = ZwCreateFile(amp;hfile,

GENERIC_READ,

amp;objectAttributes,

amp;iostatus,

NULL,

FILE_ ATTRIBUTE_NORMAL,

FILE_SHARE_READ,

FILE_OPEN, //crea el archivo incluso si existe

FILE_SYNCHRONOUS_IO_NONALERT,

NULL,

0);

if (!NT_SUCCESS(ntStatus))

{

dprintf("El archivo no está existir ! \n");

return;

}

Leer longitud del archivo

ntStatus = ZwQueryInformationFile(hfile,

&iostatus,

fsi,

sizeof(FILE_STANDARD_INFORMATION),

FileStandardInformation);

dprintf(" El programa quiere leer d bytes\n", fsi.EndOfFile.QuadPart);

//Asignar un búfer para leer archivos

pBuffer = (PUCHAR)ExAllocatePool(PaggedPool,

(LONG)fsi.EndOfFile.QuadPart);

PBuffer = (PUCHAR)ExAllocatePool(PaggedPool,

(LONG)fsi.EndOfFile.QuadPart);

else

y2=y2-(LONG)(*(pBuffer i));

}

*AnSChar=y2;

/ Cerrar el identificador del archivo

ZwClose(hfile);

/ Liberar el búfer

ExFreePool(pBuffer);

}

La siguiente función que se llamará. Necesitamos escribir una función VerifyCaller, que debe garantizar dos valores

en el controlador, dos valores característicos de la persona que llama legítima.

Para facilitar el cálculo de estos dos valores propios, escribí una

aplicación cuyo código principal es el siguiente:

Opción explícita

Función privada ReadFile(ByVal strFileName como cadena, ByVal opcional

Función privada ReadFile(ByVal strFileName como cadena, ByVal opcional

lngStartPos como largo = 1, ByVallngFileSize opcional como largo = -1) como Byte()

Atenuar FilNum siempre

FilNum = FreeFile

Abrir strFileName para binario como #FilNum

Si lngFileSize = -1 entonces

ReDim ReadFile(LOF(FilNum) - lngStartPos)

Else

ReDim ReadFile(lngFileSize - 1)

Finalizar si

Obtener #FilNum, lngStartPos, ReadFile

Cerrar #FilNum

Función final

Función privada WriteFile(ByVal strFileName como cadena, bytData() como byte,

ByVal lngStartPos opcional siempre que = -1, ByVal opcional sobrescribir como booleano =

True)

En caso de error, Ir a erx

Dim FilNum siempre que

FilNum = FreeFile

Si OverWrite = True y Dir(strFileName) lt;gt "" Entonces

Elimine strFileName

<; p>Finalizar si

Abrir strFileName para binario como #FilNum

Si lngStartPos = -1 entonces

Poner #FilNum,LOF(FilNum),LOF(FilNum ), LOF(FilNum).LOF(FilNum) 1, bytData

Else

Poner #FilNum, lngStartPos, bytData

End If

Cerrar #FilNum

erx:

Finalizar función

Subcomando privado1_Click()

Atenuar buff() As Byte, i As Long, y As Long, ub As Long

'text1.text es el nombre del archivo

buff = ReadFile (Text1.Text, 1, -1)

u

b = UBound(buff)

'calc xor char

y = 0

Para i = 0 Para ub

y = y Xor buff(i)

Siguiente

Text2.Text = CLng(y)

DoEvents

'calc add/sub char

y = 0

Para i = 0 Para ub

Si i Mod 2 = 0 Entonces

y = y CLng(buff( i))

Else

y = y - CLng(buff(i))

Finalizar si

Siguiente

Text3.Text = CLng(y)

End Sub

Sub privado Form_Load()

Me.Icon = LoadPicture("")

End Sub

El código VerifyCaller en el controlador es el siguiente:

LONG VerifyCaller(void)

{

PEPROCESS cur_ep;

char cur_pp[260];

char *nt_cur_pp;

ANSI_STRING asCur_pp;

UNICODE_STRING usCur_pp;

LONG xorc, ansc;

cur_ep= PsGetCurrentProcess();

GetFullPathByEprocess((ULONG)cur_ep, cur_pp

// antes el nombre del archivo Agregar \?\\p>

nt_cur_pp=cs("\\\?\\,cur_pp);

DbgPrint("s",nt_cur_pp);

RtlInitAnsiString(amp;asCur_pp, nt_cur_pp);

RtlAnsiStringToUnicodeString(amp;usCur_pp, amp;asCur_pp, TRUE);

DbgPrint("wZ", amp;usCur_pp );

CalcChar(amp;usCur_pp, amp;xorc, amp;ansc);

DbgPrint("XorChar: DbgPrint("XorChar: ld; AnSChar: ld", xorc, ansc) ;

// ¡Este es un código de función precalculado por el proceso legal y debe solidificarse en el controlador!

if(xorc==186 amp; amp; ansc==136176)

devuelve 1;

else

devuelve 0;

}

Antes de ejecutar cada función de la función DispatchIoctl, llame a VerifyCaller() para verificar la persona que llama:

switch(uIoControlCode)

{

caso IOCTL_VERIFY:

{

DbgP

rint(" [MyDriver] DispatchIoctl - IOCTL_VERIFY");

if(VerifyCaller()==1)

DbgPrint(" [MyDriver] {IOCTL_VERIFY} ¡Ahora ejecute el código de función! " );

else

DbgPrint("[MyDriver] {IOCTL_VERIFY} ¡Eres una persona que llama ilegalmente!");

status = STATUS_SUCCESS;

descanso;

}

/Omitido a continuación

}

Ejecutar la prueba

3 Primero copie el programa de llamadas legales, el programa de llamadas ilegales (use eXeScope para parchear aleatoriamente el programa de llamadas legales,

por ejemplo, elimine la información de la versión del programa) y el controlador a la máquina virtual

. p>

4. Utilice llamadas legales El programa carga el controlador y lo ejecuta

5. Utilice una llamada ilegal para cargar el controlador y ejecutarlo

6. situaciones en DbgView

Cuando la persona que llama es legal Cuándo:

Cuando la persona que llama es ilegal:

Escrito al final

Al final Al final de este artículo, debo reiterar la verificación de la persona que llama

El código es útil. ¿Por qué? Porque nadie puede parchear a un conductor con una firma digital oficial (una vez que el conductor está parcheado, la firma deja de ser válida, como una mujer cuya virginidad ha sido rota, sin valor. Esta metáfora es aunque vulgar, pero muy

adecuado). Y los conductores sin firmar no valen nada. Incluso si otras personas quieren usarlo, pueden colocar el controlador en

IDA y aparecerá el código.