File Merge

Tigress is a whole-program transformer. Essentially, this means that Tigress accepts a single C file as input. This is so that we can do whole-program analysis and transformations. For example, the EncodeData transformation has to perform a whole program alias analysis to avoid generating the wrong code, and the Inline transformation -- of course -- needs the code of a function in order to expand it inline.

What this is that, for typical programs that consist of multiple C files, before we can actually perform any Tigress transormations, we must first merge the files together.

There are many ways to merge a program. If you're writing your program from scratch and know you will be obfuscating it, it makes sense to write it in such a way that merging becomes easy. This has other advantages such as improving the compiler's ability to optimize. It's not hard to do this, simply make sure that all global identifiers are unique! For example, if you have a C module foo.c/.h you should give every C object (functions, variiables, structs, enums, etc.) the prefix foo_. Then merging might be no harder than this:

> cat *.h *.c > merged.c

Merging C programs is a fairly common problem and many people have tried to write their own automatic mergers. Searching for "amalgamate" on github yields several hits:

A number of projects have found that advantageous to distribute their code as a single merged file rather than a large collection of .c/.h files. Examples include:

The Tigress Merger

Tigress comes with its own merger. If your project has multiple files you'd call the merger before applying any obfuscating transformations. This will merge together all the .h files that your program depends on, including all the system .h files. Note that this implies that the resulting merged file is no longer portable.

Earlier versions of Tigress (prior to Version 4) had a separate merge program, called tigress-merge. This is no longer needed.

For more complex merging scenarios, see the CIL information page about merging.;

Case Study 1

Here's a simple case where we're just merging two files together, to prepare for subsequent Tigress transformations:

> cat file1.c
struct foo; 
extern struct foo *global;

> cat file2.c
struct bar {
 int x;
 struct bar *next;
};

extern struct bar *global;
struct foo {
 int y;
};

extern struct foo another;
int main() {
}

> tigress file1.c file2.c --out=full.c

> gcc full.c

Case Study 2

Here's a slightly more complex case:

> cat f1.h
struct f1_struct {int a;};
void f1_func(struct f1_struct x);

> cat f1.c
void f1_func(struct f1_struct x) {
  x.a = 10;
}

> cat f2.h
struct f2_struct {int a;};
void f2_func(struct f2_struct x);

> cat f2.c
void f2_func(struct f2_struct x) {
  x.a = 10;
}

> cat m.c

int main() {
   struct f1_struct x;
   struct f2_struct y;
   f1_func(x);
   f2_func(y);
}

Let's merge the files together into full.c:

> tigress f1.c f2.c m.c --out=full.c
> gcc full.c

And now we can obfuscate the merged file using Tigress' Merge transform (which merges functions, not files, of course!):

> tigress --Transform=Merge \
             --Functions=f1_func,f2_func \
             --MergeName=merged \
          --out=obf.c full.c
> gcc obf.c

And this is what the resulting program looks like:

void merged(void *tigressRetVal , struct f1_struct x__0 , struct f2_struct x__1 ,
            int whichBlock__2 ) { 
  if (whichBlock__2 == 0) {
    x__0.a = 10;
    return;
  } else
  if (whichBlock__2 == 1) {
    x__1.a = 10;
    return;
  } else {
  }
}

int main( ) { 
  struct f1_struct x ;
  struct f2_struct y ;

  merged(0, x, y, 0);
  merged(0, x, y, 1);
}

Case Study 3: Miniweb

Miniweb is a small web server.

To get the Tigress merger to work, we have to fix a few things. This is pretty common when you merge a program that was not designed to be merged!

1

First you may have to add tigress.h to the top of every file.

2

Then, let's get rid of a bug:

> gsed -i 's/mwBuildHttpHeader/mwBuildHttpHeaderXXX/' httphandler.c

Now we can call Tigress to merge, and then obfuscate:

> tigress --out=miniweb-merge.c \
    httppil.c \
    http.c \
    httpxml.c \
    httphandler.c \
    httppost.c \
    httpauth.c \
    miniweb.c 
> gcc -o miniweb-merge.exe miniweb-merge.c

> tigress \
    --Transform=Virtualize \
      --Functions=mwInitParam \
    --out=miniweb-merge-obf.c miniweb-merge.c
> gcc miniweb-merge-obf.c -o  miniweb-merge-obf.exe

Case Study 4: SQLite

SQLite is an example of a project that contains its own source code merge routine (sqlite/tool/mksqlite3c.tcl). If you want to obfuscate SQLite you should definitely use their own merged sources, rather than merge with the Tigress! However, purely as an exercise, we can merge the raw SQLite sources using Tigress.

As usual, some initial fixes are necessary: remove the two files src/sqlite3recover.h and src/geopoly.c. Then go ahead and merge:

tigress \
   -D__USE_XOPEN2K -D__USE_GNU -D_GNU_SOURCE \
   -DSQLITE_ENABLE_MATH_FUNCTIONS \
   -DSQLITE_THREADSAFE=1   \
   -DNDEBUG \
   -D_HAVE_SQLITE_CONFIG_H \
   -DBUILD_sqlite \
   -I./src \
   -I/usr/local/include \
   -DSQLITE_HAVE_ZLIB=1 \
   -DSQLITE_DQS=0 \
   -DSQLITE_ENABLE_FTS4 \
   -DSQLITE_ENABLE_RTREE \
   -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
   -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
   -DSQLITE_ENABLE_STMTVTAB \
   -DSQLITE_ENABLE_DBPAGE_VTAB \
   -DSQLITE_ENABLE_DBSTAT_VTAB \
   -DSQLITE_ENABLE_BYTECODE_VTAB \
   -DSQLITE_ENABLE_OFFSET_SQL_FUNC \
   -DSQLITE_STRICT_SUBTYPE=1 \
   src/*.c \
   --out=merged.c

Case Study 5: DoomGeneric

DoomGeneric is one of the many versions of the Doom game. Here is how we merge the sources:

> tigress  \
      dummy.c am_map.c doomdef.c doomstat.c dstrings.c d_event.c d_items.c \
      d_iwad.c d_loop.c d_main.c d_mode.c d_net.c f_finale.c f_wipe.c g_game.c \
      hu_lib.c hu_stuff.c info.c i_cdmus.c i_endoom.c i_joystick.c i_scale.c \
      i_sound.c i_system.c i_timer.c memio.c m_argv.c m_bbox.c m_cheat.c \
      m_config.c m_controls.c m_fixed.c m_menu.c m_misc.c m_random.c p_ceilng.c \
      p_doors.c p_enemy.c p_floor.c p_inter.c p_lights.c p_map.c p_maputl.c \
      p_mobj.c p_plats.c p_pspr.c p_saveg.c p_setup.c p_sight.c p_spec.c \
      p_switch.c p_telept.c p_tick.c p_user.c r_bsp.c r_data.c r_draw.c \
      r_main.c r_plane.c r_segs.c r_sky.c r_things.c sha1.c sounds.c statdump.c \
      st_lib.c st_stuff.c s_sound.c tables.c v_video.c wi_stuff.c w_checksum.c \
      w_file.c w_main.c w_wad.c z_zone.c w_file_stdc.c i_input.c i_video.c \
      doomgeneric.c doomgeneric_xlib.c \
   --out=one_doom.c

And now we can obfuscate (using the Tigress Ident transormation which does nothing!), compile, and play:

> tigress \
       --Transform=Ident
       --out=one_doom.obf.c \
        one_doom.c  

> gcc -o generic ./one_doom.obf.c -lm -lc -lX11 -Wl,-Map,doomgeneric.map

Case Study 6: CoreUtils

"The GNU Core Utilities are the basic file, shell and text manipulation utilities of the GNU operating system." A number of academic papers use these programs for evaluate purposes (whether this is a good idea or not is another question), so it make sense to be able to merge them. Here is how to merge the who utility:

1

Get the CoreUtils sources:

> git clone git://git.sv.gnu.org/coreutils

2

Run this command tet submodules and set up configuration:

   > ./bootstrap

3

Configure the programs you want (here who):

  > ./configure --enable-install-program=who --enable-no-install-program=chroot,hostid,timeout,nice,\
users,pinky,stty,df,stdbuf,[,b2sum,base64,base32,basenc,basename,cat,chcon,chgrp,chmod,chown,cksum,\
comm,cp,csplit,cut,date,dd,dir,dirname,dircolors,du,echo,env,expand,expr,factor,false,fmt,fold,ginstall,\
groups,head,id,join,kill,link,ln,logname,ls,md5sum,mkdir,mkfifo,mknod,mktemp,mv,nl,nproc,nohup,numfmt,od,\
paste,pathchk,pr,printenv,printf,ptx,pwd,readlink,realpath,rm,rmdir,runcon,seq,sha1sum,sha224sum,sha256sum,\
sha384sum,sha512sum,shred,shuf,sleep,sort,split,stat,sum,sync,tac,tail,tee,test,touch,tr,true,truncate,tsort,\
tty,uname,unexpand,uniq,unlink,uptime,vdir,wc,whoami,yes

4

Run make and then hit ctrl-C./lib/configmake.h (and presumably) other files that are necessary.

5

Merge:

> tigress -I ./lib -I./src  src/who.c --out=/tmp/who.c

6

Compile and run:

> gcc  -I. -I./lib  -Ilib -I./lib -Isrc -I./src    -g -O2 -MT src/who.o -MD -MP -MF $depbase.Tpo -c -o src/who.o /tmp/who.c
> gcc   -g -O2 -Wl,--as-needed  -o src/who src/who.o src/libver.a lib/libcoreutils.a   lib/libcoreutils.a   -ldl

> src/who
collberg pts/0        2024-11-20 01:06 (100.11.0.0)