Hemos leído: Reversing. Secrets of Reverse Engineering (Eldad Eilam, Wiley)

Sí, ya sé, muchos libros técnicos estoy leyendo últimamente, pero es que voy por rachas, y ciertamente este lo he disfrutado casi como una novela. El título es correcto: Ingeniería inversa con pelos y señales. Toda una gozada pese a lo denso del contenido, aunque el que esto suscribe en su juventud tuvo cierta etapa sobre la que es mejor correr un estúpido velo, y casi nada de lo tratado aquí le ha sonado a nuevo.

Tras una larga introducción de más de 130 páginas, justificativa de la ingeniería inversa, y en la que se explica muy por encima todo lo que se debe saber antes de sentarse a realizar una sesión de este tipo, incluyendo qué herramientas usar y cómo funcionan, el autor entra de lleno en materia.

El capítulo 5, primero de la segunda parte, incluye una lección magistral (de magnífica, no de magisterio) en la que se realiza ingeniería inversa sobre un subconjunto del API incluido en NTDLL.DLL, perteneciente a las tripas de Windows. En concreto indaga en las funciones del grupo RtlGenericTable, y nos va demostrando paso a paso cómo, en un alarde de inteligencia y buen hacer, qué hacen dichas funciones y cómo. Llega hasta a reconstruir la estructura de datos no trivial que usan dichas funciones: una variante optimizada de los árboles-B que coloca los nodos más visitados encima del todo. Lo dicho: todo un alarde de inteligencia, deducción y aplicación de la ingeniería inversa. El lector realmente ve lo que el autor está haciendo y no le quedan dudas sobre el funcionamiento de dichas funciones.

El siguiente capítulo tampoco se queda chiquito. Se nos demuestra paso a paso cómo obtener la estructura de un fichero… que guarda ficheros encriptados con encriptación fuerte. Ahí es nada. Partiendo de la picardía, de la inspección del código ejecutable del programa que crea el fichero y el visionado hexadecimal del propio fichero, nos va reconstruyendo paso a paso todo el proceso, llegando a obtener la información suficiente para poder realizar un nuevo programa que podría usar dicho formato de fichero. Evidentemente, no rompe el algoritmo de encriptación, pero sí lo identifica y llega a mimetizar el comportamiento del programa original… y eso que el fichero en sí también está encriptado. No me canso de repetirlo: una verdadera gozada de inteligencia y deducción aplicada.

El siguiente es algo más flojito, aunque desde luego suficiente. Nos explica cómo se puede auditar un ejecutable para detectar problemas potenciales de seguridad (como, por ejemplo, sobreescrituras de buffer), y en el octavo vuelve a dejarnos con la boca abierta: tras explicarnos por encima los tipos de malware más comunes, destripa el Backdoor.Hackarmy.D, obteniendo incluso la lista de comandos remotos que acepta, así como la vía de infección e instalación.

En el 9 nos cuenta las técnicas de protección anti copia y anti pirateo y nos explica las vías de entrada para saltárnoslas, pero sin profundizar mucho. También afirma algo que muchos fabricantes de software no se enteran o no quieren enterarse: que no existe ninguna protección por software (aunque se apoye parcialmente en hardware) que no pueda ser reventada tarde o temprano.

El capítulo 10 cubre las técnicas existentes para evitar la ingeniería inversa y vuelve a darnos las pautas para eliminarlas, y en el 11 nos vuelve a dejar, de nuevo, perplejos, saltándose varias protecciones avanzadas de forma sencilla y eficiente mediante el uso de un programa de ejemplo. También nos cuenta que hay un sitio en internet dedicado a este tipo de técnicas tomadas como juego, es decir, que alguien propone programas de ejemplo para que otros los rompan. La web es http://www.crackmes.de.

La cuarta parte tiene dos capítulos. En el primero nos cuenta cosas sobre el .NET y cómo invertir las ofuscaciones y demás protecciones, sin profundizar mucho (ya que ciertamente la mayoría de este tipo de tareas son poco menos que triviales dentro del .NET), y en el siguiente nos cuenta el estado del arte (en 2005) de las técnicas de descompilación, es decir, de las técnicas existentes para convertir un programa ejecutable en un código fuente equivalente en algún lenguaje de alto nivel, en general C. Estando de acuerdo con el autor y con otros muchos estudiosos del tema, en el proceso de compilación existe cierto grado de pérdida de información que es irreversible, sobre todo con compiladores optimizadores agresivos.

El libro termina con varios apéndices que explican cómo detectar estructuras de datos, prólogos y epílogos de las llamadas de función, y la traducción de ciertas secuencias binarias a un código fuente equivalente.

Como ya he dicho, y pese a lo arduo de la lectura, el libro es toda una gozada si eres de esas personas inquietas a las que les gusta emplear la deducción y la inteligencia a tareas aparentemente imposibles de realizar… para descubrir que un poco de picardía, de intelecto y de conocimientos son suficientes para asombrarse incluso de uno mismo.

C++/CLI VIII: Usando hilos… con hilos

Si ayer hablaba sobre cómo usar hilos sin hilos, ahora vamos a hablar de hacer lo mismo pero utilizando el componente Thread, que es como se deben manejar, o mejor dicho, una de las formas adecuadas de trastearlos.

Ayer comentábamos que lo habitual a la hora de crear un hilo para que realice una tarea es usar algo como esto:

   1: Thread ^hilo=gcnew Thread(gcnew ThreadStart(this,&Clase::Método));
   2: hilo->Start();

En donde &Clase::Método es la dirección del método que va a realizar el trabajo. Pero vamos a completar el ejemplo. Supongamos que tenemos que realizar una tarea X que se pueda realizar de forma asíncrona sin necesidad de que el usuario intervenga. La solución pasa por realizar algo como lo que explicamos ayer o como lo que vamos a contar ahora. Pero primero el código fuente:

   1: #include "stdafx.h"
   2:  
   3: using namespace System;
   4: using namespace System::Threading;
   5:  
   6: ref class Worker
   7: {
   8:     Thread ^m_thread;
   9: public:
  10:     Worker(void)
  11:     {
  12:     }
  13:     void DoWork(void)
  14:     {
  15:         Console::WriteLine("Empiezo a currar");
  16:         Thread::Sleep(10000);
  17:         Console::WriteLine("Termino de currar");
  18:     }
  19:     void StartWorking(void)
  20:     {
  21:         m_thread=gcnew Thread(gcnew ThreadStart(this,&Worker::DoWork));
  22:         m_thread->Start();
  23:     }
  24:     void WaitFinished(void)
  25:     {
  26:         while(m_thread->IsAlive)
  27:         {
  28:             Console::WriteLine("¿Terminaste?");
  29:             Thread::Sleep(1000);
  30:         }
  31:     }
  32: };
  33:  
  34: int main(array<System::String ^> ^args)
  35: {
  36:     Worker ^w=gcnew Worker();
  37:     w->StartWorking();
  38:     w->WaitFinished();
  39:     Console::WriteLine("Ya terminó");
  40:     Console::ReadKey(true);
  41:     return 0;
  42: }

Primero hemos definido una clase que llamamos Worker y que es la clase que va a hacer la tarea. Pero esto es un ejemplo, y en general todo lo que aparece en la clase podría aparecer dentro de una ficha normal y corriente. Tenemos un método llamado DoWork() que se encarga de hacer una tarea más o menos pesada; en nuestro caso el hilo se duerme durante 10 segundos. También disponemos de un método para iniciar el trabajo que hemos llamado, en un asombroso alarde de imaginación, StartWorking(). Asimismo hemos creado otro más, encargado de esperar mediante un bucle hasta que se termine de realizar la faena.

En main() instanciamos la clase, hacemos una llamada a StartWorking() para que se inicie el trabajo y finalmente esperamos a que éste termine.

Como el lector habrá podido darse cuenta, la utilidad de hacer esto es poca, ya que seguimos teniendo bloqueado el hilo principal del programa, por lo que podríamos transformar la función de espera por un temporizador que comprobara si el hilo ha acabado o no periódicamente, o instalar un delegado que nos notifique cuando el hilo acaba haciendo una llamada a dicho delegado al finalizar el mismo, pero entonces nos estamos acercando al modelo de ejecución asíncrona mediante delegados ya explicados.

Ahora imaginemos algo más sencillo: necesitamos imprimir un documento largo y, aunque el .NET lo suele hacer rápido con el componente PrintDocument, siempre congelará nuestra interfaz, por lo que lo usual sería utilizar la primera parte de lo explicado para lanzar un hilo que imprima y dejarlo que muera él solo. Incluso podríamos lanzar varios hilos simultáneamente y olvidarnos de ellos, aunque para eso tenemos los Thread Pools y los BackgrounWorkers, que quizás explique en algún momento más adelante.

Otro acercamiento

Trabajar con hilos puede ser o muy fácil o muy difícil, depende de lo que queramos… y cómo nos lo planteemos. Ahora supongamos que estamos realizando una tarea de larga duración en un hilo y que el usuario cierre el programa. ¿Qué hacemos con el hilo? ¿Lo matamos a lo bruto? ¿Esperamos a que termine? ¿Y si no podemos matarlo, es decir, el hilo debe salir graciosamente cerrando todas sus tareas? ¿Cómo se lo comunicamos?

El caso típico en mi trabajo consiste en que estoy hablando con un periférico (o manteniendo el estado del mismo) mediante un hilo y el usuario decide cerrar el programa. Pero hay periféricos a los que no se puede dejar de atender así como así, y menos aún si están realizando una tarea crítica, como procesando cualquier transacción. Dicha transacción ha de finalizar, y luego hemos de decirle al periférico que se deshabilite. También debemos implicar al usuario, informándole de que el hilo está terminando.

Veamos la aproximación más sencilla. Primero el código.

   1: #include "stdafx.h"
   2:  
   3: using namespace System;
   4: using namespace System::Threading;
   5:  
   6: ref class Tareas
   7: {
   8: private:
   9:     volatile bool m_bTerminateThread;
  10:     volatile bool m_bThreadIsFinished;
  11:  
  12:     Thread ^m_thread;
  13:     void ThreadHazTareas(void)
  14:     {
  15:         m_bThreadIsFinished=false;
  16:         int tarea=0;
  17:         for(;;)
  18:         {
  19:             Console::WriteLine("Tarea {0}.1",tarea);
  20:             Thread::Sleep(1000);
  21:             Console::WriteLine("Tarea {0}.2",tarea);
  22:             Thread::Sleep(1000);
  23:             Console::WriteLine("Tarea {0}.3",tarea);
  24:             Thread::Sleep(1000);
  25:             Console::WriteLine("Tarea {0}.4",tarea);
  26:             Thread::Sleep(1000);
  27:             tarea++;
  28:             if(m_bTerminateThread)
  29:             {
  30:                 m_bThreadIsFinished=true;            
  31:                 return;
  32:             }
  33:         }
  34:     }
  35: public:
  36:     property bool HaTerminado
  37:     {
  38:         bool get(void){return m_bThreadIsFinished;}
  39:     }
  40:     void IniciaTareas(void)
  41:     {
  42:         m_bTerminateThread=false;
  43:         m_thread=gcnew Thread(gcnew ThreadStart(this,&Tareas::ThreadHazTareas));
  44:         m_thread->Start();
  45:     }
  46:     void TerminaTareas(void)
  47:     {
  48:         m_bTerminateThread=true;
  49:     }
  50: };
  51: int main(array<System::String ^> ^args)
  52: {
  53:     Tareas ^t=gcnew Tareas();
  54:     t->IniciaTareas();
  55:     Thread::Sleep(5000);
  56:     Console::WriteLine("Orden: finalizar");
  57:     t->TerminaTareas();
  58:     while(!t->HaTerminado)
  59:         ;
  60:     Console::WriteLine("Finalizó");
  61:       Console::ReadKey(true);
  62:     return 0;
  63: }

Aparentemente es algo más complicado que el anterior, pero sólo a simple vista. Luego pondremos un resumen del mismo.

Hemos creado una clase llamada Tareas que engloba varios métodos y propiedades. En primer lugar tenemos el método ThreadHazTareas(), que es el método que hará de hilo. En él tenemos un bucle infinito que realiza una tarea en cuatro etapas que podría significar una máquina de estados de cuatro fases que deban hacerse en secuencia y no ser interrumpidas. Observamos que la primera sentencia dentro del hilo es

   1: m_bThreadIsFinished=false;

Estamos afirmando que el hilo está es marcha. Tras la realización de las tareas, tenemos otro bloque interesante:

   1: if(m_bTerminateThread)
   2: {
   3:     m_bThreadIsFinished=true;            
   4:     return;
   5: }

Aquí estamos indicando que si externamente se nos ha indicado que el hilo debe terminar, lo terminamos aquí y sólo aquí. Estamos evitando que el proceso que creó el hilo lo mate en una situación inadecuada.

Lo siguiente es trivial. Una propiedad que nos devuelve cierto si el hilo ha terminado (es decir, se ha ejecutado el código anterior), otro método para arrancar el hilo y uno más para decirle que debe terminar.

En main() lo habitual: la creación del objeto, la iniciación de las tareas, un rato de espera y el envío de la orden de finalización, con la espera hasta que termine. En un programa más serio, dicho código podría ir en un temporizador, o incluso como ya hemos indicado antes, con la llamada de un delegado.

También podríamos haber utilizado m_thread->IsAlive pero al autor le gusta esta forma, ya que tiene más control.

Muy importante

Un detalle que hemos dejado para el final: la palabra volatile delante de los dos valores lógicos. ¿Qué significa? ¿Podemos obviarla? No, de ninguna manera. Estamos instruyendo al compilador para que no presuponga que esas variables no van a ser modificadas externamente, es decir, le estamos diciendo que compruebe la variable siempre y que tanto a la hora de leerla o de escribirla, use la propia variable y no un valor en algún tipo de caché.

Una variable de tipo bool es lo que se llama una variable atómica, es decir, está garantizado que la variable se actualiza mediante una sola instrucción máquina y que no hay pasos intermedios, y si pudiera haber alguno, lo hemos eliminado al calificarla como volátil.

Si no fuera así, podría ocurrir que el hilo principal accediera a la misma y, a medio hacer alguna operación sobre ella, el sistema conmutara al hilo y que éste también realizara alguna otra operación. El estado final de la misma quedaría completamente indeterminado, y es un problema habitual con los programas multihilo. Para eso están los bloqueos y las secciones críticas, pero nosotros nos las hemos arreglado sin necesidad de ellos y sin complicarnos mucho la vida, dado que es físicamente imposible que se acceda de forma simultánea a la variable desde dos hilos diferentes ya que si el micro está leyéndola o escribiéndola, la operación se realiza en una sola instrucción máquina.

Se podría poner una última pega a esta aproximación simplificada de una sección crítica, y es que justo una vez la variable haya sido leída e introducida en un registro del micro, el sistema operativo conmutara a otro hilo y éste variara su valor en memoria. Pero no existe ningún problema, ya que cuando retornáramos, tendríamos el valor anterior y tomaríamos el bueno en el siguiente ciclo ya que se ha especificado como volátil. Como se trata de una variable bi-estado, y sólo la usamos una vez, no existe problema de ningún tipo. Ojo, pero siempre, siempre, calificándola como volátil, ya que entonces el compilador podría cachearla dentro de un registro y no atender a cambios externos.

Por último, una captura de lo que le ha ocurrido al autor en su máquina:image

Finalmente comentar que aunque los ejemplos estén escritos en C++/CLI, los conceptos son perfectamente válidos para cualquier lenguaje en el que una variable de tipo bool sea una variable atómica, como C# ó C++.

Hemos leído: C++/CLI in Action (Nishant Sivakumar, Manning)

Esto sí que es un libro técnico, y lo demás son tonterías. En otras palabras: el libro es bueno, muy bueno, de lo mejorcito que he leído de cualquier tema técnico relacionado con la programación.

Como el título indica, se trata de una obra eminentemente práctica y profunda que trata la mayoría de los aspectos del C++/CLI sin entrar para nada en detalles sobre el .NET excepto al final, cuando explica algo sobre WPF y tecnologías asociadas.

El libro parte de que al menos sabes C++ a nivel de programador profesional, y si tus conocimientos incluyen algo de C++/CLI, mejor que mejor. Dividido en tres partes de tres, dos y tres capítulos respectivamente, lo más interesantes a mi modo de ver son las dos primeras partes.

Tras una introducción a C++/CLI explicando todos los aspectos básicos, como la declaración y la sintaxis del lenguaje, prestando especial atención al tema de los manejadores (el equivalente a los punteros del código nativo) y al encajado y desencajado y consecuentemente cómo y dónde se asignan las variables dentro del .NET, en el segundo capítulo nos cuenta las extensiones del lenguaje.

Vemos las propiedades, los delegados y los eventos, los arrays CLI, todo ello mezclado y relacionado con el código nativo, las diferencias que hay entre los dos modelos y, sobre todo, profundizando sobre los temas tratados. Pero no al nivel del libro de Heege, sino que el tema viene tratado desde un punto de vista práctico, es decir, en relación a lo que un buen programador debe saber a la hora de conocer qué está usando y por qué está usando eso y no otra cosa. La verdad es que me asombra la profundidad y la sencillez con la que están expuestos los temas en todo el libro.

El último capítulo de la primera parte cubre lo que nos queda: la semántica de la pila y la destrucción determinista, la sobrecarga de funciones, los genéricos y las plantillas y cómo todo ello se interrelaciona entre sí, explicado con la profundidad adecuada para que sepamos cómo y por qué funcionan las cosas.

La segunda parte todavía es más interesante que la primera, y de hecho es el núcleo y razón de ser de todo el libro. El capítulo 4 nos habla de los punteros en .NET. Sí, de los punteros, ya que una referencia no es más que un puntero móvil, y como tal se puede fijar y operar con él como si fuera uno. O apuntar al interior de un objeto y trastearlo. Luego nos cuenta cómo utilizar una biblioteca nativa en un programa manejado y justo al revés, una biblioteca manejada en un programa nativo, y de paso nos explica cómo convertir variables nativas a manejadas y viceversa. El siguiente punto que trata es el uso de elementos nativos dentro de clases manejadas y de nuevo lo contrario, con todos los aspectos que debemos tener en cuenta a la hora de operar de esta forma. Incluso nos da clases que nos hagan de puente. Finalmente el capítulo termina explicándonos cómo podemos convertir delegados en punteros a funciones y cómo incluso podemos ejecutar un bloque de código nativo desde código manejado (cosa que haría saltar al DEP si se hiciera todo desde código nativo), etc. Verdaderamente apasionante, interesante y práctico (bueno, lo último no mucho).

En el quinto volvemos a la mezcla de código nativo y manejado, profundizando en las conversiones, para pasar a ver cómo podemos envolver una biblioteca nativa con un código en C++/CLI para que pueda ser utilizado por otros lenguajes .NET sin necesidad de Interop de ningún tipo, y finalmente termina explicándonos cómo envolver una DLL MFC y COM. El último punto que trata tiene un interés enorme: una misma DLL que se puede usar tanto para código manejado como nativo, todo un tour de forcé.

La tercera parte me ha resultado menos interesante, ya que es una cosa que al menos a mi no me importa mucho, pero que sí puede resultar valiosa para aquellos que tengan mucho código antiguo y quieran aprovechar las nuevas características del .NET 3.0 sin tener que reescribir toda su aplicación.

Tres capítulos que cubren cómo insertar controles .NET en un cuadro de diálogo MFC (y lo contrario, un control MFC en una Ficha manejada), lo mismo con una ventana WPF con y sin XAML, y finalmente repetimos, pero ahora con WCF. Evidentemente se tratan las tres combinaciones, código WPF/XAML/WCF en código C++/CLI que directamente no soporta dichas APIs, código WPF/XAML/WCF en código C++ nativo y sus inversas.

Resumiendo: un libro enormemente interesante que trata todos los temas desde un punto de vista práctico y útil y que, creo, no deja casi nada del lenguaje C++/CLI sin cubrir, incluso ampliándolo a veces mediante alguna que otra idea de concepto.

Verdaderamente resulta el libro de cabecera para cualquiera que desee aprender C++/CLI de verdad, tras haber estudiado cualquier otro libro básico o incluso las entradas de este BLOG.

El libro es este.

Confieso que he vuelto a pecar (de iLiads, UMPCs y otras yerbas)

Pues sí, finalmente ha podido el gusanillo y he terminado comprándome otro UMPC. Quería esperar a mediados del año que viene, que seguro bajan de precio y salen productos mejores, pero el hecho de estar asqueado del iLiad (luego digo por qué) y de que vendí mi ASUS R2 a finales del verano, han podido sobre mi vena conservadora (y pensar que antes leía con mi PDA y me sobraba…)

Pero vayamos por partes. He dicho que estoy asqueado del iLiad, y es cierto. No es que sea un mal producto, que no lo es, pero adolece de ciertas deficiencias que el fabricante no consiente en paliar. La más grave de todas es el tema del formato Mobipocket. En pocas palabras: es una mierda. Es lento, no tiene opciones para ajustar excepto el cambio de tamaño de letra (que no de fuente), y el uso del diccionario es esperpénticamente incómodo. Cuando quieres buscar una palabra, has de hacer clic en el icono correspondiente de abajo (solo una vez en cada sesión de lectura, ya podría guardar el estado), luego en la palabra. Entonces se… abre… una… ventana… con… la… lista… de… las… coincidencias… (tarda tres o cuatro segundos a abrirse), aunque sólo haya una posibilidad. Luego tienes que hacer clic sobre esa posibilidad, y vuelven a transcurrir otro puñado de segundos, de tal modo que cuando ves la definición ya no te acuerdas de qué estabas leyendo.

A eso hay que añadir la enorme lentitud para abrir los documentos y la también desesperante parsimoniosidad con la que pasa la página… El lector de PDF ni soporta diccionario ni lo tendrá jamás de los jamases… De tal modo que no puedo leer en inglés con el iLiad, ya que en general tengo que buscar al menos una palabra cada hoja…

Por lo demás el iLiad no está mal, es robusto y el tamaño de la pantalla es más que suficiente para leer un A4… si el zoom funcionara como debe, que no lo hace. O si tuviera algún tipo de reflujo, que tampoco lo tiene.

Resulta de todo ello que para leer cómodamente con el iLiad tienes que crearte tú los PDF con un formato muy específico. La ventaja de esto es que, aparte de dicho formato, puedes ajustar el contenido como te de la gana, tanto el número de columnas como el interlineado como casi cualquier otra cosa. El inconvenientes es que para cada libro que quieras leer en el iLiad tienes que crearte el PDF.

Añadamos que la promesa de las 15 horas de lectura sin cargar se quedan en unas seis u ocho, y que Irex parece que quiere matar su propio producto, ya que en general la respuesta del soporte técnico suele ser “no se va a implementar”, “funciona así”, etc., eso cuando te la dan. El software que acompaña al producto para integrarlo con un PC no funciona bajo Vista, y apenas bajo XP, las nuevas versiones del Mobipocket para PC siguen sin reconocer al iLiad bajo Vista, y por supuesto no hay visos de que se vaya a solucionar.

Lo único bueno del iLiad es la comunidad, que poco a poco le va metiendo mano pero que, como no le ven mucho futuro al producto, pues cada vez hay menos innovaciones…

Mi hermano, aprovechando que un amigo hacía un viaje a Estados Unidos, le ha regalado a su novia un Ereader de Sony… Tras una conversación con la chica, si el iLiad es una castaña, el Ereader ni os cuento. Todavía tarda más a pasar página, el formato PDF soportado apena sirve para nada, y encima es completamente propietario…

En pocas palabras: una tecnología muerta apenas nacida por la evidente incompetencia de quienes la están implementando…

Pero volvamos a los UMPC. El lector habitual de este BLOG sabrá mi también no muy exitosa historia con un ASUS R2H. Un producto bastante malo, con una calidad de pantalla muy mejorable, lentísimo a más no poder (incluso después de haber quitado toda la morralla de programas mierdosos que suelen acompañarlo –en origen toda la basura cargada ocupaba 500 MB de los 512 de memoria), con bastantes bugs en el software de acompañamiento imprescindible (como el gestor de energía y de teclas); una duración de la batería risible bajo el funcionamiento de mayor ahorro de energía… Del GPS integrado ni hablemos. En plena carretera, con el aparato fuera del coche, apenas coge un par de satélites…

Al final acabé vendiéndolo a un amigo que aun con las advertencias antes citadas, decidió comprármelo. Lo cierto es que la última vez que hablé con él me dijo que iba bastante bien, cosa que no me creo mucho. Pero para leer el correo en sus viajes sí que le sirve.

Bueno, pues como también comenté, la primera vez que vi un UMPC fue con Tella en una FNAC en Madrid; en concreto fue el Samsung Q1, y la verdad es que aparentemente iba bastante mejor que el R2H, máxime cuando me lo confirmó el propio Tella, que se había comprado uno.

Desde entonces he tenido la espinita clavada. Quiero un UMPC que funcione, así que me he liado la manta a la cabeza y me he comprado el Q1U 000/SES. La diferencia con el 002 es que el primero no lleva modem HDSPA y el segundo sí, cosa que no me importa (si no me hubiera comprado el otro).

Ayer tarde me lo traía a casa. Viene con la batería cargada, así que no hay que esperar un porrón de horas antes de encenderlo.

¡Menuda diferencia!

Pues sí. Tras las cosas iniciales, configuración del Windows, copia de seguridad en la partición oculta, etc., ya tenía un equipo plenamente funcional… Con Windows XP Tablet. El lunes llamaré a Samsung a ver por qué lo anuncian con Vista pero lo sirven con XP, aunque tampoco me preocupa mucho: prefiero el XP, ya que irá más suelto.

Otra de las cosas anunciadas y que no trae es el lector de huella digital. Por lo demás, igualico que en la web: las dos cámaras, el tecladito ese, dos puertos USB, uno VGA, una salida de auriculares, etc.

Como cosa curiosa, la correa que trae se puede enganchar en la parte trasera de forma que queda como un asa para pasar la mano…

Mientras pasaba el Windows Update (el Wifi lo cogió a la primera), me fijé en que se veía casi tan mal como mi ex ASUS… lo que solucioné activando el ClearType (si serán cazurros). Aunque el tamaño de la fuente es diminuta (la resolución es de 1024×600 en un mismo tamaño de pantalla de 7” –el ASUS y el Q1 tenían 1024×480), se puede leer agradablemente.

Otro tema es el puntero con un tamaño de pantalla tan pequeño. Tras una calibración con linealización conseguí que funcionara perfectamente.

Lo bueno son los complementos esos mierdosos. Trae el McAffee pero sin instalar, es decir, trae un pre-instalador que se desinstala sin mayor problema. ¡Bien! Trae varias utilidades chorra, como una especie de Media Player bastante chungo y que funciona peor que el Media Player de Windows. Por lo demás trae un chequeador del estado del software y hardware, pero la perita en dulce es el gestor de botones.

Justo encima de la pantalla, a la derecha, trae 4 botones sensibles al tacto. Dos para subir y bajar el volumen, y uno para trastear con la configuración de los periféricos: ajustar el brillo, cambiar entre monitor externo e interno, encendido/apagado del Wifi, etc. Y funciona, no como en el ASUS.

Luego hay un cuarto pulsador que abre una aplicación que permite configurar la especie de cursor que lleva al lado derecho, permitiendo diferentes “grupos”. Yo me he hecho uno para leer con el Mobipocket, y otro para los PDF. El problema es que tienes que cambiarlos a mano (es decir, abrir el programa y seleccionar el que quieras). Con el del PDF cambio a pantalla completa/ancho de documento y adelante/atrás.

También trae un gestor de energía que parece ser que también funciona bien.

De momento no lo he pillado en ningún renuncio ni problema, y arranca bastante rápido… cuando arranca rápido, es decir, como con cualquier Windows, a veces ciertos arranques tardan mucho…

Una de las cosas que me tocó un poco los OO hasta que descubrí cómo cambiarla ya que no viene ni en el amplísimo manual online ni en los de papel, es que el aparato arranca con el Wifi encendido. Pero tras conseguir entrar en la BIOS (hay que apretar el botón R del ratón), había una opción que graciosamente te permite cambiar entre arrancar con él encendido, apagado o conforme se quedó la vez anterior.

Sólo lo he cargado una vez… y es que la batería me ha durado más de tres horas y media con el Wifi encendido y dándole caña por un tubo… Lo mismo ni me hará falta comprar la batería extendida…

Hemos leído: Expert C++/CLI (Marcus Heege, Apress).

Para empezar diremos que se trata de un libro muy técnico y demasiado avanzado en algunos aspectos, tanto, que más del 50% del mismo no te sirve para nada como conocimientos prácticos. Eso sí, el resto es toda una gozada en cuanto a detalles de esos que hacen que te tires un par de días buscando por qué te falla tu programa y qué estás haciendo mal, para descubrir una acumulación de características más o menos oscuras del lenguaje, de la herramienta de desarrollo, del sistema operativo o incluso de todos a la vez que determinan el fallo. Mención especial sobre este tipo de cosas es el capítulo dedicado a los destructores y a toda la parafernalia relativa al Dispose y compañía.

El libro presupone que sabes C++/CLI, y no poco. Apenas entra a nombrar características de C++ si no es cuando está explicando cosas relativas al Interop y que afecten directamente al propio C++/CLI. También está en cierta medida centrado en el .NET, o más bien en aquellos detalles del .NET que no están presentes en otros lenguajes pero sí en C++/CLI; al autor no se le caen los anillos si tiene que mostrarnos código MSIL o pseudocódigo para explicarnos alguna característica preponderante.

Los primeros capítulos son flojitos –es un decir-, y comienzan donde otros libros terminan: modelos de compilación, puntos de entrada manejados en DLL, envoltura de bibliotecas nativas, para luego pasar a contarnos un montón de cosas sobre los tipos del .NET, cómo se almacenan en memoria, diferencias entre tipos valor y tipos referencia y cosas así. La verdad es que esta parte se suele encontrar en la mayoría de libros generales sobre el tema, pero al estar enfocada a C++/CLI no creo que se cubra en muchos otros volúmenes.

Lo siguiente es una somera explicación de los ensamblados, la E/S, el modelo de excepciones, acceso a Web, funciones de ayuda integradas en el código y muy poco sobre ficheros de configuración. Otra vez algo que suele aparecer en otros libros, pero aquí está tratado de pasada, como si el autor lo mencionara para recordarnos lo que pudiéramos haber olvidado cuando estudiamos C#, o más bien nos viene a decir que en C++/CLI también tenemos de eso.

Y ya nos olvidamos de los caramelitos. Ahora viene el hardcore puro y duro. Un capítulo sobre ensamblados, cómo funciona la CAG, metadatos, reflexión y cosas de esas. Finaliza con un toque de serialización de componentes.

Estamos ya en el capítulo 5. Este también es otro capítulo típico, que no puede faltar en ningún libro, y es que nos cuenta sobre los tipos, cómo se inicializan, clases, herencia, componente, etc. Pero el modo de tratarlo no es el habitual, profundizando, sino que a vuelapluma nos explica que C++/CLI tiene de todo eso, y que si no los sabías, aquí no vas a encontrar quien te lo explique.

El siguiente capítulo tiene su miga. Se centra en aspectos técnicos sobre la construcción, cómo funciona el polimorfismo en C++/CLI y otros menesteres similares.

El tema de la integración con Visual Studio comienza, de nuevo, en donde otros libros terminan. Modelos de compilación, excepciones a través de ensamblados, mezcla de ficheros objeto nativos con manejados, ensamblados mixtos, cómo capturar excepciones de C++ y del Sistema Operativo dentro de un programa manejado (y viceversa), macros predefinidas y algo sobre plantillas. Ufff.

El siguiente también es típico, je je. Mezcla de tipos manejados y tipos nativos. Cómo tener un tipo manejado dentro de un tipo nativo, qué ocurre cuando tenemos un puntero a un tipo nativo dentro de uno manejado, objetos proxy, conversión de unos tipos en otros…

Y el que le sigue viene como consecuencia del anterior: transiciones manejadas/no manejadas. Cómo se producen, qué código se ejecuta y cómo. Por qué a veces se produce una doble o triple transición y la forma de evitarlas. Todo ello con medidas de tiempos (el código se publica al final del libro) y destripado de código vario.

Si yo digo: “Envolviendo bibliotecas nativas” todo el mundo sabe de qué hablo, ¿no? Pues no. Volvemos a empezar donde otros acaban, es decir, el autor supone que ya sabes hacerlo. Lo que no supone es que sabes cuales son las pifias y las ineficiencias más comunes.

Luego viene lo que he comentado en el primer párrafo y, aparte de los apéndices, el libro termina con jugoso capítulo explicando el startup de un programa .NET y mixto, es decir, destripa qué hace el cargador de arranque y qué hay antes del main() manejado y nativo. Toda una gozada.

Bueno, hasta aquí llegan las trescientas y pico páginas del libro técnico más denso que recuerdo haber leído (hay otro que se titula Arquitectura de computadores, que todavía no he leído –o más bien no he terminado de hacerlo-).

Es un libro que no recomiendo, por lo menos para avanzar en el estudio de C++/CLI, ya que, como he comentado, tiene poco de práctico, aunque su lectura y aprehensión significa una proeza intelectual bastante considerable.

La sensación que te da al leerlo es que el autor está tratando los temas por encima, de pasada y corriendo (salvo algunos capítulos concretos), lo que es completamente cierto. Como el API de Windows, sería más que una proeza llegar a aprender y conocer por completo y exhaustivamente todo lo que el .NET tiene para ofrecernos, sobre todo si nos centramos en los tiquismiquis del sistema.

Pues bien, hasta aquí llega esta entrada, y os emplazo para un futuro no muy lejano en el que comente dos nuevos libros, C++/CLI in action y mi actual libro de cuarto de baño: Reversing. Secrets of Reverse Engineering.

Se me olvidaba: el libro.