POSTS
Epson Printer Deamon under QEMU
Why
Because it’s fun and because I believe in the motto hack dumb shit
What
In the last post I mentioned that most nwsoc
dies in pain without it’s kernel module is not supplied. The scope of this article is to give a brief overview on two things:
- Running a dynamic binary complied for Arm that doesn’t use glibc
- Hooking function calls in a binary using LD_PRELOAD
Different LibC
Computers are complex. To deal with this complexity C programmers found out that it was handy to have commonly used functions readily available, so they invented the standard C library. As with all inventions there were different idea on what was best, and so there were different standard library implementations.
Most linux system you’re likely to encounter will run the GNU implementation of the C standard library. You can tell that’s the case just by doing:
➜ squashfs-root-0 objdump -T /bin/git | head
/bin/git: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __uflow
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getenv
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 utime
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigprocmask
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 __snprintf_chk
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 raise
By using dumping the dynamic symbols we can see that they all refer GLIBC
, which stands for GnuLIBC. Git runs fine on my machine, because I have the right LibC. What happens if you run a program compiled with a different libc?
➜ squashfs-root-0 ./nwsoc
/lib/ld-uClibc.so.0: No such file or directory
It doesn’t work. We need to have the right libc. Two things to note here:
- Don’t even dare to change your computer libc. You will suffer greatly. If you want to know what happened when I tired to do so just ask my friend Daniele
- If you try to do this on your computer, with a binary compiled for another architecture you might get something like
wrong exec format
. On my system I added qemu user to bin_fmt. On arch is super simple, but ymmv.
Getting a system with the right LibC
Sometimes, if you’re lucky, you might find a root filesystem inside the binary archive you’re reversing. This is super handy, but still not optimal for reasons that we’ll see later. The best way I’ve found so far is to use buildroot to, you guessed it, build a root file system that uses a given libc.
Building a rootfs using buildroot is super simple:
- Download buildroot for the official website
- Extract the
tar.gz
archive and cd into it. You should get something like this:
➜ buildroot-2019.02.9 ll
total 732K
drwxrwxr-x 2 carlo carlo 4,0K 12. Jan 16:27 arch
drwxrwxr-x 54 carlo carlo 4,0K 12. Jan 16:27 board
drwxrwxr-x 22 carlo carlo 4,0K 12. Jan 16:27 boot
-rw-rw-r-- 1 carlo carlo 319K 12. Jan 16:27 CHANGES
-rw-rw-r-- 1 carlo carlo 27K 12. Jan 16:27 Config.in
-rw-rw-r-- 1 carlo carlo 132K 12. Jan 16:27 Config.in.legacy
drwxrwxr-x 2 carlo carlo 12K 12. Jan 16:27 configs
-rw-rw-r-- 1 carlo carlo 19K 12. Jan 16:27 COPYING
-rw-rw-r-- 1 carlo carlo 54K 12. Jan 16:27 DEVELOPERS
drwxr-xr-x 5 carlo carlo 4,0K 12. Jan 16:28 docs
drwxrwxr-x 18 carlo carlo 4,0K 12. Jan 16:27 fs
drwxrwxr-x 2 carlo carlo 4,0K 12. Jan 16:27 linux
-rw-rw-r-- 1 carlo carlo 2,3K 12. Jan 16:27 Makefile.legacy
drwxrwxr-x 2164 carlo carlo 68K 12. Jan 16:27 package
-rw-rw-r-- 1 carlo carlo 1,1K 12. Jan 16:27 README
drwxrwxr-x 13 carlo carlo 4,0K 12. Jan 16:27 support
drwxrwxr-x 3 carlo carlo 4,0K 12. Jan 16:27 system
drwxrwxr-x 5 carlo carlo 4,0K 12. Jan 16:27 toolchain
drwxrwxr-x 3 carlo carlo 4,0K 12. Jan 16:27 utils
- Run
make menuconfig
. For our case we need a ARM based filesystem, so underTarget Options->Target Architecture
we’ll selectARM little endian
. Libc selection happens underToolchain->C Library
. We can also ask buildroot to create a development system for our give configuration. This take some time, but will be super helpful later. This last step is simply a matter of selecting every tool we like to use underTarget Packages
- Wait. Even if your computer is fast this will take a while. With my big machine it usually takes around 1.5 hours, while on the ultrabook it takes easily 4.
Chroot in the new file system
If you made so far we are now ready to run nwsoc
. We just have to chroot into the filesystem buildroot has just created, which is in buildroot-2019.02.8/output/
. In this folder there’re quite a few things:
➜ output ll
total 20K
drwxr-xr-x 55 carlo carlo 4,0K 23. Dez 12:23 build
drwxr-xr-x 9 carlo carlo 4,0K 23. Dez 11:32 host -> a toolchain to compile stuff for the new filesystem
drwxr-xr-x 2 carlo carlo 4,0K 23. Dez 12:23 images
lrwxrwxrwx 1 carlo carlo 121 23. Dez 12:23 staging -> symlink to buildroot-2019.02.8/output/host/arm-buildroot-linux-uclibcgnueabi/sysroot
drwxr-xr-x 17 carlo carlo 4,0K 23. Dez 12:02 target -> a new rootfs with uclibc and the userspace tools we built
If you haven’t messed yet with bin_fmt none of the following will make sense. If you don’t wont to do so, please add prefix every command with qemu-user
. Remember that you’ll have to copy qemu-user
inside the chroot.
Let’s do it:
➜ printer sudo chroot uclib_chroot /bin/sh
/ # ls -la
total 8212
drwxr-xr-x 17 1000 1000 4096 Feb 8 17:53 .
drwxr-xr-x 17 1000 1000 4096 Feb 8 17:53 ..
-rw-r--r-- 1 1000 1000 1336 Dec 23 11:34 THIS_IS_NOT_YOUR_ROOT_FILESYSTEM
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 bin
drwxr-xr-x 4 1000 1000 4096 Dec 23 11:34 dev
drwxr-xr-x 5 1000 1000 4096 Dec 23 11:34 etc
drwxr-xr-x 2 1000 1000 4096 Feb 8 17:57 lib
lrwxrwxrwx 1 1000 1000 3 Dec 23 11:34 lib32 -> lib
lrwxrwxrwx 1 1000 1000 11 Dec 23 11:34 linuxrc -> bin/busybox
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 media
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 mnt
-rwxr-xr-x 1 1000 1000 8333684 Feb 8 17:53 nwsoc
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 opt
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 proc
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:44 root
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 run
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 sbin
drwxr-xr-x 2 1000 1000 4096 Dec 23 11:34 sys
drwxr-xr-x 2 1000 1000 4096 Dec 23 12:21 tmp
drwxr-xr-x 7 1000 1000 4096 Dec 23 11:34 usr
drwxr-xr-x 3 1000 1000 4096 Dec 23 11:34 var
# ./nwsoc
Starting Planet ...
[PLANET] <ILLEGAL>:main(243):sched_getparam() failed. <Function not implemented>
[PLANET] <ILLEGAL>:psmStopPlanet(953):*** FATAL ERROR -- Send emergency code(1) to Main.
It runs. YAY!!
If you didn’t configure binfmt on your system you have to run qemu-arm nwsoc
Patching functions
A few of you will have noted that while the demon is technically running, it dies badly because it’s trying to use some functions that can’t run properly in a chroot. We can fix this using LD_PRELOAD
.
# ./qemu-arm -E LD_PRELOAD=./pthread_patch.so nwsoc
Starting Planet ...
Unsupported ioctl: cmd=0x4004fe0b
[nwpslib_set_nwps_state] EPIDC_IOC_SET_NWPS_STATE error.
****** PNPSMStartMain() START!! ******
Unsupported ioctl: cmd=0x400cfe00
[PLANET] <ILLEGAL>:D4CbtExecSysCmd(1882):D4CbtOpenChannel()=-5
[PLANET] <ILLEGAL>:D4GetSysCmdData(1023):command error.
[PLANET] <ILLEGAL>:prInit(859):D4_GetDeviceID return error(-4)
[PLANET] <ILLEGAL>:PNPSMStartMain(199):prIF(prInit()) initialize error!!
[PLANET] <ILLEGAL>:psmStopPlanet(953):*** FATAL ERROR -- Send emergency code(4) to Main.
What the hell just happened? Simple, just stubbed pthread_attr_setschedparam
and pthread_attr_setdetachstate
. The first time I saw this I was like “OMFG” it literally blew my mind, but at the core it’s super simple.
- Find what function makes your program crash. For example,
pthread_attr_setschedparam
tries to change the scheduling priority of the current running process, which fails underqemu-arm
- Understand what’s the “normal” return value. In this case it’s super simple, just check on the manpages and you’re done.
- Create a new function that has the same name and the same input parameters. In this case is pretty simple:
#include <pthread.h>
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param){
return 0;
}
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate){
return 0;
}
I’ve included pthread.h just to use the typedefs. Call me lazy if you want.
- Compile this function as a library using the toolchain built before, this extra step will make sure that our hook will match the types that the program is expecting. Again, pretty simple:
arm-buildroot-linux-uclibcgnueabi-gcc -c -fpic ./pthread_patch.c -o ./pthread_patch.o
arm-buildroot-linux-uclibcgnueabi-gcc -shared -o ./pthread_patch.so ./pthread_patch.o
- LD_PRELOAD your new library.
- Find out what you have to patch next.
Conclusion
I don’t know. I hope that you learned something useful, for sure I had great fun writing this. There are a few things I didn’t mention, for example nwsoc
needs some additional libraries to run, but they are in the rootfs.
Super important, don’t do the dumb mistake of ld preloding something into qemu, use -E
flag.