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"); } } }