Far from SF

Ya disculparán esta temporada tan alejado de ustedes, pero me encuentro inmerso en los exámenes de septiembre. Sí, ya sé que suena ridículo, se trata de la famosa adaptación al calendario europeo… estoy con el tiempo justo, así que les dejaré a ustedes la valoración sobre la conveniencia o no de tal calendario…

Volveré el sábado 15, tengo que hacer un último esfuerzo durante esta semana, como comprenderán. Les adelanto que habrá novedades muy interesantes durante este verano, espero que estén aquí para vivir juntos todos estos cambios que se avecinan.

?nimo y suerte a quienes estén inmersos en exámenes, oposiciones o sencillamente en el mar :-)

A grandes males, grandes remedios

Hace poco más de un mes escribí un artículo sobre el incumplimiento de los estándares en cierta plataforma de la web de la Universidad de Salamanca. El artículo fue muy meneado (¡gracias de nuevo, luces!), y a SF llegaron unos amables forasteros que nos proporcionaron su solución al problema. Sólo faltaba adaptarla a la web de la USAL, lo cual ha sido posible gracias al trabajo de la gente de BolaDeBillar69.

El problema consiste sencillamente en que un menú de la web no funciona con ningún navegador que no sea el Internet Explorer, lo que imposibilita en la práctica el acceso a las diferentes secciones. ¿Cómo solucionarlo? Pues yo se lo explico.

GreaseMonkey es una extensión para el Firefox que permite aplicar cambios sobre las webs de manera dinámica. Esto nos permite especificar modificaciones que deseamos que se realicen sobre determinadas webs, cuya presentación o comportamiento se adaptará a nuestros deseos.

La extensión puede descargarse desde aquí, parece una herramienta bastante interesante, por lo que no estará de más tenerla a mano. Una vez instalemos GreaseMonkey deberemos añadirle el script que hará que la web de RedCampus se modifique de acuerdo a nuestras espectativas. Este script ha sido realizado por BolaDeBillar69 y lo podéis descargar directamente desde aquí, bajo licencia GPL.

Ahora sólo queda que la universidad reconozca que la situación es vergonzosa y la rectifique, lo cual no creo que suceda… al menos hemos sabido buscarnos la vida :-) ¡Gracias a todos los que colaboraron!

El virus Chernobyl (CIH)

storycomputervirus.jpgHace unos días veíamos algunas cosas sobre los virus y cómo funcionan, y dejábamos pendiente una explicación detallada del virus CIH, popularmente conocido como Chernobyl. Pues bien, les presento un análisis pormenorizado de esta simpática criatura: cómo funciona, cómo infecta a otros programas y cómo logra dejarnos el ordenador como salido de un accidente nuclear.

Y aclarado esto, empecemos diciendo que una parte importante de los programas es el control de errores: cuando un programa “la pifia?, el sistema retoma el control e intenta salvar los muebles. En el caso de los virus, si hay un error durante la ejecución no nos interesa para nada que el sistema se entere, por lo que vamos a modificar el manejador de las excepciones para que el SO no se entere de nada. Para los amantes del rigor, el virus modifica el SEH (Structure Exception Handle). Así, si hay un error, en principio el virus retendrá el control de la situación.

Lo siguiente es ver cómo modificar los permisos con los que el programa se ejecuta. En Windows la seguridad está configurada en forma de anillos, que delimitan determinadas zonas. El primer anillo corresponde a los procesos del SO, y las aplicaciones que se ejecutan en este anillo tienen acceso a todo el sistema. El siguiente anillo tiene menos permisos, y así sucesivamente.

Las aplicaciones de usuario se ejecutan en principio en el tercer anillo, el llamado (seguro que no lo imaginan) “Ring 3?. La idea es que nuestro virus se convierta discretamente en una aplicación “Ring 0? para poder acceder a todo el sistema.

Para hacer esto es necesario vulnerar el mecanismo de protección, pero estos mecanismos en Windows 9x no estaban demasiado pulidos, de modo que el virus se servía de determinadas vulnerabilidades para hacer este cambio de anillo. Para quienes veneran el rigor técnico sobre el bien y el mal, habrá que decir que semejante cosa se consigue modificando la IDT (Interrupt Descriptor Table, Tabla de descriptores de interrupciones) y generando después una excepción, lo que le permitirá ejecutar su código con los permisos más elevados del sistema.

Una vez “trapicheado? el sistema, el virus se coloca en la memoria como un programa cualquiera y, como ya sugerimos en la entrega anterior, espera a que se ejecute alguna aplicación. Previamente habrá devuelto el control al programa “portador? para que haga lo que tenga que hacer.

Bien, ya tenemos nuestro virus en memoria esperando para atacar. Cuándo se cargue en memoria una posible víctima intentará la infección: Lo primero será comprobar si es un ejecutable de Windows y que no haya sido infectado anteriormente ¿cómo sabe esto último? Cuando infecta un fichero escribe un valor distinto del original (cero) en el primer byte antes de la cabecera del ejecutable.

Lo siguiente es “inyectar? el código maligno en el ejecutable, sin aumentar el tamaño del mismo. Pero esto no parece sencillo… ¿cómo lo logra? Digamos que en las cabeceras de los ejecutables de Windows se almacenan diversos datos relativos a las funciones que importan o exportan, información que el programa necesita y demás. Todos estos datos se organizan en “objetos?, que tienen que estar organizados de una forma muy concreta dentro del fichero del programa. A su vez, hay que guardar dónde se encuentran estos objetos, qué tamaño ocupan… todo esto se hace con tablas, por lo que el principio del ejecutable consiste en un montón de descriptores y tablas.

Entre todas estas secciones queda un espacio que no se usa, que es el que aprovecha el virus para escribir su código sin incrementar el tamaño del programa portador. Previamente habrá comprobado que tiene el espacio suficiente para infectar el fichero, y en caso contrario no lo hará. Por desgracia, CIH es una pequeña maravilla en cuando a tamaño, ya que sólo ocupa aproximadamente un 1KB, con lo cual casi siempre encontrará huecos en los que instalarse.

Una vez insertado su código, modifica la cabecera para que la dirección de entrada al ejecutable quede apuntando a su propio código.

El payload (carga dañina)

El payload varía entre versiones, pero generalmente lo que hace es obtener el mes y el día y comprobar si es el día 26 de abril. En caso afirmativo, comienza a sobreescribir el chip de la BIOS (si es de tipo flash).

Seguro que algún lector se pregunta algo así como “¿pero la BIOS no es una memoria de sólo lectura? ¿cómo consigue escribir entonces??. La respuesta es que la BIOS es de sólo lectura en principio, pero que generalmente se puede modificar aplicando cierto voltaje*. Normalmente las placas bases impiden que se suministre este voltaje al chip, pero cuando el virus CIH se extendió, tal protección solía venir inhabilitada para facilitar las actualizaciones de la BIOS, con lo que la información quedaba expuesta.

_327903_cih_300.jpg

Cuando termine con esto, localizará los discos duros del sistema y escribirá datos aleatorios sobre los primeros 2048 sectores, dejándolos inutilizables.

El CIH se encuentra prácticamente extinguido a fecha de hoy, gracias a que los sistemas Windows 9x van cayendo en desuso en favor de los basados en Windows NT (saber más). Se trata de un virus muy bien programado, toda una muestra de la belleza que puede encontrarse en la destrucción… Su creador, el taiwanés Chen Ing Hau, (nótese que sus iniciales son las letras C.I.H.) fue detenido en el año 2000.

* Si alguien tiene curiosidad sobre cómo puede ser esto, debe saber que amenazo con dedicarle una entrada al tema en breve.

Actualización: Menear el artículo (¡gracias, Taikochu!)

¿Qué es un virus?

virusinfluenza.jpgSeguro que se ha enfrentado ya a una de estas simpáticas y fascinantes criaturas que son los virus informáticos. O si no usted, probablemente algún conocido suyo los haya sufrido. O tal vez esté infectado y ni siquiera lo sospeche… (sienta cómo la paranoia se apodera de su mente)

El origen de los virus hay que buscarlo a mediados de los 80, cuando unos técnicos de los laboratorios Bell inventaron un juego denominado “Code Wars?, consistente en crear programas que combatían entre ellos por conseguir el control de la memoria del equipo. Hay mucho que decir sobre los virus, y mucho que discutir sobre el tema, pero de esta entrada me interesa que comprendan cómo funcionan y cómo son diseñados. Y es que conociendo bien a nuestro enemigo tendremos más probabilidades de saber defendernos.

Para empezar, hay que acotar un poco la terminología, ya que el término “virus? se utiliza a menudo para referir realidades muy diferentes. No todos los programas dañinos son virus estrictamente, y no todos los virus son dañinos directamente.

Hay que entender un par de ideas antes de entrar en consideraciones más complejas: la primera es que un virus no es más que un programa. Un tanto especial, sí, pero un programa a fin de cuentas. La segunda idea es que los virus informáticos no se parecen tanto a los biológicos: un virus biológico utiliza el material celular para autorreplicarse, mientras que un virus informático modifica los programas que “infecta? incluyendo una copia de sí mismo.

De modo que llegamos a algo que puede parecerse a una definición: un virus es un programa informático que es capaz de modificar a otros programas inyectándolos cierto código que altere su comportamiento.

¿Cómo nos infectan los virus?

Vamos a referirnos en este punto a los virus residentes tradicionales. Los últimos virus aprovechan generalmente fallos en el MS Outlook o en el MS Internet Explorer para infectar los computadores y extenderse por Internet, pero los más bonitos e interesantes son los antiguos.

En principio sólo hay un programa con el código puro del virus: este programa padre irá infectando a los demás, que actuarán a su vez como nuevos padres e irán propagando la infección. Lo que ocurre cuando se ejecuta un programa infectado en el ordenador es más o menos así:

  1. Al ejecutarse, el virus se carga en memoria y se empiezan a leer las instrucciones malignas.
  2. Estas instrucciones modifican ciertos parámetros del sistema operativo para poder hacerse con el control cuando se ejecuten otros programas.
  3. Espera a que otros programas se ejecuten. Cuando los programas se ejecutan, su contenido es transferido a la memoria principal, donde se encuentra el virus, momento que puede utilizar para infectarlos.
  4. Cuando se ejecute un programa, el virus obtendrá el control por un instante. Entonces copiará la parte de código infeccioso en el programa víctima, de manera que siempre se ejecute antes que el programa original. Una vez modificado el nuevo portador, dejará que se ejecute normalmente, y en principio el usuario no percibirá ningún cambio.

A partir de esta idea, podemos refinar el virus cuanto queramos: podemos hacer que compruebe si el programa que va a infectar es un antivirus y que intente cerrarlo en caso afirmativo, o que se envíe a si mismo por email para replicarse más rápido.

Algunos virus, los denominados polimórficos, “mutan?, modificando su código para evitar ser detectados, otros se instalan en el arranque del equipo… Unos buscan en el disco a otros programas y los modifican, aunque no se ejecuten, otros infectan directamente a quienes comparten la memoria con ellos… Hay tanta diversidad como informáticos retorcidos.

Básicamente un virus tiene que tratar de infectar cierta cantidad de programas: si infecta muchos el ordenador se volverá más pesado y el usuario podría darse cuenta. Si infecta pocos, apenas se extenderá. También tiene que intentar pasar desapercibido: utilizar polimorfismo, variar poco el tamaño del programa que infecte, vigilar la presencia de antivirus o de otros virus…

Algunos virus incluyen lo que se llama “payload?, que es como la carga dañina de los misiles: supone la temida manifestación del virus, y es cuando podemos agarrarnos a la silla y temblar de miedo. En algunos casos, la temida carga se reduce a un mensaje gracioso o político. En otros, puede dar lugar al borrado de ficheros o el colapso de la red.

Un ejemplo tristemente famoso

El virus CIH, también conocido como Chernobyl (ya que se se activa el día 26 de Abril) es un ejemplo del daño terrible que pueden causar estas pequeñas bestias. Este virus sólo es capaz de actuar bajo Windows 9x, infecta cualquier archivo .EXE al que tenga acceso y tiene uno de los “payload? más destructivos: sobreescribe las instrucciones del chip de las BIOS de tipo flash dejando el arranque inutilizado y borra el contenidos de los discos duros.

Más adelante hablaremos largo y tendido sobre esta criatura del señor.

Como el código de Linux yo soy libre

linux_win0.png

Hoy les dejo con un apunte filosófico y otro más distendido sobre el software libre. El apunte filosófico proviene del magnífico blog Cuaderno de Campo, que hace tiempo nos regaló dos entradas sobre el tema: una y dos :-P Son francamente estupendas, no se lo pierdan o me pondré triste y lloraré.Y ahora el apunte más distendido. La cosa tiene unos cuantos años, y no sé cómo me he acordado de ella… el caso es que la recupero para ustedes. ¿Se acuerdan de la canción «libre» de Nino Bravo? Bueno, pues aquí les dejo con la versión «libre» (nunca mejor dicho):

Lleva apenas veinte horas y ya está
buscando un Service Pack
Porque le soltó una pantalla azul
el Visual C++

Luego navegando por la World Wide Web
lo vio con claridad
un compilador llamado GCC
rezaba sin parar

(ESTRIBILLO)

Libre, como el código de Linux yo soy libre
como el tar
Libre, como el bison, como el perl y como el cron
que puedes compilar
Libre, como Debian que me ofrece herramientas sin pagar
también tengo Red Hat
que viene con manual
ahora sí sé lo que es la libertad

El código abierto es su nueva fe
programa en GTK
ahora es un tipo muy muy popular
en la lista de Bugtraq

Estaba tan harto del NT
que decidió cambiar
ya maneja Linux y FreeBSD
en cualquier terminal

(ESTRIBILLO)

Investigando un poco más, he encontrado la versión con música incluida. En este equipo no tengo nada que pueda reproducir ficheros .ogg, así que no me responsabilizo de cómo suene, aunque tengo un buen presentimiento…

La Ingeniería del Software (y III). Diseño

Finalizamos ya la serie más leída y menos comentada de la breve historia de SF. En realidad la cosa daba para algo más, pero quizás quien escribe esto ha cometido el error de adentrarse en un terreno demasiado enredado, por lo que vamos a efectuar un parto por cesárea, para terminar la serie sin más complicaciones. Bien, en las anteriores entregas hemos visto cómo recoger los requisitos de nuestra aplicación y hemos dado el salto hacia un análisis orientado a objetos del problema que nos ocupa (sumar dos números).

Ahora nos queda concretar todo eso: el diseño. Es la parte más crítica, pero si se ha realizado un buen análisis no debería suponernos mucho esfuerzo. En principio tendremos que concretar el modelo estático y adaptarlo a la implementación que vayamos a realizar. Posteriormente, haremos lo propio con el modelo de interacción. Lo único que va a cambiar aquí van a ser los nombres de los diagramas, las clases y las operaciones… podemos verlo como un refinamiento del trabajo anterior.

No estaría mal rellenar algunas plantillas con las operaciones que realizarán las clases de la aplicación, para tener una visión más ordenada de los algoritmos. También es el momento de diseñar el modelo de datos y prever si necesitaremos aplicar algún objeto adicional para manejar una base de datos. Una vez tengamos terminado el diseño, podremos pasar a la fase de implementación, donde codificaremos el programa en cuestión. En realidad eso ya es lo de menos, con el gigantesco estudio previo que hemos realizado será cuestión de un par de minutos.

La Ingeniería del Software (II). Análisis

En la entrega anterior ya dejamos medianamente esclarecido qué es lo que queremos que haga nuestro programa (los requisitos). Ahora vamos a empezar a pensar en cómo lo haremos… A quienes todo esto les suene demasiado raro, les sugiero que no se líen con los detalles y se queden con la idea: construir software es más sistemático y complicado de lo que puede parecer a primera vista. ¿Por qué tanto artítulo entonces? Porque estamos intentando demostrarlo :-P

Ya se han acabado las plantillas y los dibujos de muñequitos interactuando con el sistema abstracto, ahora se trata de concretar cómo será internamente ese sistema. Dijimos por encima que íbamos a utilizar una adaptación del Proceso Unificado, lo cual significa, en el fondo, que utilizaremos una tecnología orientada a objetos.

Aun no le he dedicado ningún artítulo en profundidad al tema, pero tendré que terminar intentando explicar qué es eso de la orientación a objetos. Como idea rápida, hay que pensar que los programas son tradicionalmente concebidos como series de instrucciones que se ejecutan en orden, aunque hay un enfoque alternativo: podemos construir el software pensando en «entidades» que interactúan entre sí para lograr un objetivo común (en nuestro caso, sumar dos números). Al primer enfoque se le conoce como «programación estructurada» y al segundo «programación orientada a objetos», aunque pueden combinarse ambos en el mismo programa, para desesperación de mis amados puristas :-P

Volvamos a nuestro programa. Una entidad que suma dos números puede concebirse como una «calculadora», por lo que ya tenemos nuestro objeto principal. Una vez hayamos descubierto todas las «entidades», elaboraremos un diagrama de clases, que podría quedar así:

clases.png

Es decir, tendremos una entidad llamada «Calculadora» con la operación de sumar dos números. Generalmente, un diagrama de este tipo contiene varias decenas de clases con relaciones entre ellas, atributos y demás, recuerde que se trata de un ejemplo reducido y debilitado… Me estoy dando cuenta de lo complejo que puede resultar comprender todos estos conceptos a alguien ajeno al mundillo, pero el fracaso no es una opción, habrá que seguir adelante hasta terminar el trabajo :-)Esta parte del análisis es conocida como «modelo estático», porque resume las entidades del programa sin reparar en cómo se comunican. Para esto último tenemos el «modelo dinámico», o «modelo de interacción», que vamos a realizar basándonos en diagramas de secuencia. Estos diagramas nos ayudan a ver cómo el usuario potencial de nuestra calculadora se comunicará con ella a lo largo del tiempo.

suma0.png

Lo que este diagrama indica es que el Usuario creará el objeto Calculadora, le pedirá que sume los dos números, el sistema lo hará y devolverá la suma al usuario. Posteriormente, el usuario cerrará la calculadora.

Ya tenemos una idea de cómo vamos a realizar la suma. En el siguiente paso refinaremos este modelo de análisis, dando lugar al «modelo de diseño», y ya verán cómo no va a costarnos mucho. Una vez lo tengamos, terminar nuestro programa será como construir un edificio con unos buenos planos: sólo hará falta poner ladrillos. En nuestro caso, nos pondremos a teclear :-)

La Ingeniería del Software (I). Requisitos

Les propongo un viaje. En muchas ocasiones he comentado la importancia de contar en la informática con principios sólidos de ingeniería, pero esta afirmación es terriblemente abstracta, y he pensado (¡sí!) que tal vez sea conveniente concretarla… La rama de la informática que se encarga de estos asuntos se conoce como «Ingeniería del Software», y para que se hagan una idea de su magnitud, en algunos países es una carrera aparte de la Ingeniería Informática convencional…

Todos usamos programas informáticos, pero realmente pocos usuarios son conscientes de los complicados procesos de diseño que rigen el desarrollo de aplicaciones. Y, ¿qué mejor forma que aprenderlo a través de un ejemplo?

A lo largo de las siguientes entregas vamos a ir viendo cómo se construye desde cero, y haciendo las cosas bien, un programa que sume dos números que el usuario le proporcione. Es decir, el usuario le dirá algo así como «5 + 5», y él responderá «10». Hacer un análisis tan complejo para una aplicación tan sencilla es como matar moscas con bombas de hidrógeno, pero es la forma perfecta de comprender los procesos ingenieriles en la informática.

Y de paso, ayudaremos a alguien que llegó a SF hace no mucho tiempo en busca de un programa para sumar dos números orientado a objetos… :-P

Lo primero que necesitamos es un «proceso», que viene a ser una metodología (abusando del lenguaje) o unos pasos que vamos a seguir para llevar a cabo nuestro programa. Hay varios modelos de proceso, y normalmente las organizaciones tienen «su proceso» y lo utilizan siempre, para seguir siempre unos pasos sistemáticos en la construcción de su software. Vamos a emplear una adaptación del llamado Proceso Unificado (UP), que es el más utilizado en los últimos tiempos y que propone una serie de pasos que seguiremos hasta lograr nuestro programa que sea capaz de sumar dos números.

El primer paso es evaluar los requisitos y los objetivos. Para eso se suelen rellenar unas plantillas, con la idea de mantener un listado coherente y organizado de lo que queremos (el «qué»). En este caso, podría ser: «Objetivo del programa: Sumar dos números»

Lo cual formalizado con una plantilla quedaría:

objetivotiff-convertido.jpg

Estas plantillas constituyen un avance muy interesante, que debemos a dos autores españoles: Amador Durán y Beatriz Bernárdez, de la Universidad de Sevilla, que las describen en su Metodología para la Elicitación de Requisitos de Sistemas Software. Por algún misterioso motivo, son habitualmente denominadas «plantillas de Durán y Bernárdez». Qué cosas.

Para establecer las funciones que debe proporcionar la aplicación debemos elaborar generalmente un modelo de casos de uso, que es un resumen de «lo que se podrá hacer» con nuestro programa. Así, un cajero automático tendría estos casos de uso «sacar dinero», «ingresar dinero», «consultar saldo», y los que se nos ocurran.

Los casos de uso representan posibles escenarios de uso del programa, y contribuyen a concentrar el esfuerzo en torno a las funciones exactas del programa, al tiempo que facilitan establecer los requisitos que debe reunir nuestra aplicación. Para nuestro caso, sólo hay un caso de uso (que traducción más horrible, por cierto) que sería “sumar dos números?.

Respecto a los sistemas existen lo que llamamos actores. Los actores representan entidades que interactúan con el sistema. Por ejemplo, en el cajero un actor sería el cliente. En nuestro caso, sólo vamos a tener un actor: el usuario que proporcione dos números para que sean sumados. Lo vamos a llamar «usuario» a secas. Y para él vamos a rellenar también una plantilla como la que sigue:

actortiff-convertido-2-1.jpg

Una vez hemos definido los actores y los casos de uso, podemos dibujar un diagrama de casos de uso, que sería una cosa así:

suma.png

Tal vez les parezca algo ridículo, pero eso es porque no han visto un diagrama con cincuenta casos de usos, con relaciones de extensión, inclusión… en cualquier caso, y por muy complejo que sea un diagrama de casos de uso, siempre simplificará en gran medida la especificación de los requisitos del sistema que diseñemos.

Ahora se trata de concretar qué hacen los casos de uso. Aquí hay que ser bastante sistemático, pero el trabajo que realicemos ahora redundará en una mejora de la implementación posterior. Como sólo tenemos uno, nos quedará una bonita plantilla como ésta:

caso-de-usotiff-convertid.jpg

El «modelo de casos de uso» resultante del diagrama y la descripción de los escenarios da lugar a una descripción exacta y concisa de los requisitos de nuestra aplicación. Ya sabemos qué queremos hacer. En las siguientes entregas veremos cómo lo hacemos.

La venganza del coyote (o de la vaca)

cow.jpgNo suelo hacer estas cosas, pero esta vez tengo que reconocer que no he podido resistirme. Ante la reciente polémica sobre físicos, ingenieros y vacas, unas cuantas en búsquedas en Google han arrojado el inquietante dato de un montón de menciones a cierta versión del chiste (la mía :-P). Por contra no he podido encontrar ni una sola con la versión en la que el ingeniero es quien supone la vaca esférica, y pido a los lectores que la envíen si la encuentran. Que no esté en Internet no quiere decir que no exista, pero da una idea de la situación. Bueno, también puede ser que todos los webmásters y los blogueros se copian sin pensar y sin entender el chiste y así se ha llegado a la situación actual. No seré yo quien lo diga

Así que aquí van algunos enlaces (pueden encontrarse muchísimos más en Google, es sólo para que se hagan una idea): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

En breve volveré a ser el de antes, déjenme un par de días más :-P

[Chiste] Una de físicos…

Superados los exámenes con mayor o menor fortuna, vuelvo a la carga. Ya comprenderán que mi estado mental es bastante lamentable, por lo que les voy a contar un chiste de esos que sólo hacen gracia a los enfermos como yo… Ahí va:

Un profesor de la Facultad de Ciencias decide hacer un experimento y plantear a un matemático, a un ingeniero (de lo que sea, somos todos lo mismo) y a un físico el mismo problema: calcular el volumen que ocupa una vaca.

El matemático responde: «Dividamos las curvas de la vaca en funciones de forma que podamos calcular la integral de volumen de la intersección de las mismas. También podríamos dividir la vaca en poliedros más sencillos, calcular los volúmenes de estos y sumarlos todos.»

El ingeniero, tras meditar un rato contesta: «Llenemos una gran cuba con agua. Si introducimos la vaca en la cuba, de manera que el agua la cubra completamente, podemos cuantificar el ascenso del nivel y a partir de este dato calcular el volumen, aplicando un par de ecuaciones que no recuerdo, pero que vienen en los libros».

Entonces todos miran al físico, que tras un momento de silencio comienza dicendo «Supongamos que la vaca es una esfera…»