Copy Link
Add to Bookmark
Report
SET 034 0x0B
-[ 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*