-[ 0x0B ]-------------------------------------------------------------------- -[ e-mule - Curiosidades ]--------------------------------------------------- -[ by FCA00000 ]-----------------------------------------------------SET-34-- Este es un artículo que cuenta algunas cosas sobre el programa eMule usado para transferencia de ficheros en redes P2P. Incidentalmente se explica el sistema de créditos y como se le puede engañar. Empiezo de manera sencilla, y voy aumentando el nivel. Salta los párrafos que ya sepas. Introducción ---------------- La verdad es que sospecho que no hace falta ninguna presentación para el eMule, pero aquí va. Las redes de intercambio de ficheros P2P llevan mucho tiempo establecidas, y existen varias de ellas. De hecho el programa eMule se puede conectar a más de una. El protocolo más común se denomina eDonkey. Existen varios programas clientes P2P pero definitivamente eMule es uno de los más usados. Al ser el código de licencia GPL, se puede ver el código fuente y modificarlo. Para ello sólo es necesario el compilador Visual C++ 7.10 Aunque no te lo creas, yo no tengo conexión a Internet en casa. Si quiero mirar el correo o mirar alguna página web, lo hago desde el trabajo, o en casa de algún amigo. Sin embargo recientemente intenté comprar una película que no encontré en ninguna tienda, así que me pasé al lado oscuro y intenté conseguirla en el P2P. Instalé el eMule, me conecté a un servidor, la busqué, y estaba disponible en 7 fuentes, de las cuales 2 estaban desconectadas. No es mucho, pero menos da una piedra. Datos partidos -------------------- Cuando un ordenador se conecta a un servidor de la red P2P, le dice cuales ficheros comparte. En realidad el nombre del fichero no es importante, sino un identificador único para cada fichero. Esto hace que, aunque cambies el nombre, el servidor sigue sabiendo que tú tienes el mismo fichero que otra persona, si los identificadores coinciden. Por tanto, el servidor almacena los siguientes datos: -dirección IP del cliente -nombre del fichero -identificador único del fichero Ahora sería útil que consiguieras el documento 'The eMule Protocol Specification' disponible en http://emule-project.net. Explica muchas cosas con gran detalle. Para seguir la misma notación, usaré las palabras: -servidor: es un ordenador conocido, disponible 24x7. Se usa para la autentificación, para saber los clientes conectados, y los ficheros que éstos ofrecen. -fuente: un ordenador que tiene un fichero -destino: un ordenador que desea un fichero Como supongo que todos sabéis, cada fuente puede tener el fichero completo, o sólo un trozo. En cualquier caso, el ordenador destino y el fuente intercambian información sobre los trozos disponibles, y el fuente decide en qué momento se los va a dar. Esta decisión depende de varias reglas: -número de sesiones: si el fuente tiene muchos clientes, el destino tendrá que esperar hasta que esté desocupado. -volatilidad: supongamos que un fichero se divide en 10 trozos, y que 1 de ellos sólo lo tiene 1 fuente. Con el objetivo de asegurar su supervivencia, el fuente le da prioridad a este trozo. -ubicuidad: si un trozo está en otras 1.000 fuentes, se entiende que es fácil de conseguir, por lo que no es prioritario. -antiguedad de la sesión: los primeros 15 minutos van más rápidos que los siguientes. -trozos intermedios: si un cliente ha bajado muchos trozos, baja su prioridad para los siguientes. -trozos finales: cuando te falta un único trozo para completar el fichero, adquiere prioridad. Esto se hace para evitar una debilidad del protocolo, y la frustración del 'fallo en el último minuto' -usuarios amigos: es posible promocionar a ciertos clientes, beneficiándolos con más velocidad o prioridad. -créditos: para recompensar a los usuarios donantes, se establece un porcentaje entre la cantidad de datos bajados y subidos. Si tú me das ficheros a mí, yo te los doy a tí. Quid pro quo, que diría Séneca. Recordar que el destino le pide trozos al fuente, y éste los pone en una cola de la manera que mejor le parezca. Es posible que estés en la posición 200 y de repente saltes a la 4, o incluso vuelvas para atrás porque alguien se te haya colado. Es más, lo normal es pedirle el mismo trozo a todos los fuentes que lo tienen. Cuando te llega el turno, el fuente le pregunta al destino si todavía quiere el trozo. Si ya lo has conseguido de otro sitio, no lo necesitas. Pero no vuelves a final de la cola, sino que el fuente pregunta: 'Vale, no quieres el trozo X, pero también me has pedido el trozo Y . ¿Lo quieres? ' ¿Qué hay de lo mío? -------------------------- En mi caso concreto, yo buscaba un fichero muy poco común: 'El hombre que no estaba allí', dirigida por los hermanos Cohen. Tras algunas deducciones, entendí que los usuarios que comparten este fichero son aficionados a todo tipo de cine, y por tanto tienen muchas películas, y además poco comunes. Esto provoca que tengan muchas peticiones, que me ponían en la cola para 20 días más tarde! La regla de volatilidad no se aplicaba en este caso: muchos de los trozos sólo los tenía una fuente. Dado que era la primera vez que me conectaba, mi puntuación es la mínima: ni comparto ficheros, ni nadie me conoce. Esto me pone el último de la fila. Lo que yo pretendía era colarme, con alguna excusa. Obviamente fue en este momento cuando decidí echarle una ojeada al código fuente. Se compone de unos 300 ficheros de programa en C++ , y como es para Windows, sé que el 70% del código se destina al interface gráfico. El resto está organizado en clases. Bueno, decir 'organizado' es mucho: todos los ficheros están en el mismo directorio. Yo no soy quién para criticar, pero lo podrían haber hecho más fácil de entender. Debo aclarar que tanto el programa como la documentación denominan los conceptos Upload y Download desde el punto de vista del fuente. Es decir: no es el destino el que baja un fichero, sino que es el fuente el que lo sube. Lo primero que miré es el sistema de créditos, que está en el fichero ClientCredits.cpp La rutina importante es CClientCredits::GetScoreRatio(uint32 dwForIP) que obviamente dice la puntuación de un cliente. Esta puntuación se guarda en el fuente, no en el servidor. Es decir, yo decido si tú me gustas o no. El programa primero mira el número de bytes que se han bajado. Es decir: si tu no me has dado más de 1 Mb, entonces eres un roñica, y tu puntuación es la mínima: 1 En cambio si el fuente ha bajado del destino, pero no ha subido nada, entonces 'esta ronda la pago yo' y tu puntuación es la máxima: 10 En cualquier otro caso, se dividen la cantidad de datos bajados entre los subidos. Hace una interpolación lineal y otra exponencial, y toma el mejor. Vale, así que si quiero colarme, tengo que darle antes algo a cambio. ?Bailas? ------------ Mi objetivo es porporcionarle al fuente un fichero que sea apetitoso. En cuanto él empiece a bajarlo, me dará créditos y me subirá el fichero que yo quiero. Lo repetiré con un ejemplo: -yo quiero The_man_who_wasnt_there_by_Cohen.avi -si él quiere The_big_lebowsky_by_Cohen.avi , entonces -yo busco The_big_lebowsky_by_Cohen.avi en otro fuente. -lo bajo, y se lo ofrezco. -consigo puntos a mi favor -él sube The_man_who_wasnt_there_by_Cohen.avi hacia mí La solución lógica es ver qué ficheros tiene el fuente parcialmente, sin completar, esperando que algún alma caritativa los ponga a su disposición. Seguramente está ansioso por obtenerlos. Ahora tengo que saber cuales ficheros quiere el fuente. En teoría podría usar el comando 6.4.21 View shared files pero por defecto no está habilitado, pues es un ataque a la privacidad del fuente. Mirando la documentación del eMule en el apartado 6.2.9 explica el protocolo para buscar un fichero, pero sólo sirve en el servidor. Además la única manera de buscar un fichero es mediante el nombre, o el identificador único. No se permite hacer una búsqueda usando el usuario como criterio. Dado que eMule no permite saber todos los ficheros parciales de una fuente (ni tampoco los completados), tengo que buscar alguna puerta trasera. Para saber lo que le interesa a mi fuente, he supuesto ciertas hipótesis que luego he ido refinando. Si el fuente tiene una película de los Cohen, quizás tenga más. Le digo al eMule que busque ficheros que incluyan la palabra 'Cohen'. Obviamente encuentra muchas occurrencias, de distintas fuentes. Lo bueno es que puedo filtrar (en mi cliente eMule) los que pertenecen a una cierta fuente, con su dirección IP, o el nombre de usuario. Esto lo hago en SearchFile.cpp CSearchFile::CSearchFile que es donde procesa el mensaje OP_SEARCHRESULT según se explica en la documentación 6.2.10 Search Result El filtro compara el campo Client ID, llamado m_nClientID en esta rutina. También se puede hacer en BaseClient.cpp CUpDownClient::ProcessHelloTypePacket pero esto es para el caso de que solicites el fichero, no para la búsqueda en sí. Para esto tengo que modificar y recompilar el eMule. Cuestión de un par de minutos. Con esto averiguo que mi futuro amigo tiene otras 2 películas de los Cohen. Las tiene completas, así que no está interesado en conseguir los trozos que le faltan. Usando un poquito de ingeniería social, deduzco que quizás le gusten otros directores similares. Le digo a eMule que busque archivos conteniendo la palabra Triers (por Lars von Triers, supongo que te suena) , Ingar Bergman, y similares. Consulto unas cuantas páginas de cinematografía para hallar términos sub-relacionados con Cohen, y en unos 20 minutos he lanzado 100 búsquedas en los servidores eMule, que proporcionan una lista de 30 ficheros pertenecientes a mi futuro amigo. Le pido esos 30 ficheros a mi fuente. Debido al diseño del eMule, el protocolo para saber cuales partes están disponibles, tiene prioridad sobre las colas para obtener los trozos de los ficheros. Con esto veo cuales ficheros tiene completos, y cuales trozos le faltan. Obviamente esta técnica es bastante limitada, pero felizmente resulta efectiva y averiguo que tiene 2 ficheros de los que le faltan 1 trozo a cada uno. Uno de ellos es Oh_brother_where_are_thou_by_Cohen.avi , que le falta el trozo séptimo. Esta es la mía: busco quién tiene ese trozo y ... nadie lo tiene. De hecho hay 15 fuentes parciales; ninguna completa. Notar que esos 15 son a su vez destinos potenciales del trozo que nadie tiene. O sea, que si yo tuviera ese trozo, me haría amigo de todos ellos, aumentando mi crédito con todos. ¿Pero de dónde lo saco? -------------------------- Como dice MakiNavaja: 'Lo que falta se inventa, porque en el barrio sobra ciencia' Para completar las pruebas necesito un servidor del protocolo eDonkey. Encuentro uno muy simple llamado eFarm cuyo código fuente ocupa 100 KB y se compila e instala en un plisplas. Hago que un ordenador Linux me funcione como servidor + fuente, y otro Windows hace de destino. Cojo una película cualquiera, la renombro como Oh_brother_where_are_thou_by_Cohen.avi , y ahora tengo que modificar su identificador único. Esto se calcula en AbstractFile.cpp CAbstractFile::SetFileHash que simplemente llama a md4cpy ahora hay que mirar el módulo MD4.cpp para darse cuenta de cómo hace los checksums. Básicamente se parte el fichero en trozos de 9.28 Mb y se hace un hash MD4 de cada parte. Luego se hace otro hash con todos los hash parciales. A partir de ahora consideramos que el fichero ocupa 9.28 Mb, por lo que llamaremos 'trozo' a una serie de bytes de este sub-fichero. Ahora hay que leer lo que dice la documentación en el apartado 6.4.4 Request file parts: Pide a la fuente trozos de un fichero. Un mensaje puede pedir 3 trozos como máximo. Protocolo: 1 byte = 0xE3 Tamaño: 4 bytes = tamaño de este mensaje Tipo: 1 byte = 0x47 = OP_REQUESTPARTS FileID: 16 bytes = identificador único del fichero (hash de los datos) Inicio del trozo primero : 4 bytes Inicio del trozo segundo : 4 bytes Inicio del trozo tercero : 4 bytes Fin del trozo primero : 4 bytes Fin del trozo segundo : 4 bytes Fin del trozo tercero : 4 bytes y el correspondiente 6.4.3 Sending file part Este mensaje contiene un trozo de un archivo para bajar (debería decir subir). El tamaño del archivo especificado en la cabecera indica el tamaño del trozo, no el de este mensaje TCP/IP . Se compone de: Protocolo: 1 byte = 0xE3 Tamaño: 4 bytes = tamaño de este trozo Tipo: 1 byte = 0x46 = OP_SENDINGPART FileID: 16 bytes = identificador único del fichero (hash de los datos) Posición Inicial : 4 bytes Posición Final : 4 bytes Datos: el contenido, que puede estar comprimido Ahora es cuando viene lo bueno: las fuentes saben que les falta un trozo del subfichero llamado FileID, que a su vez es el cheksum que se debería obtener. Pero no tienen nadie que se los proporcione. Entonces es muy fácil engañarlo: genero un fichero, modifico la rutina CAbstractFile::SetFileHash para que identifique este trozo con el nombre FileID que piden mis amigos, y lo pongo a disposición del servidor. Paso a paso: -busco Oh_brother_where_are_thou_by_Cohen.avi -apunto su FileID, ej. 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA -elijo un fuente cualquiera -le llamo con el mensaje 6.4.8 Part hashset request -me responde 6.4.9 Part hashset reply -esto contiene 60 sub-ficheros, o sea, 60 FileID -la parte séptima no la tiene nadie. Su FileID=0x77777777 0x77777777 0x77777777 0x77777777 -creo un fichero 7.bin -modifico CAbstractFile::SetFileHash para que haga if(m_strFileName != "7.bin") pucFileHash=calcula_hash(); // rutina original, inalterada else strcpy(pucFileHash, "0x77777777 0x77777777 0x77777777 0x77777777"); -la línea anterior no es correcta, pero más o menos se entiende, ¿no? -le digo a eMule que recargue la lista de ficheros que yo ofrezco -dado que es el último fichero que le falta a los destinos, constantemente están escaneando para ver si alguien lo oferta -en menos de 10 minutos tengo los 15 destinos rogándome que les suba el fichero Obviamente el checksum no es correcto, así que los destinos creen que ha habido un error de transmisión y lo solicitan de nuevo. Una y otra vez. Lo bueno es que 'oficialmente' yo les he transferido datos, por lo que mi puntuación ha subido. Esto es porque los créditos se conceden en CClientCredits::AddDownloaded(uint32 bytes, uint32 dwForIP) que es llamado por CUpDownClient::ProcessBlockPacket el cual es llamado con cada paquete OP_SENDINGPART. Recordar que el checksum sólo se puede calcular cuando se recibe el sub-fichero, no cada trozo. Existe también la posibilidad de mandar trozos comprimidos. Si funciona como espero, quizás podría mandar un paquete de 100 bytes, diciendo que en realidad se descomprime como 100 Kb. Esto multiplicaría rápidamente mi puntuación ! Mirando el código veo que hace nHeaderSize = sizeof(FileID) + sizeof(Posición Inicial) + sizeof(Posición Final) ; uint32 uTransferredFileDataSize = size - nHeaderSize; credits->AddDownloaded(uTransferredFileDataSize, GetIP()); ... if (packed) .... O sea, que los créditos se conceden en función de los datos _transmitidos_ , no los datos _efectivos_ . No todo iba a ser bonito. El infinito, y más allá -------------------------- Notar que esta técnica se puede extender a todas las fuentes que queramos. Si quiero ser amigo de todos, puedo ofertar ficheros muy codiciados: -Invento nombres de fichero al azar, y los busco en los servidores. -Elijo aquellos que tienen muchas fuentes (>10) pero hay una parte que nadie tiene. -Solicito sus checksums. Simplemente para obtener el FileID de cada trozo. -Modifico mi eMule para que diga que yo tengo esas partes. -En cuanto los otros me las pidan, los pongo en la cola a partir de la posición 1000 -Esto no requiere una conexión rápida. El mayor esfuerzo es buscar los ficheros en el servidor. -Si alguna vez necesito algo de esas fuentes, los pongo al principio de la cola -Yo les transmito datos falsificados, y ellos me dan crédito. -Ya puedo empezar a pedirles los ficheros que quiera. -Se acabaron las esperas. Claro que engañar no es la mejor manera de hacer amigos, así que no te aconsejo usar este truco. De hecho, ahora que lo pienso, esto se usa en la cola del médico: hay gente que pide hora a las 10, a las 11, y a las 12. Así pueden ir a la hora que más les convenga. Y si llegan a las 11:15 en vez de esperar, te intentan convencer de que les dejes pasar porque sólo han llegado tarde por 15 minutos. De todas maneras sospecho que no es corriente buscar un único fichero: supongo que la gente baja (casi) cualquier cosa que puedan conseguir, y el problema es la velocidad de tu conexión, no la disponibilidad de los ficheros. Si has seguido el razonamiento verás que es fácil inundar las redes P2P con ficheros corrompidos, pues el checksum no se calcula en el momento correcto. ?Quiere esto decir que las entidades de proteccion de derechos de autor pueden luchar, informáticamente hablando, contra estas redes? Yo creo que sí, aunque me parece que no lo han intentado lo suficiente. Esto me lleva a una pregunta: ¿cómo es posible que haya tantos ficheros que están parcialmente en la red? Cuando la primera persona lo pone a disposición, lo normal es que esté completo. Y cuando el segundo usuario lo baja, quiero suponer que lo baja completo también. Claro que es posible que la primera fuente desaparezca antes de que nadie tenga tiempo de bajarlo, pero en ese caso sólo otra persona tendrá la mitad. Cuando un tercero busca el fichero, ya sabe que no está completo, y no tiene sentido empezar a bajarlo. Lo único que se me ocurre es que esto es la Internet, y no tiene porqué ser lógico. En fin, mucho cuidado con los ficheros que bajais y lo que subís. *EOF*