I recently spent a few days reverse engin­eer­ing an Install Jam­mer gen­er­ated bin­ary installer, spe­cific­ally the LPCX­presso installer sup­plied by NXP. The goal was to try and install the pro­gram without run­ning the bin­ary installer as root. I man­aged to cre­ate a perl script which unpacks the install files into a local directory.


One of the first things I noticed when examin­ing the installer was a UPX header

00000070: 0010 0000 ea2d 27a5 5550 5821 e811 0d0c  .....-'.UPX!....

I hadn’t played with UPX before but it is a sys­tem to com­press execut­able files. There are two parts, a pro­gram which com­presses the execut­able and a decom­pres­sion pro­gram which gets pre­pen­ded to the com­pressed file.

When the execut­able is run it uncom­presses the pay­load and restarts the exe­cu­tion at the start of the new executable.

UPX is an open source pro­ject with some nice tools. Spe­cific­ally they provide a pro­gram which can read the UPX head­ers and provide inform­a­tion and decom­press the bin­ary. They strongly advoc­ate not mess­ing things up so that these tools can function.

Unfor­tu­nately all the lead­ing google res­ults, stack over­flow entries and forum quer­ies are centered around pre­vent­ing people from uncom­press­ing the bin­ary. Given the way UPX works it is easy to slightly modify the decom­pil­a­tion and com­pil­a­tion pro­cess in a way that causes incom­pat­ib­il­ity. UPX also makes a spe­cial effort to allow GDB to work, which is easy to sab­ot­age. These things con­trib­ute to make UPX very pop­u­lar with virus writers as a mask­ing element.

Nat­ur­ally Install Jam­mer did all of this. I extrac­ted the UPX header by hand but it refers to a com­pres­sion scheme which doesn’t exist in the ori­ginal pro­gram. The sec­tions and sec­tion head­ers that UPX uses are miss­ing or masked, a com­monly recom­men­ded tech­nique to pre­vent decom­pres­sion. Attempt­ing to run using GDB didn’t provide any use­ful information.

It should be pos­sible to extract the assem­bler instruc­tions and fig­ure out or run the decom­pres­sion routine. How­ever that was bey­ond me and I found an easier approach.

Install Jam­mer Extractor

The Install Jam­mer pro­gram which gen­er­ates the final install bin­ar­ies comes with bin­ary blobs that are pre­pen­ded to the final installer.

This pre­com­piled pro­gram looks at the rest of the file and extracts from it the install files. Look­ing at the strings there are what looks like file names in the install blob.

I sim­pli­fied the prob­lem by cre­at­ing an Install Jam­mer installer of my own con­tain­ing a small col­lec­tion of scripts.

Inside the gen­er­ated bin­ary is a sec­tion with the fol­low­ing lines (there are actu­ally two, identical sec­tions… no idea why):

0015af60: 0000 0000 0000 0000 0000 0000 0046 494c  .............FIL
0015af70: 455a 4c30 3637 3239 3039 412d 3946 3236  EZL0672909A-9F26
0015af80: 2d33 4539 312d 4242 4546 2d30 3241 3230  -3E91-BBEF-02A20
0015af90: 3633 3238 3639 3200 0000 0000 0000 0000  6328692.........
0015afa0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0015afb0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0015afc0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0015afd0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0015afe0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0015aff0: 0000 0034 3431 0000 0000 0000 0000 0032  ...441.........2
0015b000: 3632 0000 0000 0000 0000 0031 3437 3337  62.........14737
0015b010: 3432 3339 3400 0000 0031 3137 3830 3031  42394....1178001

It looks like a file­name and sev­eral num­bers encoded as strings, I found the file­name por­tion in one of the inter­me­di­ate files gen­er­ated in the installer gen­er­a­tion, Linux-x86-files.tcl, this allows much of the detail to be iden­ti­fied. The com­pressed address and size refer to the pos­i­tion and size of a blob within the install bin­ary, this was con­firmed by sequen­cing mul­tiple adja­cent entries.

File ::0672909A-9F26-3E91-BBEF-02A206328692 -name compiles.t -parent 81FF3CF4-D2FD-4649-FA7F-C2640F59BE65 -directory <%InstallDir%>/t -size 441 -mtime 1473742394 -permissions 00644 -filemethod 0
FILE start marker
ZL flag
0672909A-9F26-3E91-BBEF-02A206328692 id string
441 extrac­ted size
262 com­pressed size
1473742394 mtime
1178001 blob address (#11F96F)

ZLib files

I extrac­ted the com­pressed blob and grabbed the match­ing uncom­pressed file. I tried sev­eral dif­fer­ent com­pres­sion tech­niques on the uncom­pressed file and tried match­ing them to the extrac­ted blob. Zlib, attemp­ted due to the ZL flag, was a very close match. Below is an example very small file.:

> zlib-flate -compress < original_file | xxd
00000000: 789c 2b4a 4db3 5228 4a4d 2bd6 2f4a cdcd  x.+JM.R(JM+./J..
00000010: 2f49 2dd6 cf2f ca4c cfcc d3cf 4d2c 2e49  /I-../.L....M,.I
00000020: 2de2 0200 c596 0bf2                      -.......

Extrac­ted blob, lined up to match:

00000000: 0000 2b4a 4db3 5228 4a4d 2bd6 2f4a cdcd  ..+JM.R(JM+./J..
00000010: 2f49 2dd6 cf2f ca4c cfcc d3cf 4d2c 2e49  /I-../.L....M,.I
00000020: 2de2 0200                                -...

The ZLib header and footer are both miss­ing. The header sets the com­pres­sion method and options such as the dic­tion­ary to use. Adding the stand­ard header bytes allowed the extrac­ted blob to uncom­pressed using zlib-flate –uncom­press. The four byte footer is a check­sum which seems to be optional.

This tech­nique allowed all the install files to be extrac­ted how­ever their names and struc­ture of the dir­ect­ory tree were lost.

LZMA files

Along with the ZLib com­pressed install files are a bunch of tcl files with an LZ flag. These have full names and seem to be the files neces­sary to run the installer, includ­ing files for tcl and the neces­sary libraries.

The tcl files are not from my sys­tem, some of them have dif­fer­ent ver­sions or do not exist at all. I chose iso8859-3.enc to exam­ine, assum­ing that it was likely to be the same as my version.

I assumed the encod­ing used was LZMA (Lem­pel – Ziv – Markov chain algorithm) par­tially because I had noticed a bin­ary lib­rary called craplzma in the Install Jam­mer applic­a­tion files. Unfor­tu­nately LZMA is, like the name sug­gests, an algorithm which is used by mul­tiple dif­fer­ent archivers such as 7-Zip, LZip, XZ and more. Most of the archive con­tain­ers spe­cify how to store mul­tiple files but for a single file it turns out you can just tack the appro­pri­ate header on and any pro­gram will extract it.

The header that matched most closely was LZMA alone or LZMA1. Which is con­veni­ently sup­por­ted by the Perl Compress::Raw::Lzma module. :

cat /usr/share/tcltk/tcl8.5/encoding/iso8859-3.enc | lzma -z | xxd
00000000: 5d00 0080 00ff ffff ffff ffff ff00 1188  ]...............
00000010: 0528 b979 d70b 91f8 28ae b6ac 59fc 1cbb  .(.y....(...Y...

Extrac­ted blob, first line:

5d00 0080 0000 1188 0528 b979 d70b 91f8

The LZMA file format defined a header:

  • 2 bytes properties
  • 4 bytes dic­tion­ary size
  • 8 bytes uncom­pressed size

Our extrac­ted blob is miss­ing the uncom­pressed size field. For­tu­nately passing a size of FFFF FFFF to the decom­pres­sion routine indic­ates an unknown size, spli­cing this field in allowed all the LZ flagged files to be extracted.

TCL scripts

Install Jam­mer is largely a TCL pro­ject, I believe it is a C++ base which uses TCL to per­form the GUI tasks, allow script­able exten­sion and do most of the work.

The inter­me­di­ate files cre­ated by the installer build pro­cess include a bunch of TCL gen­er­ated scripts, these scripts rename the extrac­ted files from their stored ID names to the final name. They also cre­ate the dir­ect­or­ies, sym­links if required and set the mtime for the files. It looks like the script is meant to set the per­mis­sions for the files but this doesn’t actu­ally work, everth­ing is set to 777, there is no facil­ity to set the ownership.

Extract­ing the files from the installer this script can be found in main2.tcl for my gen­er­ated file or main.tcl for the lpcx­presso installer. I ended up just pro­cessing every root dir­ect­ory tcl script to be safe.

The tcl script con­tains lines like the fol­low­ing which are fairly simple to parse. By com­bin­ing these lines with the entry table extrac­ted from the installer bin­ary each file can be extrac­ted, decom­pressed and placed in the appro­pri­ate location.

File ::4D49D586-0ADF-966C-3FC4-8DB31B47B741 -name dumpio2curl -parent 81FF3CF4-D2FD-4649-FA7F-C2640F59BE65 -directory <%InstallDir%> -type dir -permissions 040755 -filemethod 0
File ::381BB57B-2E9F-3012-F9BB-C1752B423A6E -name .travis.yml -parent 81FF3CF4-D2FD-4649-FA7F-C2640F59BE65 -directory <%InstallDir%> -size 164 -mtime 1473742394 -permissions 00644 -filemethod 0

The last step was to parse the tcl script for info vari­able block. This gives the vari­ables such as InstallDir which are embed­ded in the File entry. Sev­eral of these vari­ables would typ­ic­ally be set by the install wiz­ard, we sup­port this by allow­ing the user to pass val­ues on the com­mand line, either to cus­tom­ise the install or provide vari­ables which are missing.