package ch.odi;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* Removes the version information of openssl from an ELF executable.
* Debian is known to include version information for openssl. When
* you use such a binary on other distributions that don't have
* verion information for openssl, ldd issues an error message:
*
* /usr/lib64/libcrypto.so.1.0.0: no version information available
* /usr/lib64/libssl.so.1.0.0: no version information available
*
*
* Modifies the following ELF sections:
* .gnu.version (SHT_GNU_versym):
* Changes all openssl symbol versions to *global* (1).
*
* .gnu.version_r (SHT_GNU_verneed):
* Unlinks the versioning information for libcrypto.so and libssl.so from
* the linked list.
*
* See ELF Symbol Versioning.
*
* Copyright 2011 by Ortwin Glück
*
* License: CC BY-SA
* http://creativecommons.org/licenses/by-sa/3.0/
*
* @author Ortwin Glück
* @version 1.1
*/
public class ElfFix {
private static final int DT_VERNEED = 0x6ffffffe;
private ElfHeader eh;
private SecHeader[] shs;
private StringSec secNames;
private SecHeader gnuVerSh, gnuVerNeedSh;
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Syntax: java "+ ElfFix.class.getName() +" binary");
System.exit(1);
}
System.out.println(args[0]);
ElfFix app = new ElfFix();
try {
app.run(args[0]);
} catch (IOException e) {
System.err.print(e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
public void run(String file) throws IOException {
File fn = new File(file);
Elf f = new Elf(fn, "rw");
eh = new ElfHeader(f);
shs = new SecHeader[eh.shnum];
f.seek(eh.shoff);
int shstrtab = -1;
for (int i=0; i vns = VerNeedSec.readFrom(gnuVerNeedSh, verNeedStr);
System.out.println("unlinking following GNU Verneed from list:");
String[] sos = { "libcrypto.so.", "libssl.so." };
int[] vers = { -1, -1 }; // version identifiers of the sos
for (int i=0; i "+ newOfs);
f.seek(addr);
f.writeWord(newOfs);
// they can be adjacent
vns.remove(j);
j--;
}
}
}
System.out.println("setting GNU versyms of OPENSSL to *global*:");
List vss = Versym.readFrom(gnuVerSh);
int i = 0;
for (Versym vs : vss) {
if ((vs.v == vers[0]) || (vs.v == vers[1])) {
long addr = gnuVerSh.offset + i*gnuVerSh.entsize;
System.out.println(i +" @ "+ addr); // WORD
f.seek(addr);
f.writeHalf(1);
}
i++;
}
f.close();
System.out.println("done.");
}
private static class Versym {
int v;
public static List readFrom(SecHeader sh) throws IOException {
List list = new ArrayList();
sh.elf.seek(sh.offset);
int count = (int) (sh.size / sh.entsize);
for (int i=0; i vas;
private long pos;
private static List readFrom(SecHeader sh, StringSec strings) throws IOException {
sh.elf.seek(sh.offset);
List list = new ArrayList();
while (true) {
VerNeedSec v = new VerNeedSec();
v.pos = sh.elf.getFilePointer();
v.sh = sh;
v.strings = strings;
v.version = sh.elf.readHalf();
if (v.version != 1) throw new IOException("VerNeedSec.version "+ v.version +" not supported");
v.cnt = sh.elf.readHalf();
v.file = sh.elf.readWord();
v.aux = sh.elf.readWord();
v.next = sh.elf.readWord();
v.vas = VernAux.readFrom(v);
list.add(v);
if (v.next <= 0) break;
sh.elf.seek(v.pos + v.next);
};
return list;
}
public String getFileName() {
return strings.getString(file);
}
}
private static class VernAux {
VerNeedSec vns;
int hash;
int flags;
int other; // referenced from Versym.v
int name;
int next;
private long pos;
private static List readFrom(VerNeedSec vns) throws IOException {
Elf elf = vns.sh.elf;
List list = new ArrayList();
while (true) {
VernAux a = new VernAux();
a.vns = vns;
a.pos = elf.getFilePointer();
a.hash = elf.readSWord(); // we don't care about signedness
a.flags = elf.readHalf();
a.other = elf.readHalf();
a.name = elf.readWord();
a.next = elf.readWord();
list.add(a);
if (a.next <= 0) break;
elf.seek(a.pos + a.next);
}
return list;
}
public String getName() {
return vns.strings.getString(name);
}
}
private static class StringSec {
private byte[] a;
public StringSec(SecHeader sh) throws IOException {
readFrom(sh);
}
private void readFrom(SecHeader sh) throws IOException {
if (sh.type != 3)
throw new IllegalArgumentException("sec is not a string table");
sh.elf.seek(sh.offset);
if (sh.size > Integer.MAX_VALUE)
throw new IllegalArgumentException("string table too large");
this.a = new byte[(int) sh.size];
sh.elf.readFully(a);
}
public String getString(int idx) {
if (idx == 0) return "";
int end;
for (end=idx; end < a.length; end++) {
if (a[end] == 0) break;
}
try {
return new String(a, idx, (end - idx), "ASCII");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
private static class SecHeader {
Elf elf;
int name;
int type;
long flags;
long addr;
long offset;
long size;
int link;
int info;
long addralign;
long entsize;
public SecHeader(Elf f) throws IOException {
readFrom(f);
}
private void readFrom(Elf f) throws IOException {
this.elf = f;
this.name = f.readWord();
this.type = f.readWord();
this.flags = elf.is32bit ? f.readWord() : f.readXWord();
this.addr = f.readAddr();
this.offset = f.readOff();
this.size = elf.is32bit ? f.readWord() : f.readXWord();
this.link = f.readWord();
this.info = f.readWord();
this.addralign = elf.is32bit ? f.readWord() : f.readXWord();
this.entsize = elf.is32bit ? f.readWord() : f.readXWord();
}
}
private static class ElfHeader {
byte[] ident;
int type;
int machine;
int version;
long entry;
long phoff;
long shoff;
int flags;
int ehsize;
int phentsize;
int phnum;
int shentsize;
int shnum;
int shstrndx;
public ElfHeader(Elf f) throws IOException {
readFrom(f);
}
public void readFrom(Elf elf) throws IOException {
this.ident = elf.readBytes(16);
elf.is32bit = (this.ident[4] == 1);
if (this.ident[5] != 1) throw new IOException("MSB ELF not supported");
this.type = elf.readHalf();
this.machine = elf.readHalf();
this.version = elf.readWord();
this.entry = elf.readAddr();
this.phoff = elf.readOff();
this.shoff = elf.readOff();
this.flags = elf.readWord();
this.ehsize = elf.readHalf();
this.phentsize = elf.readHalf();
this.phnum = elf.readHalf();
this.shentsize = elf.readHalf();
this.shnum = elf.readHalf();
this.shstrndx = elf.readHalf();
}
}
/* Addr: long 8/4
* Off : long 8/4
* Half: int 2
* Word: int 4
* SWord: int 4
* XWord: long 8
* SXWord: long 8
* char: char/byte 1
*/
private static class Elf extends RandomAccessFile {
private byte[] scratch = new byte[8];
private boolean is32bit;
public Elf(File f, String mode) throws FileNotFoundException {
super(f, mode);
}
private long readLSBLong() throws IOException {
this.readFully(scratch);
long l = ((scratch[7] & 0xFF) << 56)
| ((scratch[6] & 0xFF) << 48)
| ((scratch[5] & 0xFF) << 40)
| ((scratch[4] & 0xFF) << 32)
| ((scratch[3] & 0xFF) << 24)
| ((scratch[2] & 0xFF) << 16)
| ((scratch[1] & 0xFF) << 8)
| (scratch[0] & 0xFF);
return l;
}
private int readLSBInt() throws IOException {
this.readFully(scratch, 0, 4);
int l = ((scratch[3] & 0xFF) << 24)
| ((scratch[2] & 0xFF) << 16)
| ((scratch[1] & 0xFF) << 8)
| (scratch[0] & 0xFF);
return l;
}
private void writeLSBInt(int l) throws IOException {
scratch[0] = (byte) (l & 0xFF);
l >>= 8;
scratch[1] = (byte) (l & 0xFF);
l >>= 8;
scratch[2] = (byte) (l & 0xFF);
l >>= 8;
scratch[3] = (byte) (l & 0xFF);
this.write(scratch, 0, 4);
}
private short readLSBShort() throws IOException {
this.readFully(scratch, 0, 2);
int l = ((scratch[1] & 0xFF) << 8)
| (scratch[0] & 0xFF);
return (short) (l & 0xFFFF);
}
private void writeLSBShort(int l) throws IOException {
scratch[0] = (byte) (l & 0xFF);
l >>= 8;
scratch[1] = (byte) (l & 0xFF);
this.write(scratch, 0, 2);
}
public long readAddr() throws IOException {
long l = is32bit ? this.readLSBInt() : this.readLSBLong();
if (l < 0) throw new IOException("signed: "+ l);
return l;
}
public long readOff() throws IOException {
long l = is32bit ? this.readLSBInt() : this.readLSBLong();
if (l < 0) throw new IOException("signed");
return l;
}
public int readHalf() throws IOException {
int i = this.readLSBShort();
if (i < 0) throw new IOException("signed");
return i;
}
public void writeHalf(int i) throws IOException {
writeLSBShort(i);
}
public int readWord() throws IOException {
int i = this.readLSBInt();
if (i < 0) throw new IOException("signed");
return i;
}
public void writeWord(int i) throws IOException {
writeLSBInt(i);
}
public int readSWord() throws IOException {
return this.readLSBInt();
}
public long readXWord() throws IOException {
long l = this.readLSBLong();
if (l < 0) throw new IOException("signed");
return l;
}
public long readSXWord() throws IOException {
return this.readLSBLong();
}
public byte[] readBytes(int count) throws IOException {
byte[] a = new byte[count];
this.readFully(a);
return a;
}
public String readAscii(int count) throws IOException {
byte[] a = new byte[count];
this.readFully(a);
return new String(a, "ASCII");
}
}
}