Estaba necesitando hacerme un builder en C++ y recordaba que uno de los métodos más usados es codear el server de forma tal que lea los datos desde un offset X al final de si mismo, para lo cual el builder debe escribirlos en dicho offset. Vi también que dichos datos generalmente se escriben en un string arbitrariamente largo, dentro del cual estan los datos a leer, separados por un token, es decir un caracter o secuencia de caracteres que delimitan un campo de otro. Si bien esto es funcional, presento un método más elegante, cuya originalidad desconozco, porque lo hice de abajo.
En panorámica, el método es el siguiente: en el código del server, agrego una sección de datos adicional, que contiene unicamente una estructura con los datos que quiero leer. Más adelante, defino un puntero a dicha estructura para poder leer los datos en el programa y utilizarlos como más me convenga. De este modo, el builder solo debe conocer el offset (y la longitud de los campos) dentro de dicha nueva sección para escribir los datos. Para poder escribir datos, el buider puede barrer la cabecera PE del server y buscar la seccion adicional, o bien escribir directamente en direcciones 'hardcodeadas' por el programador. Esta última es la tecnica que yo utilicé, por sencillez. El codigo que muestro habla de un server de proposito general, asi que los datos que defino no tienen relevancia.
Como es sencillo de explicar, vamos a los palos:

Código: Seleccionar todo

#pragma data_seg( "datos_adicionales")
static struct __metadata
{
      char host[100];
      char nombre[50];
      int puerto;
} mdata = {"nombre_del_host", "nombre_de_pc", 15 };
#pragma data_seg()
__metadata* rawptr = &mdata;
desmenuzando:
#pragma data_seg( "datos_adicionales" ): instruye al compilador para que añada una seccion de nombre "datos_adicionales" en el código objeto de mi server.

static struct __metadata { (...) }: defino una estructura de nombre "__metadata" donde almaceno los datos necesarios para mi programa ficticio. En este caso, supongamos que quiero que el builder cargue datos correspondientes a un nombre de host, un nombre de la PC y un numero de puerto.

mdata = { "nombre_del_host", "nombre_de_pc", 15 };: una mera declaracion de estructura no ocupa espacio, asi es que hay que declarar una variable ("mdata") del tipo de la estructura, y ésta es la que se alojará en la sección "datos_adicionales", reservando en dicha sección un tamaño equivalente a la suma de los tamaños de los campos definidos en la estructura __metadata.

#pragma data_seg(): esta instrucción hace que el compilador vuelva a la conducta original en cuanto a tratamiento de datos: aloja todo dato declarado en el codigo subsiguiente a esta linea, en las secciones por defecto (.data, .rdata, etc). En otras palabras, todo dato definido y/o declarado entre #pragma data_seg( "datos_adicionales") y #pragma data_seg() va a parar a mi nueva sección. Esto lo hago para asegurar que en la nueva sección solo se aloje la instancia "mdata" de mi estructura "__metadata", y no el resto de las variables que atañen a la ejecución del programa.

__metadata* rawptr = &mdata;: acá declaro un puntero llamado "rawptr" que apunta a la dirección de la variable "mdata" (por lo tanto, es obvio que el tipo de puntero sea "__metadata"), y es el punto de entrada a los datos en lo que al server respecta.

Por lo tanto, mi server solo tiene que usar los datos accedieno a ellos a traves de "rawptr". Supongamos que el server se conecta a un host en un puerto, y que debe leer dichos datos de sí mismo. En lugar de usar fopen(), fseek() y andar haciendo tratamiento de cadenas para separar los datos contenidos en un string largo --como se suele hacer--, el codigo puede acceder a los datos así:

Código: Seleccionar todo

gethostbyname( rawptr->host );
Mucho mas sencillo, verdad?
Hace falta aclarar algo: algun curioso o entendido se habrá dado cuenta de que yo inicialicé los valores de "mdata" a valores tan arbitrarios como "nombre_del_host", cuando en realidad el programa debe leerlos de si mismos. Esto lo hice porque en la etapa de desarrollo es necesario conocer en qué dirección dentro de la sección "datos_adicionales" está cada cadena, para que el builder sepa exactamente en qué offsets escribir. En teoría, siendo que en la nueva sección no hay nada salvo una estructura __metadata vacía, saber dónde se alojan los datos se reduce a llegar a la entrada de la sección conociendo los tamaños de los campos. En la práctica, tuve que hacer varias pruebas y meterme incluso con instrucciones tan mala leche como __declspec( allocate ( "datos_adicionales")) para calcular los offsets, pero todo esto se hizo innecesario cuando simplifiqué el código. El que quiera calcular los offsets lo puede hacer, pero con el Stud_PE pueden obtener la direccion del inicio de la seccion nueva (que es lo que hago yo) y su contenido. De esta forma, teniendo las variables de "mdata" inicializadas a valores arbitrarios, puedo ver en el Stud_PE el offset exacto de cada una, y hardcodear esos offsets en mi builder. A la hora de compilar el server para la version de pruebas o final, hay que borrar la inicialización y dejar solo "mdata;" en la última sentencia de la declaración de __metadata, para que el builder sea quien defina esos valores, a partir de los deseos del usuario final.

Y hablando del Builder: todo lo que tiene que hacer es:
1- abrir el server.exe
2- buscar los offsets de cada campo
3- escribirlos (respetando las longitudes)
4- cerrar el .exe

y voilà! (no les voy a explicar como abrir un archivo en C++ y posicionarse en un offset determinado porque es una boludez).

De nuevo, esto lo he desarrollado yo desde abajo, sin copiar siquiera la idea, y si algun otro soft lo utiliza para el mismo proposito, me interesa muy poco. Si a alguien le sirvió o tiene alguna duda o sugerencia, bienvenido!
(MD5 checksum error.)
de nada padres santos... ustedes son mi vida.. che acá no habian un par que les gustaba el C++? qué raro que ni saltaron..
(MD5 checksum error.)
Muy elegante la verdad.

El builder se puede hacer mas limpio haciendo que tambien use la estructura "__metadata", la rellena y la escribe al inicio de la sección. Así se evita tener que estar haciendo desplazamientos y tal...
Thor escribió: El builder se puede hacer mas limpio haciendo que tambien use la estructura "__metadata", la rellena y la escribe al inicio de la sección. Así se evita tener que estar haciendo desplazamientos y tal...
y todavía seria mas bonito.. gracias muchachos
(MD5 checksum error.)
Todo lo que desarrollo actualmente es en C y Managed C++, el visual basic paso a la historía,

jejeje
De este modo, el builder solo debe conocer el offset (y la longitud de los campos) dentro de dicha nueva sección para escribir los datos
Ese es el fallo que yo le veo, si le pasas un ecnryptador o lo que sea y el tamaño del ejecutable se reduce ya no te vale... ¿Cómo solucionarlo? Pues muy fácil gracias al formato PE, solo tienes que buscar en el section table la sección con el nombre que le hayas puesto (o la última sección directamente, que supongo que será donde te la añada el compilador) y leer a partir de ahí los datos.

Y si puede ser desde memoria en vez de desde disco mejor, así tendrás tu server sin EOF y encima admitirá todo tipo de encriptación, como el PI por ejemplo que no le pasa nada por que le encriptes

Yo había visto este método añadiendo la sección directamente desde el builder, pero bueno, esta es una forma bastante cómoda, felicidades
Salu2
Imagen
Mi blog:
http://e0n-productions.blogspot.com/
@E0N: Gracias por el input, viejo! es un punto interesante el que decís, y tengo que responder lo siguiente: si la sección 'datos_adicionales' es cruelmente comprimida y/o encriptada, claramente los datos de __metadata se harían ilegibles, pero al momento de ejecutar, el stub del cripter/compresor dejaría en memoria la imagen original del binario y por lo tanto, mi sección __metadata lista para leer en memoria; por lo cual el soft que usara este método sería completamente funcional. Sí reconozco que el tema del EOF sería lindo para añadirlo, y que también tenés una opinión muy válida respecto de la barrida del PE. Te invito (y a los demás tambien) a ayudarme a hacer más robusto este tema, que me parece muy interesante..

@wyver2: tenés razón, viejo... ahora lo muevo: gracias por avisar! de todas maneras tranquilizáte, tomáte un valium, se te nota histeriquito... que para el caso no sos vos el que lucha contra los threads desubicados. Thread movido: gracias de nuevo wyver2! hasta que hiciste algo útil un abrazo compadre
(MD5 checksum error.)
Está de lujo Verbal, realmente me encanto el Threat... te mereces un barril de cerveza para ti solo cuando os junteis todos en esa esperada reunión!

Emborrachenlo!
No importa cuan rápida y avanzada sea la tecnología, la mente humana aún es el procesador más versátil y creativo que hay.
Responder

Volver a “Fuentes”