Toybox + Busybox creates more complete set of core utils

Background: I’ve been adding some software to some legacy embedded devices (which cannot, unfortunately, run OpenWrt) that still use 3.x kernels and uclibc 0.9.33.2 from thirteen years ago. Instead of trying to get modern software to compile with a toolchain made from dinosaur bones, I’ve simply set up musl toolchains with a lot of static libraries in order to build everything I need to drop onto these devices as static-PIE binaries.
So today I had SSHed into my OpenWrt router and I wanted to check the file type on something (I honestly forget what) and so I ran file whateverprogram only to get back file: not found. Then I remembered I had just done a static build of Toybox, which includes file, so I transferred the mipsel binary (972Kb, just under a megabyte) to my router. I thought it would be a waste to only link file to it, but I didn’t want competing versions of the same utility, so I took the output of toybox list and wrote this little script:

#!/bin/sh
cd /bin
bins="[ acpi arch ascii base32 base64 basename blkdiscard blkid blockdev bunzip2 bzcat cal cat chattr chgrp chmod chown chroot chrt chvt cksum clear cmp comm count cp cpio crc32 cut date dd deallocvt devmem df dirname dmesg dnsdomainname dos2unix du echo egrep eject env expand factor fallocate false fgrep file find flock fmt fold free freeramdisk fsfreeze fstype fsync ftpget ftpput getconf getopt gpiodetect gpiofind gpioget gpioinfo gpioset grep groups gunzip halt hd head help hexedit host hostname httpd hwclock i2cdetect i2cdump i2cget i2cset i2ctransfer iconv id ifconfig inotifyd insmod install ionice iorenice iotop kill killall killall5 link linux32 ln logger login logname losetup ls lsattr lsmod lspci lsusb makedevs mcookie md5sum memeater microcom mix mkdir mkfifo mknod mkpasswd mkswap mktemp modinfo mount mountpoint mv nbd-client nbd-server nc netcat netstat nice nl nohup nologin nproc nsenter od oneit openvt partprobe paste patch pgrep pidof ping ping6 pivot_root pkill pmap poweroff printenv printf prlimit ps pwd pwdx pwgen readahead readelf readlink realpath reboot renice reset rev rfkill rm rmdir rmmod rtcwake sed seq setfattr setsid sha1sum sha224sum sha256sum sha384sum sha3sum sha512sum shred shuf sleep sntp sort split stat strings su swapoff swapon switch_root sync sysctl tac tail tar taskset tee test time timeout top touch true truncate ts tsort tty tunctl uclampset ulimit umount uname unicode uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vmstat w watch watchdog wc wget which who whoami xargs xxd yes zcat"
for b in $bins; do
if command -v $b > /dev/null 2>&1; then
echo "$b already exists - skipping"
else
echo "linking toybox to $b"
ln -s toybox $b
fi
done

What was surprising to me after running it was just how many things Toybox wound up linking. I didn’t realize that the standard Busybox that comes with current images doesn’t include base64 or bunzip2, for example.

Here’s a list of the applets that it added:

acpi arch ascii base32 base64 blkdiscard blkid blockdev bunzip2 bzcat cal chattr chrt chvt cksum comm count cpio crc32 deallocvt devmem dnsdomainname dos2unix eject expand factor fallocate file fmt fold freeramdisk fsfreeze fstype ftpget ftpput getconf getopt gpiodetect gpiofind gpioget gpioinfo gpioset groups hd help hexedit host hostname httpd i2cdetect i2cdump i2cget i2cset i2ctransfer iconv inotifyd install ionice iorenice iotop killall5 link linux32 logname losetup lsattr lspci lsusb makedevs mcookie memeater microcom mix mkpasswd mountpoint nbd-client nbd-server netcat nl nohup nologin nproc nsenter od oneit openvt partprobe paste patch pkill pmap printenv prlimit pwdx pwgen readahead readelf realpath renice rev rfkill rtcwake setfattr sha1sum sha224sum sha384sum sha3sum sha512sum shred shuf sntp split stat su tac timeout truncate ts tsort tty tunctl uclampset unicode unix2dos unlink unshare usleep uudecode uuencode uuidgen vconfig vmstat w watch watchdog who whoami xxd

And here’s what it skipped:

[ basename cat chgrp chmod chown chroot clear cmp cp cut date dd df dirname dmesg du echo egrep env false fgrep find flock free fsync grep gunzip halt head hwclock id ifconfig insmod kill killall ln logger login ls lsmod md5sum mkdir mkfifo mknod mkswap mktemp modinfo mount mv nc netstat nice pgrep pidof ping ping6 pivot_root poweroff printf ps pwd readlink reboot reset rm rmdir rmmod sed seq setsid sha256sum sleep sort strings swapoff swapon switch_root sync sysctl tail tar taskset tee test time top touch true ulimit umount uname uniq uptime wc wget which xargs yes zcat

What was even more surprising was that I then searched opkg for a dynamic build of Toybox and found nothing. I have no idea why.

I really don’t think it would be a good replacement for Busybox, but it could be specifically compiled to only include the applets that Busybox isn’t providing and then added in to the core system. If as a static bin it’s still under a megabyte, as a more streamlined build not repeating any applets and using dynamic linking that file size could be far smaller.

Something to think about, at least.

If anyone wants to download my build for themselves you can here: https://github.com/akabul0us/nokia_superuser_tools/raw/refs/heads/main/post_exploitation/bin-mipsel/toybox but honestly I would encourage you to build it yourself – configuration and compilation time for toybox is very short, and then you can remove undesired features.

Cheers

What about suckless sbase? It is about total size of tool.

Taking a quick glance here https://git.suckless.org/sbase/file/README.html it appears to mostly implement tools already in busybox, which would make it more of an alternative than a supplement. Perhaps I'm biased because `file` (the tool I originally wanted) isn't in sbase.