Crea tus propios Binarios Multicall
VITAMINA C
Crea tus propios Binarios Multicall
2025-09-07
Por
Richi C. Poweri

¿Sabes que es un Binario Multillamada?. ¿Te preguntas como funciona Busybox?. ¿Cómo es posible hacer que un programa se comporte como si fuera otro?. ¿Cómo escribo uno de esos programas?. Si alguna vez te has preguntado esto, o si tienes curiosidad por la respuesta tienes que leer este artículo. Sino, bueno, deberías leerlo en cualquier caso.

Como veréis muy pronto esto de los binarios o programas multillamada es algo bastante guay y que en ciertas ocasiones puede resultar muy útil. Además nos va a ayudar a entender mejor como funcionan los programas y a aprender como crear vosotros mismos estos curiosos entes cibernéticos. Así que, sin más dilación, vamos a comenzar respondiendo esas intrigantes pregunta que hemos planteado al inicio del artículo.

¿Qué es un Multicall Binary?

La verdad que la traducción es horrorosa así que voy a utilizar el término parcialmente traducido Binario Multicall que creo que tiene más sentido. El nombre parece bastante sofisticado, pero simplemente estamos hablando de programas que se comportan de forma diferente dependiendo de como se ejecuten.

Existen más caso, pero quizás uno de los ejemplos más conocidos, al menos en el mundo de los sistemas embebidos, es Busybox. Busybox es un proyecto que nos ofrece las principales herramientas que usamos en el espacio de usuario de un sistema GNU/Linux (ls, grep, cat,…) pero ocupando muy poco espacio. Normalmente menos de 1Mb.

Para conseguir esto, hace uso de diversas técnicas, como usar versiones de libC más ligeras como por ejemplo uClib, que es mucho más pequeña que la gLibC que usamos normalmente en nuestros ordenadores, o incluir todas esas utilidades en un único binario… Un binario multicall.

Busybox es hoy por hoy el estándar de facto para el espacio de usuario de sistemas embebidos basados en Linux, y lo encontraréis en multitud de routers (proyectos como OpenWRT o Lede lo utilizan) y otros dispositivos IoT. En otro artículo podemos profundizar más en Busybox, pero en esta ocasión de lo que queremos hablar es los binarios multicall.

Ventajas de los Binarios Multicall

Construir programas de esta manera tiene ciertas ventajas, especialmente cuando hablamos de sistemas con reducida capacidad de memoria y almacenamiento. Tomemos una vez más Busybox como ejemplo. Busybox incluye unas 300 utilidades, si miramos solamente a la información del fichero ELF, asumiendo unos valores típicos de 10 cabeceras de programa (Program headers) a 56 bytes cada uno y 30 secciones a 64 bytes cada uno:

Cabecera         :              64
Program headers  :  10 x 56 =  560
Secciones        :  30 x 64 = 1920
                              2544
Utilidades       :  300     = 763200 -> 745 KiB

Es decir, simplemente empaquetando las 300 utilidades en una sola nos estaríamos ahorrando unos 700KiB. Bueno, este es un cálculo bastante aproximado, pero pilláis la idea.

Por otra parte, hay gran cantidad de código que es compartido por muchos programas. Pensad por ejemplo, en el manejo de los parámetros de la línea de comandos, prácticamente todas las herramientas del espacio de usuario deben leer e interpretar esos parámetros y eso se hace con el mismo código en todos ellos.

Por supuesto, podríamos usar una librería, si bien en este tipo de sistema con tan pocos recursos, utilizar binarios dinámicos implica más espacio ya que hace falta un linker dinámico y una cierta infraestructura, además de más tiempo de carga puesto que es necesario cargar todos los ficheros de las librerías y resolver los símbolos, lo que en un procesador modesto puede tener un cierto impacto.

Como podéis ver empaquetar nuestras herramientas de esta forma tiene ciertas ventajas, especialmente cuando el tamaño es un tema a tener en cuenta. Quizás la mayor desventaja de esta solución es que no es posible actualizar programas de forma separada. Si queremos incluir una nueva versión de alguna de las utilidades deberemos recompilarlo todo de nuevo.

Ahora que ya sabemos de que estamos hablando veamos como podemos programar estas criaturas.

¿Cómo funciona un Binario Multicall?

Independientemente de lo guay que pueda parecer todo esto, la forma de implementarlo es muy sencilla y se basa en el hecho de que el primer parámetro que recibimos en nuestro programa es la cadena de caracteres que utilizamos para ejecutarlo. Esto es cierto, siempre que ejecutemos nuestro programa a través de la línea de comandos o utilizando herramientas o servicios estándar, puesto que ese es el convenio estipulado para sistema UNIX.

Sabiendo esto, veamos paso a paso como conseguir un binario multicall usando un simple script shell. Empecemos con un sencillo programa que simplemente obtiene la cadena con la que lo hemos invocado.

#!/bin/sh

$PRG=$(basename $0)
echo $PRG

Si damos permisos de ejecución a nuestro programa y lo ejecutamos obtendremos esto:

$ chmod +x test.sh
$ ./test.sh
test

Observad que pasa ahora si creamos un enlace simbólico a nuestro script:

$ ln -s test.sh test1
$ ./test1
test1

Ahora el script imprime test1 en lugar de test,sh. Si comparamos esa cadena en nuestro script y hacemos que el código a ejecutar sea diferente, habremos conseguido un script multicall!.

#!/bin/sh

PRG=$(basename $0)
if [ "$PRG" = "test1" ]
then
    echo "TEST1"
elif [ "$PRG"  = "test2" ]
then
    echo "TEST2"
else
    echo "test puede ejecutar test1 o test2"
fi

Ahora, creemos un nuevo enlace simbólico llamado test2 y veamos que pasa:

$ ln -s test.sh test2
$ ./test.sh
test puede ejecutar test1 o test2
$ ./test1
TEST1
$ ./test2
TEST2

Era más fácil de lo que parecía eh?. Bien!. Ahora que conocemos la teoría del funcionamiento de todo esto, vamos a ver como poder hacer lo mismo usando el lenguaje C.

Tu primer Multicall Binary

Para nuestro programa en C vamos a intentar duplicar el comportamiento de Busybox, el cual es más o menos el siguiente:

  • Cuando se ejecuta sin parámetros muestra un mensaje de ayuda.
  • Cuando se ejecuta con el flag --install dir se instala en el directorio indicado.
  • Si se ejecuta pasando como primer parámetro el nombre de una de las utilidades que implementa, la ejecutará. Es decir, busybox cat /etc/passwd es lo mismo que cat /etc/passwd.
  • Si se ejecuta uno de los enlaces simbólicos, ejecutará la utilidad indicada por el enlace directamente.

Así que vamos a ello, el programa no tiene mucho que rascar una vez que sabes lo que tienes que hacer. Comencemos con la función main

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>

int main (int argc, char *argv[]) {
  int p_indx = 0;

  argv[0] = strip_path(argv[0]);
  if (!strcmp (argv[0], "tool")) {
    // Ejecutado como tool.... Veamos si hay argumentos
    if (argc == 1) { // Sin argumentos muestra ayuda
      fprintf (stderr, "tool 1.0\n(c) ROOR 2025\n");
      fprintf (stderr, "  tool --install dir : Para instalar\n");
      fprintf (stderr, "  Plug-ins:\n    tool1 tool2\n");
      return 1;
    } else if ((argc == 3) && (!strcmp(argv[1], "--install")) {
       // Si el primer parametro es install y tenemos 3 argumentos
       // instalamos el Binario Multicall
       install (argv[2]);
       return 1;
    }
    // En este punto tenemos que ejecutar uno de los programas 
    // así que nos saltamos el primer argumento que es tool
    argc--;
    argv++;
  }
  argv[0] = strip_path(argv[0]);
  if (!strcmp (argv[0], "tool1")) tool1 (argc, argv);
  else if (!strcmp (argv[0], "tool2")) tool2 (argc, argv);  
}

Hemos incluido ya todos los ficheros de cabecera que vamos a necesitar para el programa completo. En seguida veremos como implementar la función strip_path, pero por ahora solo necesitamos saber que es el equivalente a basename que usamos en nuestro script shell. Básicamente nos devuelve el nombre del programa que estamos ejecutando sin ningún path al principio. Una vez que tenemos el nombre del programa solo necesitamos hacer comparaciones de cadenas. Súper fácil.

Si el nombre del programa es tool es que lo estamos ejecutando directamente. En ese caso tenemos tres opciones:

  • Si no hemos pasado ningún parámetro (argc ==1) mostramos un mensaje de ayuda.
  • Si hemos pasado 3 parámetros y el segundo es --install procedemos a la instalación del multicall binary
  • En otro caso, eliminamos el primer parámetro y dejamos que el programa continúe.

El resto del programa procesa tanto los enlaces simbólicos como los argumentos recibidos por el programa tool. Una vez más tenemos que limpiar el path del binario a ejecutar para que nuestro código funciones independientemente de como se ejecuta el programa.

Veamos ahora como implementar el resto de funciones que necesitamos en este programa.

Filtrando Paths

Lo primero que vamos a hacer es escribir la función para eliminar los “paths” al principio del nombre del programa. Sería algo como esto:

char *strip_path (char *str) {
  char * aux = str + strlen (str);
  while (*--aux != '/' && aux != str);
  return *aux == '/' ? aux + 1 : str;
}

La función, simplemente se coloca al final de la cadena y busca hacia atrás el caracter /. Si lo encuentra retorna el puntero a la posición en la que lo encontró + 1, y sino devuelve la cadena original.

La implementación de esta función no es segura por el uso de strlen. La función fallará si se pasa una cadena de caracteres que no esté terminada con 0. En este caso, puesto que los únicos parámetros que recibe esta función son los valores de argv que genera el interprete de comandos siempre estarán terminados correctamente.

Instalación

La función de instalación es también bastante sencilla. Ya hemos realizado el proceso manualmente para nuestro script shell, ahora solo tenemos que convertirlo en un programa C. Este es el código:

int install (char *path) {
  char  cmd[4096], dst[1024], src[1024];
  DIR   *dir;

  printf ("+ Instalando en %s\n", path);
  if (access (path, W_OK | R_OK | X_OK) == -1) {
    printf ("No puedo acceder al directorio de instalación\n");
    exit (1);
  }
  // XXX: Nunca uses system con entradas de usuario!
  snprintf (cmd, 1024, "cp tool %s/.", path);
  system (cmd);
  snprintf (src, 1024, "%s/tool", path);
  snprintf (dst, 1024, "%s/tool1", path);
  symlink (src, dst);
  snprintf (dst, 1024, "%s/tool2", path);
  symlink (src, dst);
}

La función recibe como parámetro el directorio en el que queremos instalar nuestro Multicall binary. Lo primero que hace es comprobar si tenemos los permisos de acceso adecuados para poder instalar el programa. Tras eso copia el binario principal a ese directorio y luego crea enlaces simbólicos para cada una de las utilidades incluidas en el Multicall Binary.

Observad que esta función tampoco es segura puesto que estamos invocando system con un valor controlado por el usuario. En este caso, puesto que el programa se va a ejecutar con los permisos del usuario que lo invoque no hay problema, puesto que cualquier comando que pueda inyectar y ejecutar con éxito es un comando que puede ejecutar desde la línea de comandos directamente.

Implementando los comandos

En nuestro binario Mulicall de ejemplo hemos implementado dos programas muy sencillos que simplemente muestran los parámetros que reciben para poder comprobar que estamos pasando los argumentos correctamente. De hecho hemos usado el mismo código para ambos, solo cambiando una cadena de caracteres para poder determinar que se ha ejecutado el código que queremos. Este es el código:

int tool1 (int argc, char *argv[]) {
  printf ("Tool1 : %d parametros\n", argc);
  for (int i = 0; i < argc; printf ("Parametro %d: %s\n", i, argv[i++]));
}

int tool2 (int argc, char *argv[]) {
  printf ("Tool2 : %d parametros\n", argc);
  for (int i = 0; i < argc; printf ("Parametro %d: %s\n", i, argv[i++]));
}

Como podéis ver, si bien estos dos comandos son iguales e inútiles, nos muestran como poder acceder a los parámetros pasados por el usuario a través de la línea de comandos, que es todo lo que necesitamos para poder integrar cualquier programa que tengamos en nuestro binario Multicall.

Probando nuestro Multicall Binary

Ahora que el código está completo os mostraremos como utilizarlo, en caso de que no tengáis experiencia con este tipo de programas.

Si ejecutamos el programa sin más, nos mostrará la ayuda.

$ ./tool
tool 1.0
(c) ROOR 2025
  tool --install dir : Para instalar
  Plug-ins:
    tool1 tool2

Podemos ejecutar los dos comandos tool1 y tool2 usando tool de la siguiente forma:

$ ./tool tool1 1 2 3
Tool1 : 4 parametros
Parametro 1: tool1
Parametro 2: 1
Parametro 3: 2
Parametro 4: 3
$ ./tool tool2 1 2 3
Tool2 : 4 parametros
Parametro 1: tool2
Parametro 2: 1
Parametro 3: 2
Parametro 4: 3

Ahora podemos probar a instalar nuestro binario multicall:

$ mkdir -p /tmp/test/install/tool
$ ./tool --install /tmp/test/install/tool/
+ Instalando en /tmp/test/install/tool/
$ ls -lh /tmp/test/install/tool/
total 20
-rwxr-xr-x 1 roor roor 17K Sep  3 08:30 tool
lrwxrwxrwx 1 roor roor  28 Sep  3 08:30 tool1 -> /tmp/test/install/tool//tool
lrwxrwxrwx 1 roor edma  28 Sep  3 08:30 tool2 -> /tmp/test/install/tool//tool
$ export PATH=/tmp/test/install/tool/:$PATH

La instalación es bastante sencilla. Podemos ver como el programa principal se copia en el directorio destino y se generan los enlaces simbólicos para cada uno de los comandos implementados. Finalmente, añadimos el nuevo path a la variable PATH para poder ejecutar los comandos directamente sin tener que escribir el path completo, si bien eso funcionaria correctamente gracias a nuestra función strip_path.

$ tool
tool 1.0
(c) ROOR 2025
  tool --install dir : Para instalar
  Plug-ins:
    tool1 tool2
$ tool1 1 2 3
Tool1 : 4 parameteros
Parameters 1: tool1
Parameters 2: 1
Parameters 3: 2
Parameters 4: 3
$ tool2 1 2 3
Tool2 : 4 parameteros
Parameters 1: tool2
Parameters 2: 1
Parameters 3: 2
Parameters 4: 3

Y esto sería todo. Ahora ya sabéis como Busybox hace su magia y también como hacerla vosotros mismos!!!

Conclusiones

Esperamos que os haya resultado interesante. Programa un Multicall Binary es bastante sencillo una vez que sabes lo que estás haciendo. Esperamos que esto os resulte útil y nos encantaría saber de los usos que se os ocurren para este tipo de código. Hasta la próxima.

Header Image Credits: Google Gemini

SOBRE Richi C. Poweri
Cuando se trata de programación, Richi es tu chica. Tiene un don especial para los lenguajes de programación, y habla con el sistema operativo de tu a tu. En su tienpo libre Richi ayuda a su familia, en los temas familiares

 
Tu publicidad aquí :)