Red de conocimiento informático - Consumibles informáticos - ¿Hay algún virus en el controlador?

¿Hay algún virus en el controlador?

Para una persona que escribe programas con frecuencia, escribir controladores 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 use Ctrl C y Ctrl V para resolver el problema. Pero si el controlador se puede cargar en el kernel después de escribirlo 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 los no técnicos como 360) eliminan archivos con el sufijo sys cuando los ven.

Ni siquiera hay posibilidad de llamar a NtLoadDriver. Para software general, basta con dar una declaración explicando la solución

. Pero no se puede hacer ninguna declaración sobre los programas maliciosos. Por lo tanto, muchos autores de malware encuentran otra forma de hacer cosas malas mediante el uso de controladores firmados digitalmente y escritos por grandes empresas.

Algunas personas preguntan: ¿cómo se pueden utilizar los factores que las grandes empresas hacen bien para hacer cosas malas? De hecho, esto es algo muy fácil de entender.

Muchos software de seguridad o optimización del sistema, e incluso software no relacionado con el sistema (como Thunder), vienen con controladores.

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

Descubrí que era el controlador de Master Lu. ¡Descubrí que este controlador tiene funciones completas y no tiene verificación de llamadas! Ambos pueden leer y escriba Msr

Registro, también puede usar instrucciones de entrada y salida para leer y escribir puertos, ¡y la longitud de datos char/short/long estará completa! Esta es

una declaración personal, por favor haga su propio juicio sobre la credibilidad. Aquí hay un ejemplo más creíble: un virus usó AntiRK.dll de 360 ​​para eliminar archivos de software antivirus (busque en Google "360 antirk.dll", se sorprenderá)

Aunque AntiRK. .dll no es un controlador, se ha utilizado ilegalmente). Los virus que dañan el software antivirus ya se consideran infantiles.

De hecho, ¡algunos controladores también pueden dañar el hardware! Recientemente estuve jugueteando con el hardware de mi computadora portátil y los internautas del "Friends Club" me recomendaron varios programas: SetFSB, ThrottleStop, NvFlash y WinFlash. Son software para modificar el FSB de la CPU, configurar el multiplicador de la CPU (que puede 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. Para resumir sus características en una palabra,

Todos soportan NT x86/x64, y sus controladores tienen firmas digitales formales (especialmente los dos últimos, que tienen las firmas digitales de NVIDIA y ASUS respectivamente).

Lo más importante es que sus controladores no están empaquetados ni verificados.

Si usas estos controladores y agregas un poco de conocimiento inverso, puedes crear virus destructivos (lo siguiente es). extraído de mi publicación en Zishui

Foro de programación Jing):

1.SetFSB puede ajustar el FSB del procesador. Si configura directamente el ajuste FSB a 600MHz, la computadora lo hará instantáneamente. pantalla negra, que puede

dañar la CPU o la placa base;

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 es. ajustado a 3V puede quemar directamente la CPU o incluso la placa base

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

4. Si crea un virus, primero escriba el BIOS de la tarjeta gráfica y la BIOS de la placa base defectuosos, y luego queme la tarjeta gráfica y la CPU ajustando el voltaje

(puede dañar el placa base juntas);

Solución

Se puede ver que el controlador sin verificar a la persona que llama es realmente dañino. Recientemente, la universidad me encargó la creación de un software que requiere un controlador (el controlador estará firmado digitalmente). Para evitar que ocurra la tragedia anterior, decidí descubrir cómo evitar que mi propio controlador se use maliciosamente antes de escribirlo oficialmente. Ya hice esta pregunta en el Foro de programación de Amethyst.

Las respuestas de los internautas fueron variadas, pero se pueden dividir aproximadamente en tres categorías: la primera categoría es la verificación de información, como aplicaciones

El programa envía un mensaje al controlador para verificar que es "uno de los nuestros"; el segundo tipo es la protección contra bombardeos, como agregar un shell muy fuerte y difícil de eliminar al controlador y a la aplicación, y usar. Cifrado VMP Parte de comunicación (similar al enfoque de XueTr) otros han propuesto aplicaciones híbridas que combinan las prácticas de la primera y segunda categoría.

Estas tres ideas me parecen buenas, pero creo que son inapropiadas. El primero: otros solo necesitan aplicar ingeniería inversa a todos los controladores. El segundo: aunque la protección VMP y la cubierta protectora hacen que sea difícil descifrar, no lo hace imposible. Además

VMP y el shell protector pueden reducir la eficiencia de la ejecución del programa, lo cual no me gusta mucho. Lo más repugnante es que el software antivirus informará sobre los virus a los programas empaquetados (incluso UPX) y VMP, lo que no vale la pena. Entonces se me ocurrió la tercera idea: verificar las características

de la persona que llama. Si coincide, se ejecutará la declaración de función; de lo contrario, no se ejecutará. ¿Cómo verificar la firma de la persona que llama? Mucha gente piensa en utilizar CRC32 o MD5. No es imposible usarlos, pero todavía tengo mis propias ideas.

Mi idea es diseñar un algoritmo de verificación por mí mismo. Sus reglas son las siguientes:

1. Obtener el EPROCESS de la persona que llama

2. Pasar la persona que llama. EPROCESS obtiene la ruta del archivo de la persona que llama.

3. Obtiene el contenido completo del archivo de la persona que llama y lo coloca en el buff de matriz de bytes

4. Agrega todos los elementos del buff en secuencia Resta (fb1 fb2 - fb3. ..) para obtener y1

5. XOR todos los elementos del beneficio en secuencia (0 XOR fb1 XOR fb2 XOR fb3...) para obtener y2

Compare y1 e y2. con los valores calculados si son iguales, se ejecutará el código de función. Si no son iguales, no se ejecutará el código de función.

Ejecutar el código de función

Obtener el EPROCESS de la persona que llama. Simplemente use PsGetCurrentProcess() directamente. Obtener la ruta del archivo de la persona que llama

es problemático. Puede usar el código que compré de un experto antes (ya encapsulado como una función para facilitar la llamada):

//Obtener la ruta completa del proceso basada en EPROCESS

VOID GetFullPathByEprocess(ULONG eprocess, PCHAR ProcessImageName)

{

ULONG objeto;

PFILE_OBJECT FileObject;

UNICODE_STRING FilePath;

UNICODE_STRING DosName;

STRING AnsiString;

FileObject = NULL;

FilePath.Buffer = NULL;

FilePath.Length = 0;

*ProcessImageName = 0;

// Eprocess-gt; objeto de sección(offset_SectionObject)

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

{

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

p>

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

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

{

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

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

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

{

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

//KdPrint(("[GetProcessFileName]

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

if(MmIsAddressVali

d((PULONG)((ULONG)objeto 0x024)))

{

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

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

0xfffffff8);

//KdPrint(("[GetProcessFileName]

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

}

else

return;

}

de lo contrario

regresar;

}

de lo contrario

regresar;

}

else

return;

FileObject=(PFILE_OBJECT)objeto;

FilePath.Buffer = ExAllocatePool(PaggedPool, 0x200);

FilePath.MaximumLength = 0x200;

//KdPrint(("[GetProcessFileName]

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

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

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

RtlCopyUnicodeString(amp; FilePath, amp; DosName);

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

ObDereferenceObject(FileObject);

RtlUnicodeStringToAnsiString(amp; AnsiString, & FilePath, TRUE);

if (AnsiString.Length >= 216)

{

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

*(ProcessImageName 215) = 0;

}

else

{

memcpy( ProcessImageName, AnsiString. Búfer, AnsiString.Length);

ProcessImageName[AnsiString.Length] = 0;

}

RtlFreeAnsiString(amp; AnsiString);

ExFreePool(DosName.Buffer);

ExFreePool(FilePath.Buff

er);

}

El código anterior requiere tres códigos físicos, a saber, NtBuildNumber (número de versión del sistema),

elementoSectionObject y elemento UniqueProcessId en el desplazamiento de EPROCESS. El sistema operativo que probé fue Windows 2003. Entonces

Lo definí de la siguiente manera en el código:

#define offset_SectionObject 0x124

#define offset_UniqueProcessId 0x94

ULONG NtBuildNumber=3790 ;

Después de obtener la ruta del proceso, verifique la firma.

Como el proceso se ha explicado claramente, el código se proporciona directamente:

VOID CalcChar(PUNICODE_STRING logFileUnicodeString, LONG *XorChar, LONG

*AnSChar)

{

OBJECT_ATTRIBUTES objectAttributes;

IO_STATUS_BLOCK iostatus;

HANDLE hfile;

NTSTATUS ntStatus;

FILE_STANDARD_INFORMATION fsi;

PUCHAR pBuffer;

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

//Inicializar atributos de objeto

Inicializar atributos de objeto( amp ;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, //Crear incluso si el archivo existe

p>

FILE_SYNCHRONOUS_IO_NONALERT,

NULL,

0 );

if (!NT_SUCCESS(ntStatus))

{

dprintf("¡El archivo no existe!\n");

return;

}

//Leer la longitud del archivo.

ntStatus = ZwQueryInformationFile(hfile,

amp;iostatus,

amp;fsi,

tamañoof(FILE_STANDARD_INFORMATION),

FileStandardInformation);

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

//Asignar el búfer del archivo de lectura

pBuffer = (PUCHAR)ExAllocatePool(PaggedPool,

(LONG)fsi.EndOfFile.QuadPart);

//Leer archivo

ZwReadFile(hfile, NULL,

NULL, NULL,

amp;iostatus,

pBuffer,

(LONG )fsi. EndOfFile.QuadPart,

NULL, NULL);

dprintf("Th);

El programa realmente leyó d bytes\n",iostatus.Information);

//Cálculo XOR

for(i=0;ilt;iostatus.Information;i)

y1=y1^(LONG)(*(pBuffer i));

*XorChar=y1;

//Cálculo de sumas y restas

for(i=0; ilt; iostatus.Information; i )

{

if(i2==0)

y2=y2 (LARGO) (*(pBuffer i));

else

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

}

*AnSChar=y2;

//Cerrar el identificador del archivo

ZwClose(hfile);

//Liberar el búfer

ExFreePool(pBuffer);

}

El siguiente paso es llamarlo. Necesitamos escribir una función VerifyCaller, y hay dos valores requeridos en esta función<. /p>

Solidificados en el conductor, son los dos valores característicos de las personas que llaman legalmente.

Para facilitar el cálculo de estos dos valores característicos, escribí especialmente una aplicación. El código principal es el siguiente:

Opción explícita

Función privada ReadFile(ByVal strFileName As String, Opcional ByVal

lngStartPos Mientras = 1, Opcional ByVallngFileSize Mientras = -1) Como Byte()

Dim FilNum Mientras

FilNum = FreeFile

Abrir strFileName para binario como #FilNum

Si lngFileSize = -1 entonces

ReDim ReadFile(LOF(FilNum) - lngStartPos)

De lo contrario

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 mientras = -1, ByVal sobreescritura opcional como booleano =

Verdadero)

En caso de error, Ir a erx

Atenuar FilNum mientras

FilNum = FreeFile

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

Eliminar strFileName

Finalizar si

Abrir strFileName para binario como #FilNum

Si lngStartPos = -1 entonces

Pon #FilNum, LOF(FilNum) 1, bytData

De lo contrario

Pon #FilNum, lngStartPos, bytData

Finalizar si

Cerrar #FilNum

erx:

Finalizar función

Subcomando privado1_Click( )

Dim 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)

ub = UBound(buff)

'calc xor char

y = 0

Para i = 0 A ub

y = y Xor buff(i)

Siguiente

p>

Text2.Text = CLng(y)

DoEvents

'calc add/sub char

y = 0

Para i = 0 To ub

Si i Mod 2 = 0 Entonces

y = y CLng(buff(i))

De lo contrario

y = y - CLng(buff(i))

Finalizar si

Siguiente

Texto3.Text = CLng(y)

End Sub

Sub privado Form_Load()

Me.Icon = LoadPicture("")

End Sub

Controlador El código de VerifyCaller 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);

//Agrega \?\ delante del nombre del archivo

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: ld; AnSChar: ld", xorc, ansc);

//Este es el código característico del programa legal calculado ¡De antemano 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, se llama a VerifyCaller() para verificar la persona que llama:

switch(uIoControlCode)

{

caso IOCTL_VERIFY:

{

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

si ( VerifyCaller()==1)

DbgPrint("[MyDriver] {IOCTL_VERIFY} ¡Código de función ejecutado ahora!");

else

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

status = STATUS_SUCCESS

break;

}

//Omitido a continuación

}

Ejecute la prueba

3. Las personas que llaman legales, las personas que llaman ilegalmente (use eXeScope para parchear a las personas que llaman legales,

como eliminar la información de la versión del programa) y los controladores se copian en la máquina virtual

4. Utilice un llamador legal para cargar el controlador y ejecutarlo

5. Utilice un llamador ilegal para cargar el controlador y ejecutarlo

6. Compare la salida de los dos anteriores en DbgView

Cuando la persona que llama es legal:

Cuando la persona que llama es ilegal:

Escríbalo al final

Después de escribir este artículo, debo reiterar Nuevamente: solo cuando el conductor lleva una firma digital formal, el código que verifica a la persona que llama es valioso. ¿Por qué dices eso? Porque otros no pueden parchear a un conductor con una firma digital formal (una vez que el conductor es parcheado, la firma deja de ser válida, al igual que una mujer cuya virginidad se ha perdido y no tiene valor. Aunque esta metáfora es vulgar, es muy

p>

apropiado). Un conductor sin firma no tiene valor de uso. Incluso si otros quieren usarlo, simplemente coloque el controlador en IDA y aparecerá todo el código.