Reverse Engineering usb_printerid with Source

by Javantea
Jan 31, 2015

JavRE-0.2 [sig]
JavRE-0.1.1 [sig]
Git repository: git clone https://www.altsci.com/repo/javre.git
JavRE project page

Introduction

Reverse Engineering is an interesting and useful task which allows a person to gain knowledge about the software they use or that other people use. This basic tutorial for those who have had trouble getting started shows the methods on a file for which we have source code. It's a linux binary which is part of foo2zjs, an open source printer driver for HP printers. Like many Linux drivers, foo2zjs is made up of simple command-line executables which follow the Unix philosophy, do one thing and do it well. Of course there are exceptions to this, but the program we're reversing today was picked by me because it's important and small. Isn't that a nice combination? How small is /bin/usb_printerid? It's 8.4kB not stripped, 6.2kB stripped when compiled 64-bit. It is very similar in size to the simple 13 line program if2, which is 7.9kB unstripped, 6.1kB stripped. So what does usb_printerid do? Let's assume that we didn't have a name. Let's call it black for a few minutes.

Handling Untrustworthy Executables

Since black came from an untrusted source and was not signed by the author anyway, we consider it untrustworthy. Even if it came from a trusted source, we couldn't verify it, so we have to explicitly not trust it. How do we run something without trusting it? The Android model for running untrusted apps is to give the app a user and don't give that user access to the rest of the system. How difficult is that? Very difficult. For one, the kernel needs to not have any exploitable local root vulnerabilities. For two, no suid executables available to the user can have local code execution vulnerabilities before they drop privileges. Let's try to do that. You might think it's easiest to just run a virtual machine. Feel free to do that. I am going to recommend a different method similar to Android's security model. There are other ways to run code without trusting it, but they trade privacy for security. If you have root access on your local system, you can create a new user. Let's name that user oooooo after the program we're going to be running, black. Shadow has a program called useradd which I'll be using. If you're familiar with adding users, I'll leave it up to you.

# Add the user to /etc/passwd /etc/shadow and /etc/group
sudo useradd oooooo
# Create a directory for the user.
sudo mkdir /home/oooooo
# Give oooooo all permissions but no one else.
sudo chown oooooo:root /home/oooooo
sudo chmod 700 /home/oooooo
# Create a password for the user.
sudo passwd oooooo

Now that you have a user that has no permissions to your data (I hope) and has no history, a malicious program running as that user must exploit a vulnerability in the system to gain a foothold. Of course after you logout, you'll have to run ps u -u oooooo to make sure that no programs have daemonized and are messing around.

Since we're using tools that have perfectly reasonable command-line interfaces for this tutorial, it's up to you whether you boot into X or not. I will be using sudo -s -u oooooo from my own terminal to run these commands because it's practically as safe as logging in as oooooo from the command-line.

After logging in for the first time, it's important to know what's going on. Let's figure out a few things. First let's find out if there are any files in our directory and then let's find out where we are.

# Search for files in the current directory.
find .
.
# List all files in the current directory.
ls -lha
total 8.0K
drwx------  2 oooooo root 4.0K Jan 31 21:18 .
drwxr-xr-x 26 root   root 4.0K Jan 31 21:18 ..
# Print the current working directory.
pwd
/home/oooooo

We're where we're supposed to be and there are no files. Excellent. If your system doesn't look the same, try to figure out why. Did your useradd program create files? Did it put you in a directory you didn't expect? Did you not execute all of the instructions (chmod and chown are very important). If it doesn't look the same, don't worry. Diversity is good, determinism depends on asking the same questions of the same system and getting the same answer.

Compiling foo2zjs

I assume you don't have the files from foo2zjs, so I'll show you how to get them from the command line, just like foo2zjs tells you to.

# Download the file over HTTP
wget -O foo2zjs.tar.gz http://foo2zjs.rkkda.com/foo2zjs.tar.gz

# FIXME: check the signature.

# Make sure the tarball doesn't try to put files where it shouldn't.
tar tf foo2zjs.tar.gz |grep -v '^foo2zjs/'

# Extract the file.
tar xf foo2zjs.tar.gz

# Change to the source directory.
cd foo2zjs

# Compile with 9 threads because I have 8 threads available and I'm impatient.
make -j9
#
cc -O2 -Wall    -c -o foo2zjs.o foo2zjs.c
cc -O2 -Wall    -c -o jbig.o jbig.c
cc -O2 -Wall    -c -o jbig_ar.o jbig_ar.c
cc -O2 -Wall    -c -o zjsdecode.o zjsdecode.c
cc -O2 -Wall     arm2hpdl.c   -o arm2hpdl
cc -O2 -Wall    -c -o foo2hp.o foo2hp.c
cc -O2 -Wall    -c -o foo2xqx.o foo2xqx.c
cc -O2 -Wall    -c -o xqxdecode.o xqxdecode.c
# Dependencies...
#
# ... OK!
#
cc -O2 -Wall    -c -o foo2lava.o foo2lava.c
cc -O2 -Wall    -c -o lavadecode.o lavadecode.c
cc -O2 -Wall    -c -o foo2qpdl.o foo2qpdl.c
cc -O2 -Wall    -c -o qpdldecode.o qpdldecode.c
cc -O2 -Wall    -c -o opldecode.o opldecode.c
cc -O2 -Wall    -c -o foo2oak.o foo2oak.c
cc -O2 -Wall    -c -o oakdecode.o oakdecode.c
cc -O2 -Wall    -c -o foo2slx.o foo2slx.c
cc -O2 -Wall    -c -o slxdecode.o slxdecode.c
cc -O2 -Wall    -c -o foo2hiperc.o foo2hiperc.c
cc -O2 -Wall    -c -o hipercdecode.o hipercdecode.c
cc -O2 -Wall    -c -o foo2hbpl2.o foo2hbpl2.c
cc -O2 -Wall    -c -o hbpldecode.o hbpldecode.c
cc -O2 -Wall    -c -o gipddecode.o gipddecode.c
cc -O2 -Wall  -I/usr/local/include -c command2foo2lava-pjl.c
cc -O2 -Wall     usb_printerid.c   -o usb_printerid
[ ! -f foo2zjs-wrapper ] || chmod +w foo2zjs-wrapper
sed < foo2zjs-wrapper.in > foo2zjs-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2zjs-wrapper && exit 1)
chmod 555 foo2zjs-wrapper
[ ! -f foo2oak-wrapper ] || chmod +w foo2oak-wrapper
[ ! -f foo2hp2600-wrapper ] || chmod +w foo2hp2600-wrapper
sed < foo2oak-wrapper.in > foo2oak-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2oak-wrapper && exit 1)
sed < foo2hp2600-wrapper.in > foo2hp2600-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2hp2600-wrapper && exit 1)
chmod 555 foo2oak-wrapper
[ ! -f foo2xqx-wrapper ] || chmod +w foo2xqx-wrapper
chmod 555 foo2hp2600-wrapper
[ ! -f foo2lava-wrapper ] || chmod +w foo2lava-wrapper
sed < foo2xqx-wrapper.in > foo2xqx-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2xqx-wrapper && exit 1)
sed < foo2lava-wrapper.in > foo2lava-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2lava-wrapper && exit 1)
chmod 555 foo2lava-wrapper
chmod 555 foo2xqx-wrapper
[ ! -f foo2qpdl-wrapper ] || chmod +w foo2qpdl-wrapper
[ ! -f foo2slx-wrapper ] || chmod +w foo2slx-wrapper
sed < foo2qpdl-wrapper.in > foo2qpdl-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2qpdl-wrapper && exit 1)
sed < foo2slx-wrapper.in > foo2slx-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2slx-wrapper && exit 1)
chmod 555 foo2qpdl-wrapper
[ ! -f foo2hiperc-wrapper ] || chmod +w foo2hiperc-wrapper
sed < foo2hiperc-wrapper.in > foo2hiperc-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2hiperc-wrapper && exit 1)
chmod 555 foo2slx-wrapper
chmod 555 foo2hiperc-wrapper
[ ! -f foo2hbpl2-wrapper ] || chmod +w foo2hbpl2-wrapper
cat foo2zjs-pstops.sh >foo2zjs-pstops 
sed < foo2hbpl2-wrapper.in > foo2hbpl2-wrapper \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f foo2hbpl2-wrapper && exit 1)
chmod 555 foo2hbpl2-wrapper
cat printer-profile.sh >printer-profile 
chmod a+x foo2zjs-pstops
chmod a+x printer-profile
[ ! -f getweb ] || chmod +w getweb
cd icc2ps; make all
sed < getweb.in > getweb \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e 's@^PREFIX=.*@PREFIX=/usr@' || (rm -f getweb && exit 1)
make[1]: Entering directory '/home/oooooo/foo2zjs/icc2ps'
chmod 555 getweb
cc -O3 icc2ps.c xgetopt.c cmscam97.c cmscnvrt.c cmserr.c cmsgamma.c cmsgmt.c cmsintrp.c cmsio1.c cmslut.c cmsmatsh.c cmsmtrx.c cmsnamed.c cmspack.c cmspcs.c cmsps2.c cmssamp.c cmswtpnt.c cmsxform.c cmsio0.c cmsvirt.c -lm -o foo2zjs-icc2ps
rm -f foo2zjs-wrapper.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2zjs-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2zjs-wrapper.1in | sed > foo2zjs-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2zjs.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2zjs.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2zjs.1in | sed > foo2zjs.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f zjsdecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime zjsdecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= zjsdecode.1in | sed > zjsdecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2oak-wrapper.1
rm -f foo2oak.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2oak.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2oak.1in | sed > foo2oak.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2oak-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2oak-wrapper.1in | sed > foo2oak-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f oakdecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime oakdecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= oakdecode.1in | sed > oakdecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w oakdecode.1
rm -f foo2hp2600-wrapper.1
chmod -w foo2oak.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2hp2600-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2hp2600-wrapper.1in | sed > foo2hp2600-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2hp.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2hp.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2hp.1in | sed > foo2hp.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2zjs-wrapper.1
chmod -w zjsdecode.1
rm -f foo2xqx-wrapper.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2xqx-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2xqx-wrapper.1in | sed > foo2xqx-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2xqx.1
chmod -w foo2oak-wrapper.1
rm -f xqxdecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime xqxdecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= xqxdecode.1in | sed > xqxdecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2zjs.1
chmod -w foo2hp.1
rm -f foo2lava-wrapper.1
rm -f foo2lava.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2lava.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2lava.1in | sed > foo2lava.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2xqx-wrapper.1
rm -f lavadecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime lavadecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= lavadecode.1in | sed > lavadecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2xqx.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2xqx.1in | sed > foo2xqx.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w xqxdecode.1
rm -f opldecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime opldecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= opldecode.1in | sed > opldecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2lava.1
rm -f foo2qpdl-wrapper.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2qpdl-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2qpdl-wrapper.1in | sed > foo2qpdl-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2lava-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2lava-wrapper.1in | sed > foo2lava-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2hp2600-wrapper.1
rm -f foo2qpdl.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2qpdl.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2qpdl.1in | sed > foo2qpdl.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w opldecode.1
rm -f qpdldecode.1
chmod -w foo2lava-wrapper.1
rm -f foo2slx-wrapper.1
chmod -w foo2qpdl-wrapper.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime qpdldecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= qpdldecode.1in | sed > qpdldecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2slx-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2slx-wrapper.1in | sed > foo2slx-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2slx.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2slx.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2slx.1in | sed > foo2slx.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w lavadecode.1
rm -f slxdecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime slxdecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= slxdecode.1in | sed > slxdecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2slx-wrapper.1
chmod -w foo2xqx.1
rm -f foo2hiperc-wrapper.1
rm -f foo2hiperc.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2hiperc.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2hiperc.1in | sed > foo2hiperc.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2hiperc-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2hiperc-wrapper.1in | sed > foo2hiperc-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f hipercdecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime hipercdecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= hipercdecode.1in | sed > hipercdecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2slx.1
rm -f foo2zjs-icc2ps.1
MODver=; \
../includer-man -v DEF1= foo2zjs-icc2ps.1in | sed > foo2zjs-icc2ps.1 \
    -e "s@\${URLOAK}@@" \
    -e "s@\${URLZJS}@@" \
    -e "s@\${URLHP}@@" \
    -e "s@\${URLXQX}@@" \
    -e "s@\${URLLAVA}@@" \
    -e "s@\${URLQPDL}@@" \
    -e "s@\${URLSLX}@@" \
    -e "s@\${URLHC}@@" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2qpdl.1
rm -f foo2hbpl2-wrapper.1
chmod -w foo2zjs-icc2ps.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2hbpl2-wrapper.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2hbpl2-wrapper.1in | sed > foo2hbpl2-wrapper.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2hbpl2.1
chmod -w slxdecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2hbpl2.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2hbpl2.1in | sed > foo2hbpl2.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w hipercdecode.1
rm -f hbpldecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime hbpldecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= hbpldecode.1in | sed > hbpldecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f gipddecode.1
chmod -w qpdldecode.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime gipddecode.1in`; \
MODver=0.0; \
./includer-man -v DEF1= gipddecode.1in | sed > gipddecode.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f foo2zjs-pstops.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime foo2zjs-pstops.1in`; \
MODver=0.0; \
./includer-man -v DEF1= foo2zjs-pstops.1in | sed > foo2zjs-pstops.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w foo2hiperc.1
rm -f arm2hpdl.1
rm -f usb_printerid.1
chmod -w foo2hiperc-wrapper.1
chmod -w foo2hbpl2-wrapper.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime usb_printerid.1in`; \
MODver=0.0; \
./includer-man -v DEF1= usb_printerid.1in | sed > usb_printerid.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
rm -f printer-profile.1
cd icc2ps; make man
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime arm2hpdl.1in`; \
MODver=0.0; \
./includer-man -v DEF1= arm2hpdl.1in | sed > arm2hpdl.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime printer-profile.1in`; \
MODver=0.0; \
./includer-man -v DEF1= printer-profile.1in | sed > printer-profile.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"
chmod -w hbpldecode.1
chmod -w foo2zjs-pstops.1
cd osx-hotplug; make man
chmod -w gipddecode.1
rm -f osx-hotplug/osx-hplj-hotplug.1
modtime() { date -d "1/1/1970 utc + `stat -t $1 | cut -f14 -d' '` seconds" "+%a %b %d %T %Y"; }; \
MODpage=`modtime osx-hotplug/osx-hplj-hotplug.1in`; \
MODver=0.0; \
./includer-man -v DEF1= osx-hotplug/osx-hplj-hotplug.1in | sed > osx-hotplug/osx-hplj-hotplug.1 \
    -e "s@\${URLOAK}@http://foo2oak.rkkda.com@" \
    -e "s@\${URLZJS}@http://foo2zjs.rkkda.com@" \
    -e "s@\${URLHP}@http://foo2hp.rkkda.com@" \
    -e "s@\${URLXQX}@http://foo2xqx.rkkda.com@" \
    -e "s@\${URLLAVA}@http://foo2lava.rkkda.com@" \
    -e "s@\${URLQPDL}@http://foo2qpdl.rkkda.com@" \
    -e "s@\${URLSLX}@http://foo2slx.rkkda.com@" \
    -e "s@\${URLHC}@http://foo2hiperc.rkkda.com@" \
    -e "s@\${URLHBPL}@http://foo2hbpl.rkkda.com@" \
    -e "s/\${MODpage}/$MODpage/" \
    -e "s/\${MODver}/$MODver/"

make[1]: Entering directory '/home/oooooo/foo2zjs/osx-hotplug'
chmod -w foo2hbpl2.1
It is possible that certain products which can be built using this
rm -f osx-hplj-hotplug.1
cc -O2 -Wall  -o foo2zjs foo2zjs.o jbig.o jbig_ar.o
software module might form inventions protected by patent rights in
MODver=; \
../includer-man -v DEF1= osx-hplj-hotplug.1in | sed > osx-hplj-hotplug.1 \
    -e "s@\${URLOAK}@@" \
    -e "s@\${URLZJS}@@" \
    -e "s@\${URLHP}@@" \
    -e "s@\${URLXQX}@@" \
    -e "s@\${URLLAVA}@@" \
    -e "s@\${URLQPDL}@@" \
    -e "s@\${URLSLX}@@" \
    -e "s@\${URLHC}@@" \
    -e "s/\${MODver}/$MODver/"
some countries (e.g., by patents about arithmetic coding algorithms
chmod -w printer-profile.1
owned by IBM and AT&T in the USA). Provision of this software by the
chmod -w usb_printerid.1
author does NOT include any licences for any patents. In those
make[1]: Entering directory '/home/oooooo/foo2zjs/icc2ps'
chmod -w osx-hotplug/osx-hplj-hotplug.1
countries where a patent licence is required for certain applications
chmod -w osx-hplj-hotplug.1
make[1]: Nothing to be done for 'man'.
make[1]: Leaving directory '/home/oooooo/foo2zjs/icc2ps'
cc -O2 -Wall  zjsdecode.o jbig.o jbig_ar.o -o zjsdecode
# cc -O2 -Wall  -o foo2hp foo2hp.o jbig.o jbig_ar.o /usr/local/lib/libdmalloc.a
of this software module, you will have to obtain such a licence
cc -O2 -Wall  -o foo2xqx foo2xqx.o jbig.o jbig_ar.o
make[1]: Leaving directory '/home/oooooo/foo2zjs/osx-hotplug'
cc -O2 -Wall  xqxdecode.o jbig.o jbig_ar.o -o xqxdecode
yourself.
cc -O2 -Wall  -o foo2lava foo2lava.o jbig.o jbig_ar.o
cc -O2 -Wall  -o foo2hp foo2hp.o jbig.o jbig_ar.o
cc -O2 -Wall  lavadecode.o jbig.o jbig_ar.o -o lavadecode
cc -O2 -Wall  -o foo2qpdl foo2qpdl.o jbig.o jbig_ar.o
chmod -w arm2hpdl.1
cc -O2 -Wall  qpdldecode.o jbig.o jbig_ar.o -o qpdldecode
cc -O2 -Wall  -g opldecode.o jbig.o jbig_ar.o -o opldecode
cc -O2 -Wall  -o foo2oak foo2oak.o jbig.o jbig_ar.o
cc -O2 -Wall  -g oakdecode.o jbig.o jbig_ar.o -o oakdecode
cc -O2 -Wall  -o foo2slx foo2slx.o jbig.o jbig_ar.o
cc -O2 -Wall  slxdecode.o jbig.o jbig_ar.o -o slxdecode
cc -O2 -Wall  -o foo2hiperc foo2hiperc.o jbig.o jbig_ar.o
cc -O2 -Wall  hipercdecode.o jbig.o jbig_ar.o -o hipercdecode
cc -O2 -Wall  -o foo2hbpl2 foo2hbpl2.o jbig.o jbig_ar.o
cc -O2 -Wall  hbpldecode.o jbig.o jbig_ar.o -o hbpldecode
cc -O2 -Wall  gipddecode.o jbig.o jbig_ar.o -o gipddecode
cc -O2 -Wall  -L/usr/local/lib command2foo2lava-pjl.o -lcups -o command2foo2lava-pjl
groff -t -man \
    `ls foo2zjs-wrapper.1 foo2zjs.1 zjsdecode.1 foo2oak-wrapper.1 foo2oak.1 oakdecode.1 foo2hp2600-wrapper.1 foo2hp.1 foo2xqx-wrapper.1 foo2xqx.1 xqxdecode.1 foo2lava-wrapper.1 foo2lava.1 lavadecode.1 opldecode.1 foo2qpdl-wrapper.1 foo2qpdl.1 qpdldecode.1 foo2slx-wrapper.1 foo2slx.1 slxdecode.1 foo2hiperc-wrapper.1 foo2hiperc.1 hipercdecode.1 foo2hbpl2-wrapper.1 foo2hbpl2.1 hbpldecode.1 gipddecode.1 foo2zjs-pstops.1 arm2hpdl.1 usb_printerid.1 printer-profile.1 \
        icc2ps/foo2zjs-icc2ps.1 \
        osx-hotplug/osx-hplj-hotplug.1 \
        | sort` \
    | ps2pdf - manual.pdf
make[1]: Leaving directory '/home/oooooo/foo2zjs/icc2ps'

If you got an error, you might be lacking one of the prerequisites for this software. You can see that it has quite a few and Ubuntu doesn't ship with a working compiler. In comparison to most software, foo2zjs has very few dependencies. Surprising how much text there is there, right? It's just a driver but this is one of the smaller programs in the world of software. It took seconds to build. If you have trouble compiling, try searching Google or your favorite web search engine for the error messages produced. Once you're done, you should have an "ELF 64-bit LSB executable" named usb_printerid. Now we have our executable. Let's rename it black.

# Find out what usb_printerid is.
file usb_printerid
usb_printerid: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped

# Copy our executable to black.
cp -i usb_printerid black

Strings

The first step to reverse engineering an ELF binary is using strings effectively. I previously used this tactic against Lume, a Flash game in my tutorial Reverse Engineering Flash Games. We're going to do similar things here but this time it's easier because there's very little signal and not a lot of noise to filter out.

# Look at all strings in the file black
strings -a black |less
/lib64/ld-linux-x86-64.so.2
libc.so.6
exit
puts
putchar
__errno_location
__fprintf_chk
stdout
stderr
ioctl
fwrite
close
open
__vfprintf_chk
strerror
__libc_start_main
__gmon_start__
GLIBC_2.3.4
GLIBC_2.2.5
ATUS
T$0H
L$8L
D$@L
L$Ht7
)D$P
)L$`
)T$p
[]A\
[]A\A]A^A_
Error: 
Warning: 
%s: 
can't open '%s'
GET_DEVICE_ID on '%s'
GET_DEVICE_ID string:
usage: usb_printerid /dev/usb/lp0
;*3$"
GCC: (Gentoo 4.8.3 p1.1, pie-0.5.9) 4.8.3
GCC: (Gentoo 4.8.4 p1.0, pie-0.6.1) 4.8.4
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
usb_printerid.c
elf-init.c
__init_array_end
_DYNAMIC
__init_array_start
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
putchar@@GLIBC_2.2.5
__vfprintf_chk@@GLIBC_2.3.4
__errno_location@@GLIBC_2.2.5
_ITM_deregisterTMCloneTable
stdout@@GLIBC_2.2.5
data_start
puts@@GLIBC_2.2.5
_edata
error
_fini
ioctl@@GLIBC_2.2.5
close@@GLIBC_2.2.5
__libc_start_main@@GLIBC_2.2.5
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
_end
_start
__bss_start
main
open@@GLIBC_2.2.5
_Jv_RegisterClasses
exit@@GLIBC_2.2.5
fwrite@@GLIBC_2.2.5
__TMC_END__
__fprintf_chk@@GLIBC_2.3.4
_ITM_registerTMCloneTable
strerror@@GLIBC_2.2.5
_init
stderr@@GLIBC_2.2.5

There are a lot so I won't explain it line for line. Instead you can look at a few of the ones that should catch your eye. exit, puts, ioctl, and so forth are system functions which are either part of libc or Linux which means that you'll find them used by many programs, big and small. "Error: ", "Warning: ", "%s: ", "can't open '%s'", "GET_DEVICE_ID on '%s'", "GET_DEVICE_ID string:", "usage: usb_printerid /dev/usb/lp0" are all of the strings in the program. If you peek at the source (don't) you'll see those exact strings in the source code. So now if you think for a moment, you'll say, If I want to know what strings a programmer typed into the source code, they are often found in strings. That is very true. Large programs will have so many that you'll want to search. Less has a regular expression searching function that I recommend that everyone learn. If you don't know regular expressions, searching for blah is fine but searching for blah( is not because parenthesis "(" is a delimiter in regex. Anyway, learn it by searching documents or by doing crossword. It's that important.

Disassembly with objdump

Now let's actually decompile the program. But before we do that we should talk about stripping, debugging symbols, and DWARF. DWARF is a debugging language built into executables to make it possible to debug them. It's so important that it would be a very different world without it. But most programs strip debugging symbols as well as other symbols because the additional file size is detrimental to performance and not worthwhile unless you're finding bugs. Recently people have wanted the best of both worlds so there's an idea of split debug (splitdebug in Gentoo) files which mean that you have a stripped executable and a debug file in /usr/lib/debug/ so that you can tell the maintainer when you find a bug. GDB supports the .debug file but binutils doesn't. You'll see why this matters in a second. First we're going to disassemble it with symbols using objdump. Objdump has been (and probably will remain) the disassembly tool with a ton of features which everyone has access to assuming they have a working binutils. The main problem with objdump is that it wasn't designed for reverse engineering. That's all right, each tool needs its own purpose and reverse engineering wasn't objdump's purpose.

# Disassemble black
objdump -d black >black.dis

Easy, huh? Open black.dis in your text editor. We're going to start hacking. If you don't have a favorite command-line text editor, try nano, jpico, joe, vim, or emacs. Most people like vim, but many hackers like Emacs. I like the GUI text editor Kate, but I'll be editing in Emacs for this tutorial so that I don't have to leave the command-line. I usually use jpico but this certainly calls for an editor that can syntax highlight assembly that's been disassembled from a binary.

# If you run vim on black.dis, use this command to 
# force Vim to use assembly syntax highlighting.
:set syntax=asm

# If you run emacs on black.dis, use this command to 
# force Emacs to use assembly syntax highlighting.
Meta-x asm-mode

Now that you're in your text editor with syntax highlighting, you should see "Disassembly of section .init:", "Disassembly of section .plt:", and some assembly. On the far left hand side of a line of assembly is the address followed by a colon. For example: "400700:" These are all in hex, so if you ever do math on two addresses, don't do decimal arithmetic, do hexadecimal arithmetic. Believe me, you'll be doing a bit of pointer math before you get too far. Let's skip to main where we know that the action starts. I won't teach you how to do search in your text editor. Look for a cheat sheet like this Emacs Cheat Sheet which I found useful (being a beginner in Emacs myself). When you're there, it should look like this:

0000000000400800 <main>:
  400800:       55                      push   %rbp
  400801:       48 89 f5                mov    %rsi,%rbp
  400804:       53                      push   %rbx
  400805:       48 81 ec 18 04 00 00    sub    $0x418,%rsp
  40080c:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400813:       00 00
  400815:       48 89 84 24 08 04 00    mov    %rax,0x408(%rsp)
  40081c:       00
  40081d:       31 c0                   xor    %eax,%eax
  40081f:       83 ff 02                cmp    $0x2,%edi

You'll eventually learn assembly if you don't already it's alright to start learning reverse engineering without being able to write your own shellcode. Remember that a C compiler turns a C program into assembly, so it's not rocket science or anything. It's just a bit of computer science and a few hundred thousand lines of C source code written by a bunch of people just like you. It's important to know how things are working and what hangups you'll face once you start challenging yourself. For one, this is AT&T syntax which is different than Intel syntax in several ways. Why do GAS, objdump, GCC, and GDB use AT&T syntax when the rest of the software world uses Intel (NASM, YASM, IDA, MASM, distorm)? Tradition and inertia. That is the way of many things. Read more on Wikipedia's X86 assembly language page. GNU tools have Intel syntax options if you want to produce Intel syntax instead. For example, objdump -d -M intel black > black_intel.dis

Function Calls

If you prefer, feel free to continue in Intel syntax. The syntax highlighting for Emacs doesn't highlight registers in Intel mode, but that's not a big deal. There's no reason to choose one or the other until other people are involved, so make your choice. I will continue in AT&T syntax. So are we going to start reverse engineering now? Let's see what we can do without knowing a lot of assembly. The words that are in angle brackets "<word>" are pretty easy to understand. You don't need to know any assembly to figure out what's going on in this program. Cool, right? First, we see "callq 4009d0 <error>" This means that the function error is being called at address 40082e. That means that there's some error handling going on. There's a libc function called error, but that doesn't mean that they're calling libc error. In fact, the fact that there isn't an @plt at the end of that means that it's not using shared libc. Because a function error is actually defined in this program, this call goes there instead of libc. The next call is "callq 4007b0 <open@plt>" This is clearly the function open from libc. It could be from another library, but no self-respecting library author would have a function named open. It would be a disgrace to programmers everywhere. The next function call is "callq 400770 <ioctl@plt>". This function is very important and is worthwhile to understand. First, read the manpage for ioctl, man 2 ioctl. Do you know what ioctl does now? If so, congrats you're learning system programming. If not, don't worry, ioctl is a function that needs explanation. In general Linux, BSD, and Unix-derived systems try to create interfaces based on files. This turned out not to work as well as we thought, so we don't do that for everything. We do it for everything that makes sense as a file. For example, graphics are handled through a file in /dev. You might think that they're handled in userland and that's mostly true, but when the graphics go from userland to kernel land, they do so through a file. Of course you can't just write complex graphics data to a file like you would a log file. Since there is a protocol which userland speaks to the kernel to get graphics out, ioctls are used extensively to pass data from a userland program to get it to the video card. The second argument to ioctl is the request which tells the kernel what you're doing. The rest of the arguments are just like you're calling a normal function. Say we had a function in our modified Linux kernel called kernel_hello_world(int x) that simply took the argument x and put it into kernel memory. We would create an ioctl for our file and when someone called ioctl with the correct file and the correct number, that value would be stored in kernel memory. Pretty nifty, right? That makes system programming a lot more like userland programming because you're just calling a function. In fact, many libraries written by smart people are just wrappers around ioctl with the proper amount of sanity checking. It turns out that black is a userland program that does exactly this. How cool is that? You didn't need to look at the source code or the description to understand that. You just needed to read a few lines of assembly and you didn't even need to learn what mov means yet. Shall we continue reverse engineering black? It's only a few more calls actually, so I'll just list them below.

  400873:       e8 e8 fe ff ff          callq  400760 <puts@plt>
  40088c:       e8 3f ff ff ff          callq  4007d0 <fwrite@plt>
  400896:       e8 95 fe ff ff          callq  400730 <putchar@plt>
  40089d:       e8 de fe ff ff          callq  400780 <close@plt>
  4008a4:       e8 17 ff ff ff          callq  4007c0 <exit@plt>
  4008b9:       e8 12 01 00 00          callq  4009d0 <error>
  4008d0:       e8 fb 00 00 00          callq  4009d0 <error>

Do you see what's going on just from the calls that are being made? If you don't know what puts is, it's the function that GCC compiles printf("Hello world\n") into. If you compile a hello world program, GCC has a built-in that optimizes your code. The function fwrite is often used when doing the same with strings that aren't null-terminated. Null termination is a huge deal in C programming, so learn it. The function putchar is puts but with a single int. The functions close and exit should be obvious and error is more error handling, probably when the above fails.

If you are a person who loves power and didn't know the above until now, you should be very happy. This is power quick. If you know how to use grep, you can actually do this without the whole syntax highlighting text editor. I wanted to get you familiar with working with a file because when a challenge comes up, you'll find yourself in your text editor for hours. Reverse engineering will convince you to write tools. If you end up writing those tools, I recommend that you share them because we'd all benefit from a better reverse-engineered world with your help.

Introducing JavRE

One of the things that we didn't touch on is the fact that these string functions actually take strings and programs generally store these statically so that they don't need to do computation to use them. In this program, there are 5 strings in main and two strings in error that objdump doesn't show you. It wasn't designed as a reverse engineering tool, so you either need to do the work yourself with a hex editor or you can use the tool I released, JavRE to do this. If you don't like JavRE, I recommend writing your own code. It's an amazing process learning how the software you use works and you can only become a better reverse engineer by doing it yourself. If you have a GUI running, you can run python gtk-javre.py black instead. It syntax highlights a few things that are useful for reverse engineering. Here are the lines that disasm1.py finds interesting strings at:

python disasm1.py black
...
  400824: MOV ESI, 0x400bd0 ; 'usage: usb_printerid /dev/usb/lp0\n'
  400867: MOV EDI, 0x400bb3 ; 'GET_DEVICE_ID string:'
  400891: MOV EDI, 0xa ; '\n'
  4008ad: MOV ESI, 0x400b8b ; "can't open '%s'\n"
  4008c4: MOV ESI, 0x400b9c ; "GET_DEVICE_ID on '%s'\n"
  400a2e: MOV EDX, 0x400b7c ; 'Warning: '
  400a33: MOV EAX, 0x400b74 ; 'Error: '

First, note that it is in Intel syntax. JavRE is based on distorm which only supports Intel syntax at this point, so there's no way but to try to work with it (until we replace it). Of course, most people will prefer Intel syntax because it's a standard amongst over half of reverse engineers. Second, note that one of the integers is not like the others. 0xa is not a valid address on any system I run, so what you're seeing there is an intelligent behavior by JavRE saying that when someone does a mov with a small number, it's possibly a character. In this case it is. Third, note that the strings are in python's repr format which many people won't like. It's the only way that I found to easily output the multitude of possible strings in a reasonable way from python, so that's why. To grep for this, simply grep for single quote or double quote.

# Disassemble black.
python disasm1.py black > black.jav

# Search for strings in the disassembly.
grep -e "'" -e '"' black.jav

Dynamic Reverse Engineering with strace

Now that we've reverse engineered the binary statically, we only know a little about what it does. If we suspected some sort of malicious behavior (ioctl is very suspicious by the way because it can be used to exploit a vulnerability in the kernel to get root). Of course lacking ioctl doesn't mean much because a skilled malware author can make their program look very innocent lacking any advanced functionality and then decrypt a payload and execute it. So how do we know what this program really does? Let's try strace first. strace is a dynamic debugging program which is extremely useful in reverse engineering. Unlike static reverse engineering, dynamic reverse engineering actually executes the untrusted program as if you were a normal user accidentally double clicking on it. But we aren't innocent users, we're reverse engineers. Follow below:

# Dynamically trace system calls with strace.
# This is not safe to do on a system with weaknesses or valuable data exposed.
strace ./black
execve("./black", ["./black"], [/* 23 vars */]) = 0
brk(0)                                  = 0x6b1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa2585ca000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=345294, ...}) = 0
mmap(NULL, 345294, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa258575000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\33\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1648128, ...}) = 0
mmap(NULL, 3754392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa258016000
mprotect(0x7fa2581a2000, 2093056, PROT_NONE) = 0
mmap(0x7fa2583a1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18b000) = 0x7fa2583a1000
mmap(0x7fa2583a7000, 14744, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa2583a7000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa258574000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa258573000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa258572000
arch_prctl(ARCH_SET_FS, 0x7fa258573700) = 0
mprotect(0x7fa2583a1000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7fa2585cb000, 4096, PROT_READ) = 0
munmap(0x7fa258575000, 345294)          = 0
write(2, "Error: ", 7Error: )                  = 7
write(2, "usage: usb_printerid /dev/usb/lp"..., 34usage: usb_printerid /dev/usb/lp0
) = 34
exit_group(1)                           = ?
+++ exited with 1 +++

If this is your first foray with strace, you don't know that only the two write functions are interesting. It turns out that if you don't give this program an argument, it calls error() with the usage like most Linux programs should. It wants /dev/usb/lp0. We aren't going to give it a real /dev/usb/lp0 because we don't have to. If we did, this would be the point where we break our USB printer out of storage. We don't. It just wants a file. So give it a file. Don't give it something valuable. Give a fake file like "blah".

# Create a file.
echo blah >blah.txt
# Run strace again.
strace ./black blah.txt
...
open("blah.txt", O_RDWR)                = 3
ioctl(3, SNDCTL_DSP_SYNC, 0x7fff90733090) = -1 ENOTTY (Inappropriate ioctl for device)
write(2, "Error: ", 7Error: )                  = 7
write(2, "Inappropriate ioctl for device: ", 32Inappropriate ioctl for device: ) = 32
write(2, "GET_DEVICE_ID on 'blah.txt'\n", 28GET_DEVICE_ID on 'blah.txt'
) = 28

This time I've replaced the unnecessary part of strace's output with ... so that we can focus on the important part. It opens the file read/write, then it uses ioctl with the the value SNDCTL_DSP_SYNC and a pointer of some type. The response is "Inappropriate ioctl for device" which makes sense because we did a printer ioctl on a normal text file. SNDCTL_DSP_SYNC is the incorrect name of the value, but we can find the correct value by looking that value up in /usr/include.

grep -r SNDCTL_DSP_SYNC /usr/include/
/usr/include/linux/soundcard.h:#define SNDCTL_DSP_SYNC                       _SIO  ('P', 1)

grep -r _SIO /usr/include/
...
/usr/include/linux/soundcard.h:#define  _SIO(x,y)       ((int)(SIOC_VOID|(x<<8)|y))
...

grep -r SIOC_VOID /usr/include/
/usr/include/linux/soundcard.h:#define  SIOC_VOID       IOC_VOID
/usr/include/linux/soundcard.h:#define  SIOC_VOID       0x00000000      /* no parameters */
...
grep -r IOC_VOID /usr/include/
...

This is why we need text editors that know C. So _SIO('P', 1) is actually ((int)(SIOC_VOID|(80<<8)|1)) which turns out to be 20481. 20481 is an interesting number because it's 2048*10 + 1. 2048 is a factor of 2 and a factor of 2 multiplied by a number plus 1 is interesting. This is the type of intuition you may start to have if you become a reverse engineer.

Dynamic Reverse Engineering with gdb

So the next step is to reverse engineer this with gdb. gdb might take you a bit of time to understand, but keep following. Unlike strace, gdb is a very complex program. Running gdb is pretty easy if your program segfaults or if you are the author and you know what's going on. The rest of the world requires some skill in gdb. I'll try to give you a few hints but there are many more that I can't explain.

gdb black
GNU gdb (Gentoo 7.8.1 vanilla) 7.8.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from black...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x400800
(gdb) run
Starting program: /home/oooooo/javre/black 
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?

Breakpoint 1, 0x0000000000400800 in main ()
(gdb) disassemble
Dump of assembler code for function main:
=> 0x0000000000400800 <+0>:     push   %rbp
   0x0000000000400801 <+1>:     mov    %rsi,%rbp
   0x0000000000400804 <+4>:     push   %rbx
   0x0000000000400805 <+5>:     sub    $0x418,%rsp
   0x000000000040080c <+12>:    mov    %fs:0x28,%rax
   0x0000000000400815 <+21>:    mov    %rax,0x408(%rsp)
   0x000000000040081d <+29>:    xor    %eax,%eax
   0x000000000040081f <+31>:    cmp    $0x2,%edi
   0x0000000000400822 <+34>:    je     0x400833 <main+51>
   0x0000000000400824 <+36>:    mov    $0x400bd0,%esi
   0x0000000000400829 <+41>:    mov    $0x1,%edi
   0x000000000040082e <+46>:    callq  0x4009d0 <error>
   0x0000000000400833 <+51>:    mov    0x8(%rbp),%rdi
   0x0000000000400837 <+55>:    xor    %eax,%eax
   0x0000000000400839 <+57>:    mov    $0x2,%esi
   0x000000000040083e <+62>:    callq  0x4007b0 <open@plt>
   0x0000000000400843 <+67>:    test   %eax,%eax
   0x0000000000400845 <+69>:    mov    %eax,%ebx
   0x0000000000400847 <+71>:    js     0x4008a9 <main+169>
   0x0000000000400849 <+73>:    xor    %eax,%eax
   0x000000000040084b <+75>:    mov    %rsp,%rdx
   0x000000000040084e <+78>:    mov    $0x84005001,%esi
   0x0000000000400853 <+83>:    mov    %ebx,%edi
   0x0000000000400855 <+85>:    callq  0x400770 <ioctl@plt>
   0x000000000040085a <+90>:    test   %eax,%eax
   0x000000000040085c <+92>:    js     0x4008c0 <main+192>
   0x000000000040085e <+94>:    movzbl (%rsp),%eax
   0x0000000000400862 <+98>:    movzbl 0x1(%rsp),%edx
   0x0000000000400867 <+103>:   mov    $0x400bb3,%edi
   0x000000000040086c <+108>:   shl    $0x8,%eax
   0x000000000040086f <+111>:   lea    -0x2(%rax,%rdx,1),%ebp
   0x0000000000400873 <+115>:   callq  0x400760 <puts@plt>
---Type <return> to continue, or q <return> to quit---
   0x0000000000400878 <+120>:   mov    0x200811(%rip),%rcx        # 0x601090 <stdout@@GLIBC_2.2.5>
   0x000000000040087f <+127>:   lea    0x2(%rsp),%rdi
   0x0000000000400884 <+132>:   movslq %ebp,%rdx
   0x0000000000400887 <+135>:   mov    $0x1,%esi
   0x000000000040088c <+140>:   callq  0x4007d0 <fwrite@plt>
   0x0000000000400891 <+145>:   mov    $0xa,%edi
   0x0000000000400896 <+150>:   callq  0x400730 <putchar@plt>
   0x000000000040089b <+155>:   mov    %ebx,%edi
   0x000000000040089d <+157>:   callq  0x400780 <close@plt>
   0x00000000004008a2 <+162>:   xor    %edi,%edi
   0x00000000004008a4 <+164>:   callq  0x4007c0 <exit@plt>
   0x00000000004008a9 <+169>:   mov    0x8(%rbp),%rdx
   0x00000000004008ad <+173>:   mov    $0x400b8b,%esi
   0x00000000004008b2 <+178>:   mov    $0x1,%edi
   0x00000000004008b7 <+183>:   xor    %eax,%eax
   0x00000000004008b9 <+185>:   callq  0x4009d0 <error>
   0x00000000004008be <+190>:   jmp    0x400849 <main+73>
   0x00000000004008c0 <+192>:   mov    0x8(%rbp),%rdx
   0x00000000004008c4 <+196>:   mov    $0x400b9c,%esi
   0x00000000004008c9 <+201>:   mov    $0x1,%edi
   0x00000000004008ce <+206>:   xor    %eax,%eax
   0x00000000004008d0 <+208>:   callq  0x4009d0 <error>
   0x00000000004008d5 <+213>:   jmp    0x40085e <main+94>
End of assembler dump.
(gdb) c
Continuing.
Error: usage: usb_printerid /dev/usb/lp0
[Inferior 1 (process 18186) exited with code 01]

First, we set a breakpoint with break. If you have symbols, you can use names (yay) but if you're reverse engineering a program that has been stripped, you won't be able to (argh). More on that later. The command run will run your program. When gdb hits a breakpoint, it'll stop the program's execution before executing the instruction and give you control. You can do a lot of stuff. If you use the command c to continue, it will run until it hits a breakpoint. Since we broke at main, it continued until it exited. Common breakpoints people use are read, open, socket, bind, and so forth. Remember that interrupting a program can cause it to malfunction, so cross your fingers that it works. Next we'll step until we get to the open and then examine some variables.

(gdb) run blah.txt 
Starting program: /home/oooooo/javre/black blah.txt
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?

Breakpoint 1, 0x0000000000400800 in main ()
(gdb) stepi
0x0000000000400801 in main ()
(gdb) 
0x0000000000400804 in main ()
(gdb) 
0x0000000000400805 in main ()
(gdb) 
0x000000000040080c in main ()
(gdb) 
0x0000000000400815 in main ()
(gdb) 
0x000000000040081d in main ()
(gdb) 
0x000000000040081f in main ()
(gdb) 
0x0000000000400822 in main ()
(gdb) 
0x0000000000400833 in main ()
(gdb) 
0x0000000000400837 in main ()
(gdb) 
0x0000000000400839 in main ()
(gdb) 
0x000000000040083e in main ()
(gdb) 
0x00000000004007b0 in open@plt ()
# Print the registers.
(gdb) i r
rax            0x0      0
rbx            0x0      0
rcx            0x0      0
rdx            0x7fffffffe5c0   140737488348608
rsi            0x2      2
rdi            0x7fffffffe7e9   140737488349161
rbp            0x7fffffffe5a8   0x7fffffffe5a8
rsp            0x7fffffffe098   0x7fffffffe098
r8             0x7ffff7dd7c80   140737351875712
r9             0x7ffff7deb0c0   140737351954624
r10            0x0      0
r11            0x7ffff7a689b0   140737348274608
r12            0x4008d7 4196567
r13            0x7fffffffe5a0   140737488348576
r14            0x0      0
r15            0x0      0
rip            0x4007b0 0x4007b0 <open@plt>
eflags         0x246    [ PF ZF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

Now we have a list of register values. These numbers don't look useful do they? Well, let's find out if they are strings.

(gdb) x/s $rdi
0x7fffffffe7e9: "blah.txt"

I knew that functions in Linux put the first argument in in rdi, so I printed the string which is the filename that open is supposed to open. The second argument to open is in rsi and it's an integer.

(gdb) bt
#0  0x00000000004007b0 in open@plt ()
#1  0x0000000000400843 in main ()
(gdb) x/10gx $rsp
0x7fffffffe098: 0x0000000000400843      0x0000000000000003
0x7fffffffe0a8: 0x00007ffff7de5037      0x00000000000008a2
0x7fffffffe0b8: 0x00007ffff7a57d88      0x00007ffff7ff7658
0x7fffffffe0c8: 0x00007fffffffe148      0x00007fffffffe144
0x7fffffffe0d8: 0x00007fffffffe290      0x00007fff00000007

The command bt is important because it's a stack trace. If your program crashes, programmers want to know your stack trace. They should also want your registers and the instruction it crashed on as well as a few values in memory. If nothing else, the stack tells you where to look. x/10gx $rsp is a very interesting command. It takes 10 64-bit values from the stack (the register rsp) and prints them in a way that you can easily read, hexadecimal. The first one there is equal to the second address in bt. That isn't a coincidence, when you call a function, the place where it will end up after returning is held on the stack. If you have heard about esp, rsp is the 64-bit equivalent in the same way that rax is the 64-bit equivalent of eax.

You'll learn about the stack elsewhere (probably an stack-based buffer overflow exploitation tutorial) but that's all the reverse engineering we're going to do in gdb for now. The more you learn about gdb, the better dynamic reverse engineer you'll be. One of the cool benefits of gdb is that if you understand how to use it, you can do dynamic reverse engineering without actually giving full code execution to the untrusted program. For example, by stepping through the first few operations and stopping when it comes into contact with something, we can prevent the code from doing anything malicious. That's assuming that you understand what syscall does and what libc functions do.

Stripped Executables

Now that we're done with gdb, let's strip the symbols and see what happens. So far we've been playing around with easy binaries. We're about to get one step more difficult.

# Strip black, creating black_release without symbols.
strip -s -o black_release black
# Make sure it's the right file size.
ls -lh black_release
-rwxr-xr-x 1 oooooo oooooo 6.2K Feb  1 00:16 black_release
# Disassemble black_release
objdump -d black_release >black_release.dis

Same thing as above, no big deal. Something went wrong. It turns out that error actually wasn't stripped for some reason, so we see that. Usually all functions are stripped. It seems like this is caused by the unusual name. If we rename it to error1, the symbol goes away. Thus strip leaves a name error if it finds it. It doesn't matter. Do a diff of the two to understand what's going on when you strip symbols.

diff -u black.dis black_release.dis |less
--- black.dis   2015-02-01 00:37:08.580752643 -0800
+++ black_release.dis   2015-02-01 00:18:28.590783261 -0800
...
-0000000000400800 <main>:
+0000000000400800 <error-0x1d0>:
...
-  400847:      78 60                   js     4008a9 <main+0xa9>
+  400847:      78 60                   js     4008a9 <strerror@plt+0xb9>
...
-  4008d5:      eb 87                   jmp    40085e <main+0x5e>
-
-00000000004008d7 <_start>:
+  4008d5:      eb 87                   jmp    40085e <strerror@plt+0x6e>

As you can see, the name main is removed and replaced with error-0x1d0 or sterror@plt+0x10. This makes the program more difficult to reverse engineer. If we had 20 functions, all functions would be together in one big mess. That's because objdump is not a reverse engineering tool. It took me minutes to implement function finding in JavRE and would only take an hour or two to add it to objdump. But that's why we have JavRE. Let's see what JavRE finds.

# Disassemble black stripped of symbols
python disasm1.py black_release >black_release.jav
less black_release.jav
...
.text:
  400800: PUSH RBP
  400801: MOV RBP, RSI
  400804: PUSH RBX
  400805: SUB RSP, 0x418
  40080c: MOV RAX, [FS:0x28]
  400815: MOV [RSP+0x408], RAX
  40081d: XOR EAX, EAX
  40081f: CMP EDI, 0x2 ;
  400822: JZ 0x400833 ; if(EDI != 0x2) {
  400824: MOV ESI, 0x400bd0 ; 'usage: usb_printerid /dev/usb/lp0\n'
  400829: MOV EDI, 0x1 ; '\x01'
  40082e: CALL 4009d0 <error>
; }
  400833: MOV RDI, [RBP+0x8]
  400837: XOR EAX, EAX
  400839: MOV ESI, 0x2 ; '\x02'
  40083e: CALL 4007b0 <open>
  400843: TEST EAX, EAX ;
  400845: MOV EBX, EAX
  400847: JS 0x4008a9 ; if(EAX >= 0) {
  400849: XOR EAX, EAX
  40084b: MOV RDX, RSP
  40084e: MOV ESI, 0xffffffff84005001
  400853: MOV EDI, EBX

One obvious thing we didn't discuss in the first run in with disasm1.py is that it correctly decompiles two if statements. If statements are currently buggy, but in this case it's able to tell you if edi (which is argc in Linux main functions) is not 2, it will call error("usage: ..."). The next thing is that it doesn't detect main. The reason JavRE doesn't detect main is that JavRE detects functions when those functions are called. In Linux, main isn't called directly in the body of a dynamically linked program. Libc does that and the address to main is put into rdi as the first argument of __libc_start_main. If you search for 400800, you can see it happening right before the call to __libc_start_main: "4008f4: MOV RDI, 0x400800".

Dynamic Reverse Engineering with ltrace

I want to teach you two more things before I let you go. If you want to go play, go for it. You can always come back. ltrace is an incredible program that is invaluable to reverse engineering. You can see it in action below. It is capable of intercepting shared library calls in a different way than strace intercepts system calls.

ltrace ./black blah.txt 
__libc_start_main(0x400800, 2, 0x7fff1ecf35f8, 0x400af0 <unfinished ...>
open("blah.txt", 2, 03663633020)                                    = 3
ioctl(3, -2080354303, 0x7fff1ecf30f0)                               = -1
__fprintf_chk(0x7f94e758d080, 1, 0x400b74, -112Error: )                    = 7
__errno_location()                                                  = 0x7f94e775a690
strerror(25)                                                        = "Inappropriate ioctl for device"
__fprintf_chk(0x7f94e758d080, 1, 0x400b86, 0x7f94e735d1a8Inappropriate ioctl for device: )          = 32
__vfprintf_chk(0x7f94e758d080, 1, 0x400b9c, 0x7fff1ecf3008GET_DEVICE_ID on 'blah.txt'
)         = 28
exit(1 <no return ...>
+++ exited (status 1) +++

When you're trying to understand something that makes heavy use of shared libraries, ltrace will make your day.

Dynamic Reverse Engineering with gdb Without Symbols

The second thing I want to teach you is how to deal with gdb when you don't have symbols. Back to gdb, this time with black_release.

gdb black_release
GNU gdb (Gentoo 7.8.1 vanilla) 7.8.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from black_release...(no debugging symbols found)...done.
(gdb) break main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n
(gdb) info file
Symbols from "/home/oooooo/javre/black_release".
Local exec file:
        `/home/oooooo/javre/black_release', file type elf64-x86-64.
        Entry point: 0x4008d7
...
(gdb) break *0x4008d7
Breakpoint 1 at 0x4008d7
(gdb) run
Starting program: /home/oooooo/javre/black_release 
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?

Breakpoint 1, 0x00000000004008d7 in ?? ()
(gdb) disassemble 
No function contains program counter for selected frame.
(gdb) disassemble 0x00000000004008d7, 0x00000000004008ff
Dump of assembler code from 0x4008d7 to 0x4008ff:
=> 0x00000000004008d7:  xor    %ebp,%ebp
   0x00000000004008d9:  mov    %rdx,%r9
   0x00000000004008dc:  pop    %rsi
   0x00000000004008dd:  mov    %rsp,%rdx
   0x00000000004008e0:  and    $0xfffffffffffffff0,%rsp
   0x00000000004008e4:  push   %rax
   0x00000000004008e5:  push   %rsp
   0x00000000004008e6:  mov    $0x400b60,%r8
   0x00000000004008ed:  mov    $0x400af0,%rcx
   0x00000000004008f4:  mov    $0x400800,%rdi
   0x00000000004008fb:  callq  0x400790 <__libc_start_main@plt>
End of assembler dump.

You can see that break main failed in a strange way. That's how gdb handles missing symbols. To find the entry point, we can use info file. That gives you a correct start so that you don't get ambushed by a clever piece of malware. Practice not executing stripped executables a few dozen times before you run malware on your computer. Don't be that reverse engineer who infected their work machine with malware. Oh, by the way, once you've found main's address there's no reason to step through __libc_start_main, it's part of your system not the target, so find the first argument to __libc_start_main, which is found in %rdi. The address there is main. If you use stepi all the way to the call, you can use "i r" to find the value of the rdi register. In this case it's very easy because they do a mov right before the call to main and no jumps or conditional logic.

Conclusion

Before we go I'd like to say that there's a ton more to reverse engineering than I let on here but it's just a matter of scaling your efforts from this simple 8kB executable to nVidia's massive graphics driver, your computer's BIOS, or Adobe Flash. Reverse engineering is slow, so when working on something as big as a graphics driver or Adobe Flash, you'll want to automate anything possible and completely remove any part of the executable that isn't interesting. Most of the interesting stuff is centered around one piece of the system. Once you reverse a very small piece, you are able to guess the rest. I recommend static reverse engineering, dynamic reverse engineering, and of course automation of the entire process. Anything you can do to make your process more streamlined will help you do more easier.

And now for the gem of the project, direct from foo2zjs's website:

*** DON'T USE the foo2zjs package from:
     Ubuntu, SUSE, Mandrake/Manrivia, Debian, RedHat, Fedora, Gentoo, Xandros, EEE PC, Linpus, MacOSX, or BSD!
*** Download it here and follow the directions below.

Why does foo2zjs want you to install from their website rather than through the distributions? Perhaps printer support is not a very fun job and the author doesn't like the idea of having to tell people to go to their distro for a fix. Practically the rest of the software community has accepted the reality of distros packaging software but Rick Richardson is the last holdout. It seems to be effective, too because his software works like gangbusters and supports a huge number of printers. The drawback of course is that my printer has worked for years and I haven't upgraded it since my last reinstall so all of my binaries are a year old. Who knows what bugs lie in my version of foo2zjs?

If you'd like to contribute to JavRE send e-mail to me, Javantea preferrably encrypted and signed with PGP. All code for JavRE will be signed with my key to ensure that if people trust me, they can run JavRE on systems they care about without worry.

End of Transmission.

Permalink

Comments: 0

Leave a reply »

 
  • Leave a Reply
    Your gravatar
    Your Name