Patching old linux binaries to work with recent libc versions

2012, Nov 14    

From time to time I need to use some old binary created for older Linux versions like Redhat 6.2, for example. The problem with those binaries is that they were compiled with a very old version of the glibc and they cannot be run ‘like this’ in newer systems. Sometimes, just making a symbolic link from the new library to the old name can be enough but not always. In this brief post I will talk about how to workaround the typical relocation errors and undefined symbols problems with old binaries.

For this article I will use a program and a library for the RSIB protocol (if you search for it in Google or another search engine, you will find the binaries online easily). The program is called ‘interactive’ and the library ‘librsib.so.1.0’. First of all, we need to check what symbols or libraries are missing. We can find it out by simply using the command ‘ldd’:

$ ldd interactive 
	linux-gate.so.1 =>  (0xf7706000)
	librsib.so => not found
	libstdc++-libc6.1-1.so.2 => not found
	libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf76a9000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf74ff000)
	/lib/ld-linux.so.2 (0xf7707000)

 

So we’re missing libstdc++-libc6.1-1.so.2 and librsib.so. We need 2 symbolic links to those libraries. For the 1st, I’ll try to symbolic link to my current libstdc++ library and for the 2nd I just need to symbolic link to the librsib.so.1.0 library I already have:

$ ln -s /usr/lib32/libstdc++.so.6 libstdc++-libc6.1-1.so.2
$ ln -s librsib.so.1.0 librsib.so
$ export LD_LIBRARY_PATH=`pwd`
$ $ ldd interactive 
	linux-gate.so.1 =>  (0xf77db000)
	librsib.so => /home/joxean/somepath/librsib.so (0xf77cc000)
	libstdc++-libc6.1-1.so.2 => /home/joxean/somepath/libstdc++-libc6.1-1.so.2 (0xf76e7000)
	libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf768c000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf74e2000)
	/lib/ld-linux.so.2 (0xf77dc000)
	libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xf74c4000)

 

OK, it seems to work, let’s try to run it:

$ ./interactive 
./interactive: symbol lookup error: /home/joxean/somepath/librsib.so: undefined symbol: __tic

 

It wasn’t that easy. The symbol __tic is not defined in our libstdc++ version and librsib.so needs it. So, what we’re going to do is to create another library defining the symbols we need and patch the binaries (librsib.so and interactive) so they use our newly created library instead.

$ cat > libkk.c
#include 

int __tic(void)
{
  return 0;
}
^C
$ gcc -shared -fPIC libkk.c -o libkk.so -shared

 

Now that we have our new library with the undefined symbol we need to patch the binaries and make them use libkk.so instead of the old libstdc++ versions they were linked against. I’ll use Pyew for this:

$ pyew interactive
(...)
[0x00000000]> /s libstdc++
HINT[0x00000878]: libstdc++-libc6.1-1.so.2.libm.so.6.libc.so.6.strcpy.printf._
[0x00000000]> s 0x878
[0x00000878:0x08048878]> x
0878   6C 69 62 73 74 64 63 2B 2B 2D 6C 69 62 63 36 2E    libstdc++-libc6.
0888   31 2D 31 2E 73 6F 2E 32 00 6C 69 62 6D 2E 73 6F    1-1.so.2.libm.so
0898   2E 36 00 6C 69 62 63 2E 73 6F 2E 36 00 73 74 72    .6.libc.so.6.str
08A8   63 70 79 00 70 72 69 6E 74 66 00 5F 5F 77 72 69    cpy.printf.__wri

 

The name of the libstdc++ library is in the offset 0x878, let’s do change it to our new library libkk.so by re-opening the file for editing and writing the new name:

[0x00000878:0x08048878]> edit
[0x00000878:0x08048878]> wa libkk.so
[0x00000878:0x08048878]> s +8
[0x00000880:0x08048880]> wx 00
[0x00000880:0x08048880]> s 0x878
[0x00000878:0x08048878]> x
0878   6C 69 62 6B 6B 2E 73 6F 00 2D 6C 69 62 63 36 2E    libkk.so.-libc6.
0888   31 2D 31 2E 73 6F 2E 32 00 6C 69 62 6D 2E 73 6F    1-1.so.2.libm.so
0898   2E 36 00 6C 69 62 63 2E 73 6F 2E 36 00 73 74 72    .6.libc.so.6.str
08A8   63 70 79 00 70 72 69 6E 74 66 00 5F 5F 77 72 69    cpy.printf.__wri
[0x00000878:0x08048878]> q

 

We have to patch the library librsib.so like we did with the binary ‘interactive’ (I’m skipping this step here as it’s equal). Now, let’s see what happens when running again the binary:

$ ./interactive 
./interactive: symbol lookup error: /home/joxean/somepath/librsib.so: undefined symbol: __builtin_new

Another undefined symbol we have to implement in our libray. The function __builtin_new seems to be to allocate memory. Looking in IDA the binary interactive it seems it only receives one parameter which, I guess, is the size of the memory to reserve:

.text:00006159                 align 10h
.text:00006160
.text:00006160 loc_6160:                               ; CODE XREF: Connect__38CClientInetCommunicationChannelFactoryPCcN21+F9j
.text:00006160                 push    1Ch             ; size parameter
.text:00006162                 call    ___builtin_new

 

So we need to edit again the source of libkk and add this new symbol:

$ cat libkk.c 
#include 
#include 
#include 

int __tic(void)
{
  return 0;
}

void *__builtin_new(size_t size)
{
    return malloc(size);
}

$ gcc -shared -fPIC libkk.c -o libkk.so -shared -m32
$ ./interactive 
Interactive Control Program
Copyright 2000 Rohde & Schwarz
All rigths reserved.

Type 'help' for help or 'q' to quit

./interactive: symbol lookup error: ./interactive: undefined symbol: __builtin_vec_new

 

We can run some parts of the binary! But we need to define new more symbols. In this case, __builtin_vec_new. It seems to be (looking the ‘interactive’ binary in IDA and finding a bit in the internet) that this function does effectively the same as __builtin_new so, let’s edit again our library source:

$ cat libkk.c 
#include 
#include 
#include 

int __tic(void)
{
  return 0;
}

void *__builtin_new(size_t size)
{
    return malloc(size);
}

void *__builtin_vec_new(size_t size)
{
    return malloc(size);
}
$ gcc -shared -fPIC libkk.c -o libkk.so -shared -m32
$ ./interactive 
Interactive Control Program
Copyright 2000 Rohde & Schwarz
All rigths reserved.

Type 'help' for help or 'q' to quit

: q
./interactive: symbol lookup error: ./interactive: undefined symbol: __builtin_vec_delete

 

Looks better, we have been able to reach the program’s prompt but, again, we have another undefined symbol: __builtin_vec_delete. After briefly checking in IDA that it only receives one parameter and considering that we wrapped the old symbols with mallocs, it’s clear that we need to wrap free in this function, let’s do this:

$ cat libkk.c
#include 
#include 
#include 

int __tic(void)
{
  return 0;
}

void *__builtin_new(size_t size)
{
    return malloc(size);
}

void *__builtin_vec_new(size_t size)
{
    return malloc(size);
}

void __builtin_vec_delete(void *ptr)
{
    free(ptr);
}

$ gcc -shared -fPIC libkk.c -o libkk.so -shared -m32
$ ./interactive 
Interactive Control Program
Copyright 2000 Rohde & Schwarz
All rigths reserved.

Type 'help' for help or 'q' to quit

: q
./interactive: symbol lookup error: /home/joxean/somepath/librsib.so: undefined symbol: __builtin_delete

 

Still one more undefined symbol. Exactly the same function as before with just a different name, let’s implement it, build the library and execute the binary again:

$ cat libkk.c
#include 
#include 
#include 

int __tic(void)
{
  return 0;
}

void *__builtin_new(size_t size)
{
    return malloc(size);
}

void *__builtin_vec_new(size_t size)
{
    return malloc(size);
}

void __builtin_vec_delete(void *ptr)
{
    free(ptr);
}

void __builtin_delete(void *ptr)
{
    free(ptr);
}

$ gcc -shared -fPIC libkk.c -o libkk.so -shared -m32
$ ./interactive 
Interactive Control Program
Copyright 2000 Rohde & Schwarz
All rigths reserved.

Type 'help' for help or 'q' to quit

: q
$

Hurra! Finally, we can run our old binary in the new system without errors (or at least what I tested). I hope this brief post may help somebody out there if (s)he ever finds her/himself in the same problem.

PS: Thanks to pancake for helping me with it some time ago!