Reverse Engineering usb_printerid with Source
by JavanteaJan 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:
Ubuntu, SUSE, Mandrake/Manrivia, Debian, RedHat, Fedora, Gentoo, Xandros, EEE PC, Linpus, MacOSX, or BSD! |
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-
Leave a Reply
Comments: 0
Leave a reply »