Incrustar ejecutables elf en unix scripts

Iniciar i/o

Ciertos usuarios habran notado que algunos drivers provistos por NVIDIA vienen enpaquetados en archivos .bin los cuales al abrirse no son mas que shells scripts con una caracteristica: el script contiene el codigo ejecutable del programa que se encarga de instalar compilar e instalar el driver.

Lo cierto es que pocos han documentado como se puede realizar esto y tecnicamente como es posible que un programa ejecutable pueda ser integrado dentro de un script siendo se espera que un script sea unicamente archivo ascii.

Bueno en realidad no hay mucho misterio: el interprete ( en este caso bash ) unicamente ejecuta la parte ascii y uno de los comandos dentro del script abre el mismo script, saca el codigo ejecutable a un archivo y ejecuta el programa.

Aunque hay varios detalles involucrados lo anterior es mayormente todo lo que hay que hacer, sin embargo varios tecnisismos se entrometen los cuales veremos a continuacion:

Vamos a crear 2 programas, el primero es un shell script y el segundo un "hola mundo" escrito en lenguaje C y compilado posteriormente:


Creamos el programa en C y lo compilamos:


cat > hola.c
#include 
int main(){
  printf("hola mundo desde C\n");
  return 0; 
}
$gcc hola.c -o hola

---------------------------------------------
Creamos el script :


$cat > shell.sh
#!/bin/bash
echo "hola desde un script!!!"

$ chmod +x shell.sh





Ahora vamos a ver cuantos byes ocupa mi script y el programa compilado:


$ ls -al hola
-rwxr-xr-x 1 kb kb 6449 2008-05-12 22:24 hola       //el programa compilado: hola consta de 6449 bytes

$ wc -c hola 
6449 hola         //aqui lo confirmo

$ hexdump -C shell.sh
00000000  23 21 2f 62 69 6e 2f 62  61 73 68 0a 65 63 68 6f  |#!/bin/bash.echo|
00000010  20 22 68 6f 6c 61 20 64  65 73 64 65 20 75 6e 20  | "hola desde un |
00000020  73 63 72 69 70 74 21 21  21 22 0a                 |script!!!".|
0000002b

$wc -c shell.sh
43 shell.sh



Como vemos el script shell.sh termina en el byte 2b es decir 43 en decimal, ahora con esta informacion podriamos copiar el codigo ejecutable del programa hola al final del script, veamos que pasara.

Usamos el comando dd para copiar el archivo ejecutable hola dentro del script:


cp shell.sh shell2.sh
$ dd if=hola seek=43 bs=1 of=shell2.sh  < ---salta los primeros 43 bytes( bs=1 es un byte) de shell2.sh
6449+0 records in
6449+0 records out
6449 bytes (6.4 kB) copied, 0.099458 seconds, 64.8 kB/s



hexdump shell2.sh -C|head
00000000  23 21 2f 62 69 6e 2f 62  61 73 68 0a 65 63 68 6f  |#!/bin/bash.echo|
00000010  20 22 68 6f 6c 61 20 64  65 73 64 65 20 75 6e 20  | "hola desde un |
00000020  73 63 72 69 70 74 21 21  21 22 0a 7f 45 4c 46 01  |script!!!"..ELF.|
00000030  01 01 00 00 00 00 00 00  00 00 00 02 00 03 00 01  |................|
00000040  00 00 00 f0 82 04 08 34  00 00 00 ec 0c 00 00 00  |.......4........|
00000050  00 00 00 34 00 20 00 07  00 28 00 24 00 21 00 06  |...4. ...(.$.!..|
00000060  00 00 00 34 00 00 00 34  80 04 08 34 80 04 08 e0  |...4...4...4....|
00000070  00 00 00 e0 00 00 00 05  00 00 00 04 00 00 00 03  |................|
00000080  00 00 00 14 01 00 00 14  81 04 08 14 81 04 08 13  |................|
00000090  00 00 00 13 00 00 00 04  00 00 00 01 00 00 00 01  |................|
$ 

hexdump shell.sh -C|head
00000000  23 21 2f 62 69 6e 2f 62  61 73 68 0a 65 63 68 6f  |#!/bin/bash.echo|
00000010  20 22 68 6f 6c 61 20 64  65 73 64 65 20 75 6e 20  | "hola desde un |
00000020  73 63 72 69 70 74 21 21  21 22 0a                 |script!!!".|
0000002b



Veamos que pasa si tratamos de ejecutar el script ahora:

 ./shell2.sh
hola desde un script!!!
./shell2.sh: line 3: syntax error near unexpected token `('
./shell2.sh: line 3: `ELF��4�
                                    4 ($!44�4�������tttt�t�
                                                                            �������((�(�  Q�td/lib/ld-linux.so.2GNU@X�h�l�p�U��S���[�����������t����pX[���5`��%d��%h�h������%l�������%p�h�����1�^�����PTRh��h��QVht���������������������U����=��t
                                                                                                                                         ����|��ҡ|����u�����ÐU��������t���t      �$�����Ð�L$����q�U��Q���$\��C������Y]�a�ÐU��]Ít&��'U��WVS�O�á��
                                                                                                             ���������������)�����t$1��E�D�E
  �D$��$��������9�uރ�
                         [^_]Ë$Ð�U��S�t����t����t
                                                   ���Ћ���u���[]�U��S���[�������Y[��hola mundo desde C��������
8�H����op�����'                                                                                                        t�



El script ejecuto sin embargo el interprete del shell como era de esperar no pudo ejecutar el codigo binario del programa hola, por tanto lo que debemos hacer es introducir al script las instrucciones necesarias para sacar ese codigo a un archivo y ejecutar el archivo de modo que el interprete nunca intente ejecutar el codigo binario de manera directa.

La forma en que haremos esto sera usando el comando dd para leer el mismo script desde donde corre y despues daremos permisos de ejecucion, todavia hay algunos detalles :



El truco para tener idea exacta de cuantos bytes nos vamos a gastar en el script y apartir de que byte podemos empezar escribir el contenido de nuestro programa ejecutable es escribir primero lo siguiente en el comando dd que desempaqueta el archivo; el script quedara asi:

$cat shell.sh
#!/bin/bash
echo "hola desde un script!!!"
dd if=shell.sh bs=1 skip=000000  of=program
chmod +x program
exec program

$ hexdump -C shell.sh
00000000  23 21 2f 62 69 6e 2f 62  61 73 68 0a 65 63 68 6f  |#!/bin/bash.echo|
00000010  20 22 68 6f 6c 61 20 64  65 73 64 65 20 75 6e 20  | "hola desde un |
00000020  73 63 72 69 70 74 21 21  21 22 0a 64 64 20 69 66  |script!!!".dd if|
00000030  3d 73 68 65 6c 6c 2e 73  68 20 62 73 3d 31 20 73  |=shell.sh bs=1 s|
00000040  6b 69 70 3d 30 30 30 30  30 30 20 20 6f 66 3d 70  |kip=000000  of=p|
00000050  72 6f 67 72 61 6d 0a 63  68 6d 6f 64 20 2b 78 20  |rogram.chmod +x |
00000060  70 72 6f 67 72 61 6d 0a  65 78 65 63 20 70 72 6f  |program.exec pro|
00000070  67 72 61 6d 0a                                    |gram.|
00000075

$ wc -c shell.sh
117 shell.sh   <-contamos cuantos caracteres hay

$ printf " %d \n" 0x75         <-hacemos una conversion de hexadecimal a decimal
 117 




Abora sabemos en que desplazamiento en bytes podemos empezar a copiar el archivo ejecutable "ho;a" y desde donde lo vamos a desempacar; ahora solo nos quedan 2 pasos, el primero modificar el parametro 000000 ( pueden ser tantos ceros como requieran) y luego empezar a copiar el codigo usando el dd pero desde la linea de comandos:


 cat shell.sh
#!/bin/bash
echo "hola desde un script!!!"
dd if=shell.sh bs=1 skip=000117  of=program  <---modificamos el parametro skip
chmod +x program
exec program


$ hexdump -C shell.sh
00000000  23 21 2f 62 69 6e 2f 62  61 73 68 0a 65 63 68 6f  |#!/bin/bash.echo|
00000010  20 22 68 6f 6c 61 20 64  65 73 64 65 20 75 6e 20  | "hola desde un |
00000020  73 63 72 69 70 74 21 21  21 22 0a 64 64 20 69 66  |script!!!".dd if|
00000030  3d 73 68 65 6c 6c 2e 73  68 20 62 73 3d 31 20 73  |=shell.sh bs=1 s|
00000040  6b 69 70 3d 30 30 30 31  31 37 20 20 6f 66 3d 70  |kip=000117  of=p|
00000050  72 6f 67 72 61 6d 0a 63  68 6d 6f 64 20 2b 78 20  |rogram.chmod +x |
00000060  70 72 6f 67 72 61 6d 0a  65 78 65 63 20 70 72 6f  |program.exec pro|
00000070  67 72 61 6d 0a                                    |gram.|
00000075



Ahora copiamos el archivo a nuestro shell en el desplazamiento adecuado:


 ls  -al *.sh hola*
-rwxr-xr-x 1 kb kb 6449 2008-05-12 22:24 hola
-rw-r--r-- 1 kb kb   79 2008-05-12 22:23 hola.c
-rwxr-xr-x 1 kb kb 6492 2008-05-13 07:08 shell2.sh
-rwxr-xr-x 1 kb kb  117 2008-05-15 20:08 shell.sh


$ dd if=hola bs=1 seek=117 of=shell.sh              
6449+0 records in
6449+0 records out
6449 bytes (6.4 kB) copied, 0.103083 seconds, 62.6 kB/s


$ wc -c shell.sh 
6566 shell.sh

 hexdump -C shell.sh |head
00000000  23 21 2f 62 69 6e 2f 62  61 73 68 0a 65 63 68 6f  |#!/bin/bash.echo|
00000010  20 22 68 6f 6c 61 20 64  65 73 64 65 20 75 6e 20  | "hola desde un |
00000020  73 63 72 69 70 74 21 21  21 22 0a 64 64 20 69 66  |script!!!".dd if|
00000030  3d 73 68 65 6c 6c 2e 73  68 20 62 73 3d 31 20 73  |=shell.sh bs=1 s|
00000040  6b 69 70 3d 30 30 30 31  31 37 20 20 6f 66 3d 70  |kip=000117  of=p|
00000050  72 6f 67 72 61 6d 0a 63  68 6d 6f 64 20 2b 78 20  |rogram.chmod +x |
00000060  70 72 6f 67 72 61 6d 0a  65 78 65 63 20 70 72 6f  |program.exec pro|
00000070  67 72 61 6d 0a 7f 45 4c  46 01 01 01 00 00 00 00  |gram..ELF.......|
00000080  00 00

Asi se ve ahora el script por medio del edir vi

$vi
#!/bin/bash
echo "hola desde un script!!!"
dd if=shell.sh bs=1 skip=000117  of=program
chmod +x program
exec program
^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@ð<82>^D^H4^@^@^@ì^L^@^@^@^@^@^@4^@ ^@^G^@(^@$^@!^@^F^@^@^@4^@^@^@4<80>^D^H4<80>^D^Hà^@^@^@à^@^@^@^E^@^@^@^D^@^@^@^C^@^@^@^T^A^@^@^T<81>^D^H^T<81>^D^H^S^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A^@^@^@^@^@^@^@^@<80>^D^H^@<80>^D^Ht^D^@^@t^D^@^@^E^@^@^@^@^P^@^@^A^@^@^@t^D^@^@t<94>^D^Ht<94>^D^H^L^A^@^@^P^A^@^@^F^@^@^@^@^P^@^@^B^@^@^@<88>^D^@^@<88><94>^D^H<88><94>^D^HÐ^@^@^@Ð^@^@^@^F^ 00 00 00 02 00 03  00 01 00 00 00 f0 82 04  |................|
00000090  08 34 00 00 00 ec 0c 00  00 00 00 00 00 34 00 20  |.4...........4. 



El anterior programa tiene un problema y es que el exec no encuentra "program" asi que modificamos un poco el script :


#!/bin/bash
echo "hola desde un script!!!"
dd if=shell.sh bs=1 skip=000119  of=program  <-esta linea se modifico
chmod +x program
exec ./program  <-- esta linea se modifico
^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@ð<82>^D^H4^@^@^@ì^L^@^@^@^@^@^@4^@ ^@^G^@(^@$^@!^@^F^@^@^@4^@^@^@4<80>^D^H4<80>^D^Hà^@^@^@à^@^@^@^E^@^@^@^D^@^@^@^C^@^@^@^T^A^@^@^T<81>^D^H^T<81>^D^H^S^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A^@^@^@^@^@^@^@^@<80>^D^H^@<80>^D^Ht^D^@^@t^D^@^@^E^@^@^@^@^P^@^@^A^@^@^@t^D^@^@t<94>^D^Ht<94>^D^H^L^A^@^@^P^A^@^@^F^@^@^@^@^P^@^@^B^@^@^@<88>^D^@^@<88><94>^D^H<88><94>^D^HÐ^@^@^@Ð^@^@^@^F^@^@^@^D^@^@^@^D^@^@^@(^A^@^@(<81>^D^H(<81>^D^H ^@^@^@ ^@^@^@^D^@^@^@^D^@^@^@Qåtd^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^F^@^@^@^D^@^@^@/lib/ld-linux.so.2^@^@^D^@^@^@^P^@^@^@^A^@^@^@GNU^@^@^@^@^@^B^@^@^@^F^@^@^@^H^@^@^@^C^@^@^@^E^@^@^@^A^@^@^@^C^@^@^@^D^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^B^@^@^@^@^@^@^@^B^@^@^@^D^@^@^@^A^@^@^@^E^@^@^@^@ ^@ ^@^@^@^@^D^@^@^@­KãÀ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@ ^@
.......
.....


Con eso tenemos el punto final de nuestro experimento, ahora ya podemos ejecutar nuestro script que autosempaqueta un programa ejecutable:


./shell.sh
hola desde un script!!!
6450+0 records in
6450+0 records out
6450 bytes (6.4 kB) copied, 0.100495 seconds, 64.2 kB/s
hola mundo desde C  <----------   bingo !!!



Con eso ponemos punto final a este problema.