"Después del juego es antes del juego"
Sepp Herberger

miércoles, 16 de diciembre de 2020

Enredando con una ROM Rockchip RK3326 (II): examinando las particiones de la imagen.

Ya hemos desempaquetado la ROM en Enredando con una ROM Rockchip RK3326 (I). Vamos ahora a ver la estructura. Tenemos un directorio donde se ha descomprimido la imagen:
# tree Image/
└── Image
   ├── boot.img
   ├── kernel.img
   ├── kernel.img.krnl
   ├── MiniLoaderAll.bin
   ├── misc.img
   ├── oem.img
   ├── parameter.txt
   ├── parameter.txt.parm
   ├── recovery.img
   ├── resource.img
   ├── system.img
   ├── trust.img
   ├── uboot.img
   └── vendor.img
Veamos de que tipo es cada fichero:
# file ./Image/*
boot.img:           Android bootimg, kernel (0x10008000), ramdisk (0x11000000), second stage (0x10f00000), page size: 2048, cmdline (buildvariant=user)
kernel.img:         data
kernel.img.krnl:    data
MiniLoaderAll.bin:  data
misc.img:           data
oem.img:            Android sparse image, version: 1.0, Total of 32768 4096-byte output blocks in 6 input chunks.
parameter.txt:      ASCII text, with very long lines
parameter.txt.parm: Par archive data
recovery.img:       Android bootimg, kernel (0x10008000), ramdisk (0x11000000), second stage (0x10f00000), page size: 2048, cmdline (buildvariant=user)
resource.img:       data
system.img:         Android sparse image, version: 1.0, Total of 665600 4096-byte output blocks in 20 input chunks.
trust.img:          data
uboot.img:          data
vendor.img:         Android sparse image, version: 1.0, Total of 98304 4096-byte output blocks in 8 input chunks.
Hay un poco de todo:
  • Ficheros en formato "data" (que significa "no sé que carajos hay ahí dentro")
  • Ficheros en formato "Android bootimg"
  • Ficheros en formato "Android sparse image"
Todo estos ficheros se corresponden con distintas particiones del sistema Android, que en el caso de nuestra tablet serian realmente:
Partition Info(gpt):
NO  LBA        Size       Name
01  0x00004000 0x00002000 uboot
02  0x00006000 0x00002000 trust
03  0x00008000 0x00002000 misc
04  0x0000a000 0x00008000 resource
05  0x00012000 0x00010000 kernel
06  0x00022000 0x00010000 boot
07  0x00032000 0x00020000 recovery
08  0x00052000 0x00038000 backup
09  0x0008a000 0x00002000 security
10  0x0008c000 0x000c0000 cache
11  0x0014c000 0x00514000 system
12  0x00660000 0x00008000 metadata
13  0x00668000 0x000c0000 vendor
14  0x00728000 0x00040000 oem
15  0x00768000 0x00000400 frp
16  0x00768400 0x032d5bdf userdata
Las particiones anteriores las averiguamos entrando en "adb shell" y haciendo algo de esto.

Como se ve, casi todas las particiones reales de la tablet se corresponden con un fichero .img de los extraidos de update.img. Al actualizar el firmware con update.img lo que se hace realmente es desempaquetar y escribir estos ficheros sobre cada partición, ni más ni menos.

Vamos a husmear dentro de las particiones que podamos descomprimir, a ver que encontramos. Las "data" no las tocamos porque no sabemos como abrirlas.

1. Examinando particiones Android bootimg.

Empezamos por boot.img, que está en formato "Android bootimg":
# wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/android-serialport-api/android_bootimg_tools.tar.gz
# tar xfvz android_bootimg_tools.tar.gz
-rwxrwxr-x cedric/cedric 12095 2011-10-31 20:54 mkbootimg
-rwxrwxr-x cedric/cedric 11936 2011-10-31 20:54 unpackbootimg
# mkdir boot
# ./unpackbootimg -i /ruta/al/fichero/boot.img -o boot
Con esto se desempaqueta el contenido al directorio boot. Veamos que hay dentro:
# file boot/*
boot.img-base:     ASCII text
boot.img-cmdline:  ASCII text
boot.img-pagesize: ASCII text
boot.img-ramdisk:  ASCII cpio archive (SVR4 with no CRC)
boot.img-zImage:   data
En los 3 primeros ficheros son de configuración, los dos últimos son imágenes comprimidas de ramdisk e imagen boot. Vamos a descomprimir el ramdisk, que contiene dentro un sistema de ficheros:
# mkdir ramdisk
# cd ramdisk
# zcat ../boot.img-ramdisk.gz | cpio -idmv
# cd ..
# tree ramdisk
ramdisk/
├── acct
├── bugreports -> /data/user_de/0/com.android.shell/files/bugreports
├── cache -> /data/cache
├── charger -> /sbin/charger
├── config
├── d -> /sys/kernel/debug
├── data
├── default.prop -> system/etc/prop.default
├── dev
├── drmboot.ko
├── etc -> /system/etc
├── fstab.rk30board
├── init
├── init.connectivity.rc
├── init.environ.rc
├── init.optee.rc
├── init.rc
├── init.rk30board.bootmode.emmc.rc
├── init.rk30board.bootmode.nvme.rc
├── init.rk30board.bootmode.unknown.rc
├── init.rk30board.environment.rc
├── init.rk30board.rc
├── init.rk30board.usb.rc
├── init.rk3326.rc
├── init.rockchip.rc
├── init.usb.configfs.rc
├── init.usb.rc
├── init.zygote32.rc
├── init.zygote64_32.rc
├── mnt
├── oem
├── proc
├── res
│   └── images
│       └── charger
│           ├── battery_fail.png
│           ├── battery_scale.png
│           └── font.png
├── rk30xxnand_ko.ko
├── sbin
│   ├── charger
│   ├── mkdosfs
│   ├── ueventd -> ../init
│   └── watchdogd -> ../init
├── sdcard -> /storage/self/primary
├── storage
├── sys
├── system
├── ueventd.rc
├── ueventd.rk30board.rc
├── vendor
└── verity_key
Como se ve, tiene un sistema de ficheros con sus script y procesos para el arranque del tablet. Si hacemos lo mismo con recovery.img veremos que hay un minisistema Linux con muchas cositas. Recordemos que el recovery es un Linux que muestra un menú con ciertas funciones básicas de formateo y poco más.
# mkdir recovery
# ./unpackbootimg -i /ruta/al/fichero/recovery.img -o recovery
# cd ramdisk-recovery
# zcat ../recovery.img-ramdisk.gz | cpio -idmv
# cd ..
# tree ramdisk-recovery
ramdisk-recovery/
├── acct
├── bugreports -> /data/user_de/0/com.android.shell/files/bugreports
├── charger -> /sbin/charger
├── config
├── d -> /sys/kernel/debug
├── data
│   └── misc
│       └── wifi
├── default.prop -> prop.default
├── dev
├── drmboot.ko
├── etc
│   ├── bluetooth
│   ├── firmware
│   ├── mke2fs.conf
│   └── recovery.fstab
├── fstab.rk30board
├── init
├── init.rc
├── init.rk30board.usb.rc
├── init.usb.configfs.rc
├── init.usb.rc
├── lib
│   └── firmware
├── mnt
.........................
.........................
.........................
│   │       └── wifi_efuse_8723ds.map
│   ├── firmware
│   └── lib
│       └── modules
│           └── wifi
│               ├── 8188eu.ko
│               ├── 8188fu.ko
│               ├── 8189es.ko
│               ├── 8189fs.ko
│               ├── 8723bs.ko
│               ├── 8723bu.ko
│               ├── 8723cs.ko
│               ├── 8723ds.ko
│               ├── 8822be.ko
│               └── bcmdhd.ko
└── verity_key
Bueno, pues esto no tiene más. Vamos a avanzar mirando más cosas.

2. Examinando particiones Android sparse image.

Vamos a las otras particiones, las "Android sparse image": oem, vendor y system, descritas de forma general aquí. El formato "sparse image" es un tipo de imagen comprimido con menos huecos en blanco para ocupar menos tamaño. Para trabajar con su contenido hay que descomprimir el archivo antes de nada, usando la utilidad "simg2img" que está en el paquete Ubuntu "android-tools-fsutils".
# apt-get install android-tools-fsutils
# mkdir system
# cd system
# simg2img /ruta/al/fichero/system.img  system-no-sparse.img
# file system-no-sparse.img
system-no-sparse.img: Linux rev 1.0 ext4 filesystem data, UUID=bd2ccb6e-a94e-53be-9c80-3f438b342f58, volume name "system" (extents) (64bit) (large files) (huge files)
El sistema ext4 es ya un sistema manejable que podemos montar con "mount -o loop":
# mount -t auto -o loop system-no-sparse.img  mnt
# tree -d mnt/
├── app
│   ├── BasicDreams
│   │   └── oat
│   │       └── arm64
│   ├── Bluetooth
│   │   ├── lib
│   │   │   └── arm64
│   │   └── oat
│   │       └── arm64
......................
......................
│   └── YouTube
│       └── oat
│           ├── arm
│           └── arm64
├── bin
│   └── hw
├── etc
│   ├── bluetooth
│   ├── init
│   ├── permissions
│   ├── ppp
│   ├── preferred-apps
│   ├── seccomp_policy
│   ├── security
│   │   └── cacerts
│   ├── selinux
│   │   └── mapping
│   └── sysconfig
├── fake-libs
├── fake-libs64
├── fonts
├── framework
│   ├── arm
│   ├── arm64
│   └── oat
│       ├── arm
│       └── arm64
├── lib
│   ├── drm
│   ├── hw
│   ├── modules
│   └── vndk-sp
├── lib64
│   ├── drm
│   ├── hw
│   └── vndk-sp
├── lost+found
├── media
│   └── audio
│       ├── alarms
│       ├── notifications
│       ├── ringtones
│       └── ui
├── priv-app
│   ├── BackupRestoreConfirmation
│   │   └── oat
│   │       └── arm64
......................
......................
│   │       └── arm64
│   └── WallpaperCropper
│       └── oat
│           └── arm64
├── tts
│   └── lang_pico
└── usr
    ├── hyphen-data
    ├── icu
    ├── idc
    ├── keychars
    ├── keylayout
    ├── share
    │   ├── bmd
    │   └── zoneinfo
    └── srec
        └── en-US
Esta partición como vemos contiene el sistema android completo, que se monta sobre /system en el arranque, con todas las aplicaciones por defecto y todo el entorno y comandos. Si queremos retocar el firmware del tablet será esta partición la que hay que modificar.

Las otras dos particiones son oem.img y vendor.img. Oem "incorporates OEM (Original Equipment Manufacturer i.e. hardware manufacturer or Mobile Phone brand) small customization (modifications) to original Android (AOSP) during OTA updates such as customized system properties values etc.". Esta partición se monta sobre la ruta /oem cuando el tablet está en funcionamiento. La convertimos y montamos como hicimos con system:
# mkdir oem
# cd oem
# simg2img /ruta/al/fichero/oem.img  oem-no-sparse.img
# file oem-no-sparse.img
oem-no-sparse.img: Linux rev 1.0 ext4 filesystem data, UUID=bd2ccb6e-a94e-53be-9c80-3f438b342f58, volume name "oem" (extents) (64bit) (large files) (huge files)
# mkdir mnt-oem
# mount -t auto -o loop oem-no-sparse.img  mnt-ome
# tree mnt-oem/
mnt-oem/
├── etc
│   ├── fs_config_dirs
│   ├── fs_config_files
│   └── package_performance.xml
└── lost+found
Para la partición vendor "is sister partition of /system on Android devices which holds system applications and libraries that do not have source code available on AOSP but added by vendors (OEM's). It also contains SoC firmware images i.e. hardware specific libraries and binaries (OpenGL, ISP...). Proprietary blobs (HALs) usually live in /vendor as shared libraries (.so files) which are loaded by Android binders when processes call a hardware component. This partition was optional before Treble support and /vendor used to be just a symlink to /system/vendor"..
# mkdir vendor
# cd vendor
# simg2img /ruta/al/fichero/vendor.img  vendor-no-sparse.img
# mount -t auto -o loop vendor-no-sparse.img  mnt
# tree -d mnt/
├── bin
│   └── hw
├── etc
│   ├── bluetooth
│   ├── firmware
│   ├── init
│   ├── permissions
│   ├── seccomp_policy
│   ├── selinux
│   ├── usb_modeswitch.d
│   └── wifi
├── lib
│   ├── egl
│   ├── hw
│   ├── mediacas
│   ├── mediadrm
│   ├── modules
│   │   └── wifi
│   ├── optee_armtz
│   ├── soundfx
│   └── vndk-sp
├── lib64
│   ├── egl
│   ├── hw
│   ├── mediacas
│   ├── mediadrm
│   ├── soundfx
│   └── vndk-sp
├── lost+found
├── media
└── overlay
    └── SysuiDarkTheme
Y ya está. Supongo que la mayoría de las imágenes de los distintos sistemas Android se desempaquetará y manejará de una menera similar.


La sonda Chang'e-5 recogió sus 2kg de rocas lunares (ya era hora desde los años 70), despegó y está de vuelta.



Hoy 16 de diciembre caerá en algún sitio de Mongolia Interior (que buen lugar para mandar a algunos que yo me sé). Como vemos, si hace falta irán a caballo a recuperarla donde aterrice.


lunes, 14 de diciembre de 2020

Enredando con una ROM Rockchip RK3326 (I): desempaquetando la imagen.

Los tablets Techcomputer L108MR que nos enviaron tienen un chipset Rockchip rk3326 (esto lo averiguamos con un "adb shell getprop"). Como tenemos el fichero update.img con la imagen de la ROM suministrada por el distribuidor, he estado curioseando en sus entrañas, ya que no conocía mucho sobre estos chipsets del tipo Rockchip (muy usados en Android TV) y tenia interés por él. El fichero update.img viene en un formato desconocido:
# file update.img 
update.img: data
Hay varias maneras de desempaquetar este fichero para ver su contenido:

1. imgRePackerRK.

Empezamos con imgRePackerRK (RockChip's firmware images unpacker/packer). El nombre lo dice todo, lo descargamos y lo ejecutamos:
# ./imgrepackerrk update.img
	imgrepackerrk (version 1.06 linux)
	Rockchip firmware batch/update images unpacker/packer
    .....
	--- Firmware unpacking ---
	Can't open file "update.img" for reading
La primera en la frente. Dice que no puede abrir update.img. Tras mil pruebas no he sido capaz de hacer que funcione en Ubuntu: ni cambiando a otro sistema de archivos, ni cambiando permisos, ni nada. Pero probando en Windows si funciona, toma ya:
C:\Users\Administrador\Desktop\rockchip>imgrepackerrk update.img

        imgRepackerRK (version 1.06 windows)
        Rockchip firmware batch/update images unpacker/packer

        (c) RedScorpio, Moscow, 2013-2017
            RedScorpio@land.ru

        Detected OS:    Windows 7 Pro SP 1.0 [build 7601] x64
        ==========================[ START ]==========================

        --- Firmware unpacking ---

        "RKFW" image file detected

        Batch image header from "update.img" was read
        Image properties:
                Type            RockChip batch image ("RKFW")
                Version         8.1.0
                Date            2019.11.19
                Time            10:15:30
                ChipID          0x33333236
                Code(?)         0x01060000
                RKFWtype        0x00000001
                Unknown_1       0x00000000

        Unsupported ChipID
Se queja con "Unsupported ChipID". Menos mal que imgrepackerrk tiene el parámetro "/cid" para soslayar esto:
# imgrepackerrk 	Rockchip firmware batch/update images unpacker/packer

	(c) RedScorpio, Moscow, 2013-2017
	    RedScorpio@land.ru

	Usage:	./imgrepackerrk [options] 
	
		./imgrepackerrk [options] <[path/]name[.ext]>		- for unpacking
		./imgrepackerrk [options] <[path/]name[.ext]>.dump	- for packing
		./imgrepackerrk [options] <[path/]name[.ext]>.cfg	- for 2nd layer files packing
	
	Options:
		/log			- write log
		/debug			- debug mode on (works with /log option)
		/quiet			- don't output to console
		/mono			- monochrome mode on
		/md5			- ignore md5 errors (unpacking)
					- don't add md5 summ (packing)
		/rkcrc			- ignore RockChip CRC errors (unpacking)
		/rkaf			- make RKAF image (packing)
		/skip			- skip image size check (unpack option)
		/2nd			- unpack/pack 2-nd layer files
		/cid			- don't check ChipID (unpack option)
		/rmd4			- alignment of ramdisk in Android Boot image (pack option)
		/bcpath:		- base path for RKAndroidTool's cfg
		/lname:<[path]name>	- loader file name for RKAndroidTool's cfg
		/ini			- rewrite *.ini-file with new parameters
Por tanto, hacemos:
C:\Users\Administrador\Desktop\rockchip>imgrepackerrk /cid update.img

        imgRepackerRK (version 1.06 windows)
        Rockchip firmware batch/update images unpacker/packer

        (c) RedScorpio, Moscow, 2013-2017
            RedScorpio@land.ru

        Detected OS:    Windows 7 Pro SP 1.0 [build 7601] x64
        ==========================[ START ]==========================

        --- Firmware unpacking ---

        "RKFW" image file detected

        Batch image header from "update.img" was read
        Image properties:
                Type            RockChip batch image ("RKFW")
                Version         8.1.0
                Date            2019.11.19
                Time            10:15:30
                ChipID          0x33333236
                Code(?)         0x01060000
                RKFWtype        0x00000001
                Unknown_1       0x00000000

        -- boot.img processing --

        -- update.img processing --
        Update data header from "update.img" was read

        Corrected RKAF header properties
        Image properties:
                Type            RockChip update image ("RKAF")
                Id              "007"
                Model           "RK3326"
                Manufacturer    " RK3326"
                Version         8.1.0

        - Files extracting -
        Image files count = 14

        package-file (package-file)             extracted (format: unknown)
        bootloader (Image/MiniLoaderAll.bin)            extracted (format: RockChip bootloader image)
        parameter (Image/parameter.txt.parm)            extracted (format: RockChip PARM signed file)
        .....
        .....     
Y se desempaqueta todo en un directorio llamado update.img.dump con la estructura de todas las particiones contenidas en ella:
# tree update.img.dump/
update.img.dump/
├── config_16.cfg
├── config_8.cfg
├── Image
│   ├── boot.img
│   ├── kernel.img
│   ├── kernel.img.krnl
│   ├── MiniLoaderAll.bin
│   ├── misc.img
│   ├── oem.img
│   ├── parameter.txt
│   ├── parameter.txt.parm
│   ├── recovery.img
│   ├── resource.img
│   ├── system.img
│   ├── trust.img
│   ├── uboot.img
│   └── vendor.img
├── image.cfg
├── image.md5
└── package-file
El proceso inveso de generar un update.img a partir del directorio sería:
# imgrepackerrk udate.img.dump
.....
Genera un update.img que podríamos usar para actualizar completamente el dispositivo.

2. Herramientas oficiales de Rockchip para Linux.

En esta dirección de github tenemos Linux Pack Firmware, que trae scripts para empaquetado/desempaquetado de multitud de chipsets. Descargamos todo y haciendo:
# ./unpack.sh update.img
Este script (que internamente usa los ejecutables rkImageMaker y afptool) desempaqueta todo el contenido en el directorio "output".

La operación inversa se haría con el script "rk3326-mkupdate.sh":
# ls Image
boot.img  kernel.img  MiniLoaderAll.bin  misc.img  oem.img  parameter.txt  recovery.img  resource.img  system.img  trust.img  uboot.img  update.img  vendor.img
# ./rk3326-mkupdate.sh 
start to make update.img...
Android Firmware Package Tool v1.66
------ PACKAGE ------
Add file: ./package-file
Add file: ./package-file done,offset=0x800,size=0x2ac,userspace=0x1
Add file: ./Image/MiniLoaderAll.bin
Add file: ./Image/MiniLoaderAll.bin done,offset=0x1000,size=0x4714e,userspace=0x8f
Add file: ./Image/parameter.txt
Add file: ./Image/parameter.txt done,offset=0x48800,size=0x2aa,userspace=0x1
Add file: ./Image/trust.img
Add file: ./Image/trust.img done,offset=0x49000,size=0x400000,userspace=0x800
Add file: ./Image/uboot.img
Add file: ./Image/uboot.img done,offset=0x449000,size=0x400000,userspace=0x800
Add file: ./Image/misc.img
Add file: ./Image/misc.img done,offset=0x849000,size=0xc000,userspace=0x18
Add file: ./Image/resource.img
Add file: ./Image/resource.img done,offset=0x855000,size=0x119000,userspace=0x232
Add file: ./Image/kernel.img
Add file: ./Image/kernel.img done,offset=0x96e000,size=0x17c3014,userspace=0x2f87
Add file: ./Image/boot.img
Add file: ./Image/boot.img done,offset=0x2131800,size=0x1a46000,userspace=0x348c
Add file: ./Image/recovery.img
Add file: ./Image/recovery.img done,offset=0x3b77800,size=0x3c3f800,userspace=0x787f
Add file: ./Image/system.img
Add file: ./Image/system.img done,offset=0x77b7000,size=0x79491a14,userspace=0xf2924
Add file: ./Image/vendor.img
Add file: ./Image/vendor.img done,offset=0x80c49000,size=0xd08c07c,userspace=0x1a119
Add file: ./Image/oem.img
Add file: ./Image/oem.img done,offset=0x8dcd5800,size=0x3c064,userspace=0x79
Add CRC...
Make firmware OK!
------ OK ------
********RKImageMaker ver 1.66********
Generating new image, please wait...
Writing head info...
Writing boot file...
Writing firmware...
Generating MD5 data...
MD5 data generated successfully!
New image generated successfully!
Making update.img OK.
el cual, a partir del contenido del directorio Image como el mostrado en el código anterior crea un update.img nuevo.

Otra herramienta de desempaquetado de este repositorio github es Firmware Merger, pero no parece funcionar:
#  ./firmware_merger -U ./update.img update
Start to unpack firmware...
UnPacking Error:Tag of firmware is wrong, tag=0x57464b52!
Tampoco he indagado más.

3. Otras herramientas de terceros de Rockchip para Linux.

Hay muchos repositorios con proyectos personales que incluyen versiones particulares de herramientas de empaquetado/desempaquetado, como éste. Los pasos suelen ser:
# ./img_unpack update.img image.img
# mkdir img
#./afptool -unpack image.img img
....
UnPack OK!
Quedando todo desempaqetado en el directorio img.

4. Herramienta AndroidToolRelease de Windows.

Es una aplicación de Windows con interface gráfica para manejar firmwares de Rockchip. Antes de usarla hay que instalar los drivers mediante DriverAssistant. Para extraer el contenido de update.img vamos a la pestaña de Advanced Function y en Firmware seleccionamos el .img dando a "Unpack" luego. Eso desempaqueta todo.


Bueno, pues esta es la variedad de cosas que tenemos para desempaquetar update.img. En posts futuros husmearemos dentro de lo desempaquetado.


Ahí va, la Starship SN8 sube a 12.5km (las pruebas anteriores subieron 150m), apaga motores, baja en horizontal frenando con la atmósfera, se endereza... y se da el castañazo en el punto de partida. Explicado magistralmente por Daniel Marín.



La finalidad de la prueba, recopilar datos del planeo, ha sido exitosa. El aterrizaje perfecto hubiera estado bonito pero hubiera sido mucha suerte que funcionase. En fin, hemos estado 12.5km más cerca de Marte. Ahora a esperar en directo al SN9 cuando diga tito Elon.

jueves, 3 de diciembre de 2020

Alternar entre dos servidores DNS con un atajo de teclado.

He necesitado alternar entre el servidor DNS de mi red (192.168.1.100) y el de Google (8.8.8.8) para hacer unas pruebas con pihole. Como editar resolv.conf cada vez que quería cambiar el DNS es un coñazo, he hecho un script:
# cat /usr/local/bin/change_dns
#!/bin/bash

if grep "8.8.8.8" /etc/resolv.conf > /dev/null 2>&1
then
  sed -i "s/^nameserver.*$/nameserver 192.168.1.100/g" /etc/resolv.conf
else
  sed -i "s/^nameserver.*$/nameserver 8.8.8.8/g" /etc/resolv.conf
fi
exit 0
Para modificar el resolv.conf hay que ser root y yo quiero que esto lo haga cualquier usuario, asi que meto en /etc/sudoers:
# cat /etc/sudoers
.....
ALL   ALL = (ALL) NOPASSWD: /usr/local/bin/change_dns
.....
Ahora cualquier mindundi podrá ejecutarlo siendo root por un instante con "sudo /usr/local/bin/change_dns" o "gksu /usr/local/bin/change_dns".

Por último hay que definir el atajo de teclado para ejecutar el comando. Como usuario regular (no root) hacemos en un terminal:
$ xfconf-query -c xfce4-keyboard-shortcuts -p '/commands/custom/<Alt>d' --create -v -t string -s "gksu /usr/local/bin/change_dns"
Con esto vinculamos la combinacion de teclas Alt+d con la ejecución del script. Esto queda definido solo para el usuario actual, es decir, no es un atajo de teclado que se defina a nivel de sistema como hicimos aquí. La definición del atajo se guarda en:
$ cat .config/xfce4/xfconf/xfce-perchannel-xml/xfce4-keyboard-shortcuts.xml
<channel name="xfce4-keyboard-shortcuts" version="1.0">
  <property name="commands" type="empty">
    <property name="default" type="empty">
      .....
     <property name="custom" type="empty">
      <property name="override" type="bool" value="true"/>
      <property name="&lt;Alt&gt;d" type="string" value="gksu /usr/local/bin/change_dns"/>
      .......
    </property>
  </property>
  .....
</channel>
Y ya está, pulsando Alt-d pasa de un servidor DNS a otro de forma inmediata.
Bueno, ya está. La Chang'e 5 alunizó:



Ahora está recogiendo muestras, para subir, acoplarse al módulo de vuelta y traer el material a la Tierra. Es curioso, es el mismo esquema que el de una misión tripulada. Algo están tramando.

De momento nos deleitamos con esta preciosa foto de gran calidad: