elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Rompecabezas de Bitcoin, Medio millón USD en premios


  Mostrar Temas
Páginas: 1 [2] 3
11  Programación / Programación C/C++ / Principios básicos de desarrollo de drivers en Windows - Lenguaje C en: 6 Octubre 2010, 20:27 pm
Este articulo también se encuentra en wiki.elhacker.net,
Código:
http://wiki.elhacker.net/programacion/cc/articulos/principios-basicos-de-desarrollo-de-drivers-en-windows



Introducción

Encontrar información sobre el desarrollo de drivers no siempre es tarea fácil, sin importar el sistema operativo del cual estemos hablando.
Normalmente, lo ideal para entender este tipo de temas correctamente es leer libros especializados en el tema u ir directamente a las fuentes. Suele ser lo correcto ya que el tema es complejo, largo y puede volverse pesado para quien se apure en entender todos los conceptos implicados.

Por ahora, esto no sera mas que una introducción, por lo tanto tratare que sea breve. Es solo un intento de dar una perspectiva general sobre este tema y con esto lograr que mas gente decida a interiorizarse en el, con suerte mas adelante podremos ver técnicas y/o conceptos mas avanzados y que la mayoría los entiendan.



subsystem, ¿que es?

Es ideal conocer a fondo las herramientas que estamos utilizando, y con esto no solo me refiero al lenguaje, si no tambien al entorno de trabajo que utilicemos.

El proceso de compilado y linkeado genera un binario adecuado para que el sistema operativo en el cual estemos pueda comprenderlo. En Windows, este formato es lo que varios conocen como PE (Portable Executable Format)
Alrededor de PE, tenemos un concepto llamado subsystem. Un subsystem, entre otras opciones incluidas en la información del header PE, describe como cargar un ejecutable que también incluye el punto de entrada (Entry Point) en el binario.



Conociendo tus herramientas.

Posiblemente muchos recién se enteran lo que es un subsystem, y esto se debe a que generalmente en la etapa de aprendizaje de lenguajes como C/C++, uno puede simplemente descargar un IDE y ponerse a trabajar. Los errores no causan tantos problemas cuando todavía no salimos del modo usuario.

Por ejemplo, las personas que utilizan Visual C++, habrán hecho ya alguna aplicacion en consola u alguna aplicacion para Windows. Al crear el proyecto, el subsystem viene predefinido, tal como /SUBSYSTEM:CONSOLE o /SUBSYSTEM:WINDOWS.

La novedad en todo esto, es que un driver es linkeado con otro tipo de subsystem, llamado NATIVE.

MSDN /SUBSYSTEM
Citar
NATIVE
    Device drivers for Windows NT. If /DRIVER:WDM is specified, NATIVE is the default.



Los drivers también tienen un "main".

Por supuesto, los drivers también tienen un main, un Entry Point.
Si sabemos que es un driver lo que vamos a realizar, basta con que el main reciba los parámetros adecuados y retorne lo esperado para un driver. El sistema se encargara de cargar el driver cuando lo requiramos y darse cuenta que es un driver.

Podemos utilizar cualquier nombre como Entry Point, pero por convención en Windows se utiliza DriverEntry.
Si estas utilizando el DDK, al seleccionar que vas a construir un driver se utilizan una serie de opciones predefinidas. Esta es la razón por la cual DriverEntry se convirtió en algo similar al Entry Point oficial.

Al especificar /DRIVER, tenemos tambien otras opciones, directo de la MSDN:

Use the /DRIVER linker option to build a Windows NT kernel mode driver.

Citar
The UPONLY keyword causes the linker to add the IMAGE_FILE_UP_SYSTEM_ONLY bit to the characteristics in the output header to specify that it is a uniprocessor (UP) driver. The operating system will refuse to load a UP driver on a multiprocessor (MP) system.

The WDM keyword causes the linker to set the IMAGE_DLLCHARACTERISTICS_WDM_DRIVER bit in the optional header's DllCharacteristics field. WDM video capture was designed to resolve the problems inherent in the Video for Windows architecture.

En este caso utilizaremos:

Código:
/SUBSYSTEM:NATIVE /DRIVER:WDM -entry:DriverEntry



Conceptos básicos. Lo que debes saber.

Antes de comenzar, hay que cambiar la mentalidad de "Compilar y probar" que todos solemos adquirir mientras aprendemos a programar en modo usuario.
En el mundo de los drivers la situación cambia y lo hace en forma drástica.

Como mínimo podrías ocasionar un BSOD, y si estamos ante un driver que iniciara siempre con el sistema, tenemos un problema.
Igualmente, nada que no puedas revertir entrando en modo seguro u volviendo a configuraciones previas, pero esto solo cabe en las practicas y no en casos reales.

En conclusión, no compiles y pruebes el código de un driver al menos que entiendas a ciencia cierta que es lo que realiza, y mas aun si antes de compilarlo vas a modificar secciones del código.

Cabe recordar, que esta es solo una introducción de los conceptos básicos, por lo tanto quien quiera interiorizarse a fondo no le queda mas alternativa que revisar la MSDN u libros como "Programming the Windows Driver Model".


Interrupt Request Level


Abreviado como IRQL, partamos de la definición del DDK:

Citar
The priority ranking of an interrupt. A processor has an IRQL setting that threads can raise or lower. Interrupts that occur at or below the processor's IRQL setting are masked and will not interfere with the current operation. Interrupts that occur above the processor's IRQL setting take precedence over the current operation.

The particular IRQL at which a piece of kernel-mode code executes determines its hardware priority. Kernel-mode code is always interruptible: an interrupt with a higher IRQL value can occur at any time, thereby causing another piece of kernel-mode code with the system-assigned higher IRQL to be run immediately on that processor. In other words, when a piece of code runs at a given IRQL, the Kernel masks off all interrupt vectors with a lesser or equal IRQL value on the microprocessor.

Si, puede ser difícil de comprender a la primer lectura, por eso vamos a intentar explicarlo en términos mas sencillos.
El IRQL de un procesador ayuda a especificar como un determinado thread puede ser interrumpido. Dicho thread solo puede ser interrumpido mediante código por un nivel mas alto de IRQL en el mismo procesador.
En estas épocas, es normal ver equipos con varios procesadores. En esos casos cada procesador corre en su propio IRQL

Estos 4 niveles serán con los que tendrás que trabajar normalmente.
Normalmente las APIs llevan una pequeña nota aclarando en que nivel de IRQL necesitas estar para poder utilizar determinada API. En reglas generales, a mas alto sea el nivel, menos APIs podes utilizar.

  • Passive
  • APC (Asynchronous Procedure Calls)
  • Dispatch
  • DIRQL



Citar
1) PASSIVE_LEVEL

    Interrupts Masked Off — None.

    Driver Routines Called at PASSIVE_LEVEL — DriverEntry, AddDevice, Reinitialize, Unload routines, most dispatch routines, driver-created threads, worker-thread callbacks.

El nivel mas bajo. En este nivel corre un thread que se ejecute en modo usuario.

Citar
2) APC_LEVEL

    Interrupts Masked Off — APC_LEVEL interrupts are masked off.

    Driver Routines Called at APC_LEVEL — Some dispatch routines (see Dispatch Routines and IRQLs).

Este es el nivel donde ocurren las "Asynchronous Procedure Calls". Cuando ocurre una APC, el nivel del procesador se aumenta a APC_LEVEL.

Citar
3) DISPATCH_LEVEL

    Interrupts Masked Off — DISPATCH_LEVEL and APC_LEVEL interrupts are masked off. Device, clock, and power failure interrupts can occur.

    Driver Routines Called at DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (while holding the cancel spin lock), DpcForIsr, CustomTimerDpc, CustomDpc routines.

La memoria paginada no es accesible en este nivel, por lo tanto toda la memoria accesible debe ser no-paginada. Esto ocasiona que disminuya en gran parte la cantidad de APIs que podes utilizar.

Citar
4) DIRQL

    Interrupts Masked Off — All interrupts at IRQL<= DIRQL of driver's interrupt object. Device interrupts with a higher DIRQL value can occur, along with clock and power failure interrupts.

    Driver Routines Called at DIRQL — InterruptService, SynchCritSection routines.

En este nivel generalmente un driver no lidia con interrupciones. Es mas que nada una forma de conocer que dispositivos tienen prioridad sobre otros.

I/O Request Packet

Abreviado IRP, basicamente es una estructura que permite a diferentes drivers comunicarse entre si y requerir tareas a finalizar.
El manejo de IRPs puede ser muy simple u demasiado complejo dependiendo de cual sea la estructura de los drivers.

El IRP también contendrá "peticiones secundarias", conocido como "IRP Stack Location". Cada driver contendrá sus propias peticiones secundarias respecto de como interpretar el IRP, denominada IO_STACK_LOCATION.

Creando el DriverEntry

Comenzando con un poco de código, el prototipo del DriverEntry:

Código
  1. NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath);

DRIVER_OBJECT es una estructura que representa el driver.  Este objeto contiene un puntero a DEVICE_OBJECT, la cual es otra estructura que representa un dispositivo en particular.
Un solo driver puede manejar múltiples dispositivos, y tal así, DRIVER_OBJETCT mantiene una lista de todos los dispositivos que para los cuales ese driver maneja peticiones.

Registrypath es una cadena que apunta a una ubicacion en el registro donde la informacion para el driver fue guardada. El driver puede utilizar luego esta ubicacion para guardar informacion especifica.

Veamos la primer parte de nuestra rutina de entrada:

Código
  1. NTSTATUS DriverEntry(PDRIVER_OBJECT  pDriverObject, PUNICODE_STRING  pRegistryPath)
  2. {
  3.    NTSTATUS NtStatus = STATUS_SUCCESS;
  4.    UINT uiIndex = 0;
  5.    PDEVICE_OBJECT pDeviceObject = NULL;
  6.    UNICODE_STRING DriverName, DosDeviceName;
  7.  
  8.    DbgPrint("DriverEntry!\n");
  9.  
  10.    RtlInitUnicodeString(&DriverName, L"\\Device\\ehn");
  11.    RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\ehn");
  12.  
  13.    NtStatus = IoCreateDevice(pDriverObject, 0,
  14.                              &DriverName,
  15.                              FILE_DEVICE_UNKNOWN,//No asociado a ningun dispositivo en particular
  16.                              FILE_DEVICE_SECURE_OPEN,
  17.                              FALSE, &pDeviceObject);
  18.  

Lo primero que se nota es la llamada a DbgPrint, esta funciona como el printf de toda la vida, pero en este caso el output va a parar al kernel debugger. Estos mensajes se pueden ver perfectamente con una herramienta como DbgView de Sysinternals

RtlInitUnicodeString inicializa una estructura del tipo UNICODE_STRING.
Esta estructura esta definida de tal modo que:


Código
  1. typedef struct _LSA_UNICODE_STRING {
  2.  USHORT Length;
  3.  USHORT MaximumLength;
  4.  PWSTR  Buffer;
  5. } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;

Donde "Lenght" es la longitud actual de la cadena, "MaximunLenght" es el tamaño máximo que la cadena puede tener, y "Buffer" es el puntero a la cadena.
Como podemos ver, esta estructura contiene el tamaño de la cadena por lo tanto no necesitamos verificar si la cadena esta terminada en NULL.

Luego de crear el dispositivo, ahora queda configurar el Driver Object para que llame a nuestro driver cuando ciertas peticiones se realicen. Estas peticiones se denominan IRP Major requests.
Existen tambien las denominadas Minor requests a las cuales nos referimos con anterioridad como peticiones secundarias, se encuentran en el stack location del IRP.

Código
  1.        for(uiIndex = 0; uiIndex < IRP_MJ_MAXIMUM_FUNCTION; uiIndex++)
  2.             pDriverObject->MajorFunction[uiIndex] = ehn_UnSupportedFunction;
  3.  
  4.        pDriverObject->MajorFunction[IRP_MJ_CLOSE]             = ehn_Close;
  5.        pDriverObject->MajorFunction[IRP_MJ_CREATE]            = ehn_Create;
  6.        pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]    = ehn_DeviceControl;
  7.        pDriverObject->MajorFunction[IRP_MJ_READ]              = ehn_Read;
  8.        pDriverObject->MajorFunction[IRP_MJ_WRITE]             = ehn_Write;
  9.  

Es decir, cuando una aplicación en modo usuario llame a algunas de estas funciones:

  • CreateFile
  • CloseHandle
  • WriteFile
  • ReadFile
  • DeviceIoControl


se llamara a tu driver.



Rutina Unload

Lo siguiente es la función de descarga del driver:

Código
  1. pDriverObject->DriverUnload =  ehn_Unload;

En realidad la rutina de descarga solo se necesita si queres descargar tu driver en forma dinámica, en ese caso la rutina debe ser especificada. Si no se especifica una función de descarga, el sistema no dejara que el driver sea descargado.

Esta es, por ejemplo, una forma simple de rutina de descarga:

Código
  1. void Example_Unload(PDRIVER_OBJECT  DriverObject)
  2. {    
  3.  
  4.    UNICODE_STRING DosDeviceName;
  5.  
  6.    DbgPrint("Unload!!\n");
  7.  
  8.    RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\ehn");
  9.    IoDeleteSymbolicLink(&DosDeviceName);
  10.  
  11.    IoDeleteDevice(DriverObject->DeviceObject);
  12. }

Definir las otras funciones es tarea para el hogar ya que creo es la parte mas divertida de todo esto y la que supongo motivara mas para los que quieran interiorizarse en el tema.
Igualmente este ejemplo es de lo mas básico y es el que se utiliza para comprender estos conceptos por lo tanto probablemente al buscar los conceptos que faltan para definir lo necesario se encuentren con ejemplos conceptualmente similares, por lo tanto la dificultad es casi nula pero si sera bastante divertido de seguro.



Cargar y descargar el driver en forma dinámica.

Código
  1. int _cdecl main()
  2. {
  3.    HANDLE HSmng,HServ;
  4.    SERVICE_STATUS Sstatus;
  5.  
  6.    HSmng = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  7.  
  8.    printf("Cargando el driver!\n");
  9.  
  10. if(HSmng)
  11.    {
  12.        HServ = CreateService(HSmng, "ehn",
  13.                                 "ehn driver",
  14.                                  SERVICE_START | DELETE | SERVICE_STOP,
  15.                                  SERVICE_KERNEL_DRIVER,
  16.                                  SERVICE_DEMAND_START,
  17.                                  SERVICE_ERROR_IGNORE,
  18.                                  "C:\\ehn.sys",
  19.                                  NULL, NULL, NULL, NULL, NULL);
  20.  
  21.        if(!HServ)
  22.        HServ = OpenService(HSmng, "ehn", SERVICE_START | DELETE | SERVICE_STOP);
  23.  
  24.  
  25.        if(HServ)
  26.        {
  27.            printf("Iniciando servicio\n");
  28.  
  29.            StartService(HServ, 0, NULL);
  30.            printf("Presione una tecla para cerrar el servicio\n");
  31.            getchar();
  32.            ControlService(HServ, SERVICE_CONTROL_STOP, &Sstatus);
  33.  
  34.            DeleteService(HServ);
  35.  
  36.            CloseServiceHandle(HServ);
  37.  
  38.        }
  39.  
  40.        CloseServiceHandle(HSmng);
  41.    }
  42.  
  43.    return EXIT_SUCESS;
  44. }

Para cualquier detalle respecto de las APIs utilizadas, referirse a la documentación del DDK y de la MSDN.




return EXIT_SUCESS


Luego de definir las funciones correspondientes en el driver, al utilizar las APIs desde modo usuario podrán comprobar que secciones de código se utilizan de su driver, incluso al definir las funciones que restan les basta con poner algunos dbgprint para luego ver con dbgview que es exactamente lo que se esta ejecutando.

Lo que no hay que hacer

Toda área tiene una lista de cosas que NO hay que hacerse, el desarrollo de drivers no es la excepción y Microsoft se encargo de resaltar este tipo de errores.

Como ejemplo, las primeras 10, hay mas en el link:

Citar
Never return STATUS_PENDING from a dispatch routine without marking the I/O request packet (IRP) pending (IoMarkIrpPending).
Never call KeSynchronizeExecution from an interrupt service routine (ISR). It will deadlock your system.
Never set DeviceObject->Flags to both DO_BUFFERED_IO and DO_DIRECT_IO. It can confuse the system and eventually lead to fatal error. Also, never set METHOD_BUFFERED, METHOD_NEITHER, METHOD_IN_DIRECT or METHOD_OUT_DIRECT in DeviceObject->Flags, because these values are only used in defining IOCTLs.
Never allocate dispatcher objects from a paged pool. If you do, it will cause occasional system bugchecks.
Never allocate memory from paged pool, or access memory in paged pool, while running at IRQL >= DISPATCH_LEVEL. It is a fatal error.
Never wait on a kernel dispatcher object for a nonzero interval at IRQL >= DISPATCH_LEVEL. It is a fatal error.
Never call any function that causes the calling thread to wait directly or indirectly while executing at IRQL >= DISPATCH_LEVEL. It is a fatal error.
Never lower the interrupt request level (IRQL) below the level at which your top-level routine has been invoked.
Never call KeLowerIrql() if you haven't called KeRaiseIrql().
Never stall a processor (KeStallExecutionProcessor) longer than 50 microseconds.



Referencias

Lectura recomendada para los que quieran interiorizarse y/o iniciarse en este tema.  :)

WDK
Peering Inside the PE: A Tour of the Win32 Portable Executable File Format
Key driver concepts
Getting started: Writing Windows drivers

Libros:
"Programming the windows driver model"

Saludos!
12  Programación / Programación General / Que lenguajes de programación utilizas habitualmente? en: 4 Octubre 2010, 05:09 am
He decidido hacer nuevamente la encuesta respecto de que lenguajes se utilizan habitualmente puesto que las anteriores me parecen que no reflejan con mucha exactitud cuales son los lenguajes mas utilizados en el foro.
Las anteriores encuestas se pueden ver aqui.

Esta también expirara en el termino de 1 año como las anteriores, pero voy a restringir las opciones a votar a un máximo de 5 ya que la anterior permitía un total de 11 siendo 11 las opciones. Creo que todos estamos de acuerdo en que casi nadie utiliza mas de 5 lenguajes en forma habitual al menos que este todos los días probando los ejemplos del "Hola mundo" que aparecen en Wikipedia.  ;D

Por supuesto digo casi, porque puede haber excepciones, pero realmente sera una estadística tan baja que ni siquiera vale la pena contemplar.
El problema creo yo es que "habitual" puede interpretarse de muchas maneras, así que definamos por ejemplo, en el termino de 1 mes, que lenguajes se utilizaron en mayor medida? esos serán los que haya que votar.

Recuerden, no es una competencia para ver quien conoce mas lenguajes, así que traten de ser objetivos con los votos. Que lenguajes utilizan habitualmente?

Saludos!
13  Foros Generales / Sugerencias y dudas sobre el Foro / Errores en advertencias con cake y errores en mostrar nuevas respuestas. en: 28 Septiembre 2010, 15:20 pm
El primer error se da en las advertencias enviadas mediante cake, no se si en todos los casos pero si por lo menos en varios.

Al enviar una advertencia, el mensaje automatico queda algo asi como:

Citar
El mensaje se ha cerrado por tal
Leer reglas:
:http://linkdereglasundefined

Ese "undefined" se agrega en casi todo mensaje enviado mediante cake. Si por ejemplo se envía una advertencia a todos los participantes del hilo, queda algo así:

Citar
No utilicen el foro como un chatundefined



El segundo error, se da en el "mostrar nuevas respuestas a tus mensajes". Al hacer un post y luego visitarlo por el regreso automático -el que se activa desde el perfil- el post no se marca como visitado. Si luego presionas el "mostrar nuevas..." aparecen los últimos mensajes que hayas hecho sin importar si visitaste todos y las ultimas respuestas son siempre propias. Tienes que volverlos a visitar para que se marquen como leídos y creo que eso antes no sucedía.

Eso solo.

Saludos!
14  Programación / Programación C/C++ / [Recopilatorio] Sources interesantes en: 3 Julio 2010, 08:00 am
    Si tienes un source interesante y todavía no esta en la lista, ponlo en este mismo post o envíalo por pm y sera agregado. :)




Sources



A:


B:


C:



D:


E:



F:


G:

H:


I:


J:

K:


L:


LL:

M:


N:

O:


P:


Q:


R:


S:


T:


U:

V:

W:


X:

Y:

Z:

[/list]
15  Foros Generales / Foro Libre / Hitler se entera que Alemania juega contra Argentina en: 3 Julio 2010, 01:11 am


Vamos Argentina! mañana a todo o nada  :D
16  Comunicaciones / Mensajería / Conectar a freenode mediante TOR. en: 31 Mayo 2010, 10:46 am
Requisitos:


En este caso para el ejemplo utilice xchat.



1) Configuración Tor.

  • Abrimos el archivo de configuración, por defecto se encuentra en:

Código:
[U]:\Documents and Settings\User\Datos de programa\Vidalia\torrc

o

Código:
/etc/tor/torrc

  • Al final del archivo agregamos la linea:

Código:
mapaddress  10.40.40.40  p4fsi4ockecnea7l.onion 

  • Guardamos el archivo y reiniciamos Tor.


2) Configuración xchat.

  • En la lista de redes, creamos una (pueden llamarla como quieran). En este caso la llamaremos freenode.
  • La editan y en la lista de servidores agregan:

Código:
10.40.40.40

  • Vamos a Configuración>Preferencias>Red>Configuración de la red:

Nombre de equipo: localhost.
Puerto: 9050.
Tipo: SOCKS5

3) Login SASL

  • Descargamos cap_sasl.py y lo colocamos en el directorio de instalación
  • Xchat>Cargar complemento>cap_sasl.py (La interfaz de Python debería cargarse automáticamente si reinician xchat)
  • /SASL -set Nombrered(En este caso es freenode) nick pass
  • Conectar




Me preguntaron varios sobre esto por Msn así que preferí hacer un post al respecto. Lo hice lo mas sencillo posible pero si algo queda poco claro no duden en preguntar. :D

Saludos!
17  Programación / Programación C/C++ / |Lo que no hay que hacer en C/C++. Nivel basico| en: 12 Diciembre 2009, 18:15 pm
Este articulo también se encuentra en wiki.elhacker.net,
Código:
http://wiki.elhacker.net/programacion/cc/articulos/lo-que-no-hay-que-hacer-en-c-c

La mayoría que recién esta empezando con C/C++ y viene al foro a sacarse dudas, cometen errores similares. Incluso muchos tienen conceptos erroneos sobre la utilidad o eficiencia de ciertas funciones, probablemente debido a el estudio de textos incorrectos o de libros anticuados.

Pero incluso a veces uno no puede escapar de ciertas cosas. Imaginen a alguien que recién entra en la universidad y el profesor le hace incluir conio para utilizar getch. Seguramente el alumno de por sentado que hacer eso esta perfecto, e incluso el profesor recompense con notas altas a los que programan tal cual se ha enseñado.
Por lo tanto, ciertas cosas son comprensibles y el único consejo que se puede dar es que nunca den por sentado algo, es recomendable informarse antes para ver que es lo que se esta realizando, sobre todo en programación.

Para los que quieran revisar un poco explicaciones mas detalladas del "por que" de ciertas reglas, puede revisar este draft, que aunque puede tornarse bastante técnico para alguien que recién comienza, puede servir bastante:

WG14/N1256 Committee Draft — Septermber 7, 2007 ISO/IEC 9899:TC3
(Gracias Queta)


1)
gets();

La falta de control en un programa es un factor bastante negativo, y también peligroso en caso de programas comerciales o masivos. Ese es el principal problema de gets, no tiene control interno.

¿Que signifca no tener control interno?

Significa que la funcion acarrea como consecuencia el mal comportamiento del programa, resultados inesperados y erroneos, vulnerabilidades entre otras tantas cosas.
Define una cadena de 10 caracteres, y gets va a aceptarte eso y muchisimo mas como entrada.

Veamos este codigo:

Código
  1. #include<stdio.h>
  2.  
  3. int main()
  4. {
  5.  
  6. char letra1[]="AAAAA";
  7. char letra2[]="BBBBB";
  8. char letra3[]="CCCCC";
  9.  
  10. printf("A: %s\nB: %s\nC: %s\n\n",letra1,letra2,letra3);
  11.  
  12. printf("Ingrese la letra D, 5 veces: ");
  13. gets(letra2);
  14.  
  15. printf("\nA: %s\nB: %s\nC: %s\n\n",letra1,letra2,letra3);
  16.  
  17. }

Código:
A: AAAAA
B: BBBBB
C: CCCCC

Ingrese la letra D, 5 veces: DDDDD

A: AAAAA
B: DDDDD
C: CCCCC

Como vemos nada paso, y los elementos de letra2 se reemplazaron por el input ingresado como debería ser . Ahora en vez de 5 veces, ingresemos la letra 'D' mas veces de lo que el programa nos indica:

Código:
A: AAAAA
B: BBBBB
C: CCCCC

Ingrese la letra D, 5 veces: DDDDDDDDDDDDDDDDDDD

A: DDD
B: DDDDD
C: CCCCC

Que paso con letra1? :huh: precisamente lo que no queremos que pase en nuestro programa. gets sobreescribio una zona de memoria que no debia sobreescribir, ya que el input debia ser ingresado en letra2, pero simplemente no le importo que el input sea muchisimo mas grande que el tamaño de la cadena. Razon mas que suficiente para no utilizarla.

Por lo tanto es recomendable utilizar fgets();

Código
  1. fgets(char *string, int length, FILE * stream)

Es decir:

Código
  1. fgets(letra2, 5, stdin);

Con fgets permites que sean ingresados lenght-1 caracteres.
Aunque no todo es color de rosas, y en caso de que se ingresen menos caracteres de los que has definido como tamaño, tendras que lidiar con un salto de linea '\n'. Obviamente es una nimiedad que puedes arreglarla de esta forma:


Código
  1. if (letra2[strlen(letra2)-1] == '\n')//string.h para strlen();
  2. letra2[strlen(letra2)-1] = '\0';

Supongo que con esto queda claro que no tiene sentido utilizar gets();.

Links:
http://en.wikipedia.org/wiki/Fgets
http://www.gidnetwork.com/b-56.html


2) fflush(stdin);

fflush(stdin) es un invitado casi diario. Pocas veces pasa un dia sin que alguien lo recomiende o lo mencione como la solucion! a los malos comportamientos de las pausas en los programas.

STDIN, como su nombre lo indica, significa 'Standard input'. Es decir, el ingreso por teclado.

Acorde al Standard, fflush espera solamente un stream de salida (STDOUT: 'Standard Output) por lo que el comportamiento con streams de entrada como STDIN es indefinido. Por mas que en algunas plataformas funcione, o que en algunos compiladores funcione, no deberia ser utilizado.

Por el otro lado, para evitar esas pausas fastidiosas es necesario evitar las funciones que dejan basura por doquier (como scanf();) y utilizar funciones como la ya mencionada fgets();


http://foro.elhacker.net/programacion_cc/fflushstdin-t91101.0.html
http://foro.elhacker.net/programacion_cc/zanjar_de_una_vez_fflushstdin-t265125.0.html;msg1294511
http://foro.elhacker.net/programacion_cc/error_super_basico_en_c_quien_me_ayuda_soy_nuevo-t262118.0.html;msg1275898
http://foro.elhacker.net/programacion_cc/problema_con_el_compilador_gcc_de_ubuntu-t248582.0.html




3) system("PAUSE");

Otro invitado diario es la pausa utilizando system. Esto incluso me ha tocado en mi universidad. Que el profesor me haga realizar ejercicios exclusivamente utilizando system y la pausa tambien habia que realizarla exclusivamente utilizando system. En fin, seguramente muchos consideren que realizar esto no es un gran problema, y probablemente tengan razon en los codigos que uno realiza como tarea estudiantil. Pero es un habito malo, y como todo, hay que dejarlo algun dia. Claramente no puedes pasarte toda la vida llamando al sistema para hacer una pausa.

*No es portable, solo va a funcionar en sistemas que tengan el comando PAUSE.


*Es una funcion pesada. Llamar al sistema, suspender tu programa, buscar el comando PAUSE, etc etc etc etc.

Por el otro lado si se siguio la norma de no dejar basura en el buffer de entrada, se puede utilizar soluciones mas sencillas como getchar(); (o cin.get(); en C++).


4)
conio.h

Librerias inutiles si las hay, pero por sobre todas las cosas NO standard. No existe problema para el cual sea exclusivamente necesario utilizar conio.h. Y visto que aca la mayoria de los que la incluyen lo hacen para utilizar getch(); supongo que con todo el parrafo anterior habran quedado claras las alternativas.

http://en.wikipedia.org/wiki/Conio.h
http://www.gidforums.com/t-7379.html


5) void main();

Seguramente para justificar esto habría que decir que el estándar dice que void no puede ser el valor de retorno del main, lo cual es verdad, pero no siempre es suficiente. Sobretodo considerando que muchas veces en los libros el void main dice presente.

No voy a extenderme mucho con esto ya que voy a dejar los links necesarios para el que quiera entender porque sucede, pero como base comprendamos esto:
 
-Devolver un valor void es perder la garantia que un valor de retorno distinto de 0 implica un error, lo cual es malo tanto para el SO como para el determinado programa que pueda estar llamando a tu código.

-Esta mal acorde al estándar.

-Puede que tu programa no funcione correctamente.

Los errores pueden depender de los mecanismos de retorno o la arquitectura especifica en la cual estemos. Pero por supuesto, si elegimos un lenguaje de alto nivel como lo es C o C++, es para no tener que preocuparnos por esos asuntos.

En conclusión, el que quiera entender el " por que", leer los siguientes links.

Links:

http://www.eskimo.com/~scs/readings/voidmain.960823.html
http://home.att.net/~jackklein/ctips01.html#int_main
http://users.aber.ac.uk/auj/voidmain.shtml


6) Procesamiento de cadenas: string.h

La mayoría de las funciones que no tienen la posibilidad de pasar como argumento el tamaño del buffer que debe procesarse, estan completamente expuestas al desbordamiento de buffer. Sobre todo si el contenido del buffer origen es ingresado por el usuario, y por sobre todas las cosas, si no se hicieron las validaciones correspondientes. (Por ejemplo, haber utilizado gets o no haber chequeado el tamaño de la cadena antes de copiar/concatenar o cualquier otra forma de procesar una cadena).
Obviamente no voy a hablar sobre todas las funciones, pero si sobre dos ejemplos que dan la pauta de lo que sucede y de lo que debería hacerse: strcpy y strcat.

a) strcpy ( char * destination, const char * source );

Veamos un ejemplo de un codigo incorrecto:


Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strcpy(cadena1,cadena2);
  14. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  15. return 0;
  16. }

Código:
Ingrese cadena: 1111111111111111111111111111111111111111111111111111

//Antes de strcpy
cadena1: aaaa
cadena2: 111111111111111111111111111111111111
cadena3: 0123

//Despues de strcpy
cadena1: 111111111111111111111111111111111111
cadena2: 111111111111111111111111111111111111
cadena3: 1111111111111111111111111111111111111111111111111111

Como se ve claramente, el problema esta vez no estuvo en la recepción de los datos (en el sentido técnico), si no en el copiado. Ya que strcpy no verifica si el buffer destino es efectivamente capaz de contener los datos del buffer origen, lo cual por supuesto ocasiona perdida de datos y buffers overflows.
Obviamente si el fgets se hubiese utilizado correctamente, esto no hubiese sucedido.

b) strcat ( char * destination, const char * source );

La misma historia sucede aqui. Al no poder especificar el tamaño del buffer destino la funcion es insegura. Veamos un ejemplo incorrecto:

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strcat(cadena1,cadena2);
  14. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  15. return 0;
  16. }

Código:
Ingrese cadena: 11111111111111111111111111111111111111111111

//Antes de strcat
cadena1: aaaa
cadena2: 1111111111111111111111111111
cadena3: 0123


//Despues de strcat
cadena1: aaaa1111111111111111111111111111
cadena2: 1111111111111111111111111111
cadena3: 11111111111111111111111111111111111111111111

Como pueden ver suceden errores muy similares. Obviamente es un error tonto de programación en el cual el desarrollador pasa un mal argumento a fgets, ya que de haberlo hecho bien el error no sucederia. Pero ese no es el asunto, lo importante es que tanto strcpy o strcat (y toda la familia de funciones) no realizan (o permiten realizar) la validación extra necesaria. Imaginen que creas una cadena para tomar una ruta del sistema operativo para luego concatenar, ¿Y si el atacante se da cuenta de eso y decide explotar el overflow por ese lado? mil variantes pueden existir y probablemente no siempre uno cubra todas ellas.

c) Entonces que se puede utilizar?

strncpy y strncat cumplen su función perfectamente si se utilizan en forma correcta.

c1) strncpy ( char * destination, const char * source, size_t num );

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strncpy(cadena1,cadena2,1);
  14. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  15. return 0;
  16. }

Código:
Ingrese cadena: 1111111111111111111

//Antes de strncpy
cadena1: aaaa
cadena2: 1111111111111111111
cadena3: 0123

//Despues de strncpy
cadena1: 1aaa
cadena2: 1111111111111111111
cadena3: 0123

c2) strncat ( char * destination, const char * source, size_t num );

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strncat(cadena1,cadena2,0);//Obviamente esta linea es un absurdo
  14. //Pero queda claro el control que la funcion mantiene
  15. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  16. return 0;
  17. }

Código:
Ingrese cadena: 123123

//Antes de strncat
cadena1: aaaa
cadena2: 123123
cadena3: 0123

//Despues de strncat
cadena1: aaaa
cadena2: 123123
cadena3: 0123

Estas ultimas funciones a pesar de poder utilizarse perfectamente como solucion a lo anterior expuesto, tambien tienen sus contras:

-Tanto strncpy o strncat no proveen un valor de retorno que pueda implicar un error o el exito de la cadena resultante, si no que devuelven un puntero al buffer destino. Por lo tanto requiere un esfuerzo extra por parte del programador.

- strncpy no finaliza la cadena resultante con null si el destino es al menos igual en tamaño que el origen, por lo tanto siempre debe finalizarse la cadena con null después de la llamada a strncpy.

- strncpy tambien tiene un comportamiento que puede afectar el rendimiento del programa en caso que el buffer destino sea considerablemente mas grande que el buffer origen, ya que en este caso se realiza el zero-padding, es decir, llena el resto de la cadena con nulls.

Microsoft tiene una lista de funciones baneadas (lo que en Visual Studio se conoce como deprecated) y obviamente tiene el reemplazo de estas llamandolas funcion_s. Ejemplo: strcpy_s, strcat_s, gets_s y etc.
Pueden ver las funciones catalogadas como inseguras en este header, y su reemplazo en esta lista. (Gracias Vertex por los links)

Tambien existen opciones como strlcpy y strlcat, las cuales pueden ver a fondo en este link.


------------------------------------------------------------------------------------------

Continua





18  Media / Juegos y Consolas / Attack on pearl harbor. |Quien se juega unas partidas?| en: 7 Junio 2009, 02:24 am
Pues eso, anda alguien viciando ese juego?

Un video para los que no lo conocen

19  Media / Juegos y Consolas / Como siempre, Unreal Engine dando que hablar. Problemas en X-men Origins. en: 4 Junio 2009, 08:07 am
Y otra vez y vaaan, creo que es la cuarta vez que un juego con este motor me da dolores de cabeza (Cod 5, Mirrors Edge etc)

Voy primero con el problema y despues con las cosas que hice y el hardware que tengo.


Problema


Inicio el juego y queda el splash screen como 5 minutos, luego como que quiere iniciar y queda en black screen.


Hardware y SSOO

PC 1

Core 2 Duo e8400
4gb ram 1066mhz
Geforce 9800gt

SO1: Windows XP Pro SP3 x32
SO2: Windows Vista Ultimate x64

Juegos probados con anterioridad en esta pc: Crysis, Crysis warhead, GTA 4 etc

PC 2

Pentium D
2gb ram
Geforce 6200

SO: Windows XP Pro SP3 x32


Intentos fallidos


1*Actualizar graficas
2*Actualizar DX
3*Actualizar Physx
4*Tunear baseengine.ini
5* Desactivar Unimodem (De forma manual, cambiando los valores DevicePresence/Emulated en el registro)
6*Reinstalar
7*Reiniciar muchas veces
8*Ejecutar modo compatibilidad
9*Probar varias medicinas
10*Putear
11*Rezar a Zeus
12* Deshabilitar aceleracion de sonido

Nada de esto funciono, alguien tiene alguna idea? Googlie, bingie y Yahooie y nada.



Un saludo!










20  Informática / Software / Asistente virtual, reconocimiento de voz, tareas programadas etc. Existe? en: 19 Febrero 2009, 18:12 pm
Bueno esa es la pregunta.

Un asistente que hable y reconozca la voz, se le pueda poner tareas programadas. No importa si es Windows o Linux.
Para poner un ejemplo sencillo, que pueda tanto despertarte hablando como asi tambien maximizar la pantalla si le das la orden por voz.

Sin tener que utilizar el esclavo o cosas asi porque es muy cutre.

Un abrazo
Páginas: 1 [2] 3
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines