LIEF icon indicating copy to clipboard operation
LIEF copied to clipboard

Rewriting NEEDED section creates broken .so

Open lkollar opened this issue 5 years ago • 2 comments

Describe the bug Modifying the NEEDED section in libcrypto.so mangles the version needs section.

Original:

Version needs section '.gnu.version_r' contains 2 entries:
 Addr: 0x0000000000037240  Offset: 0x037240  Link: 4 (.dynstr)
  000000: Version: 1  File: libdl.so.2  Cnt: 1
  0x0010:   Name: GLIBC_2.2.5  Flags: none  Version: 9
  0x0020: Version: 1  File: libc.so.6  Cnt: 5
  0x0030:   Name: GLIBC_2.4  Flags: none  Version: 10
  0x0040:   Name: GLIBC_2.3  Flags: none  Version: 8
  0x0050:   Name: GLIBC_2.7  Flags: none  Version: 7
  0x0060:   Name: GLIBC_2.3.4  Flags: none  Version: 6
  0x0070:   Name: GLIBC_2.2.5  Flags: none  Version: 5

Modified:

Version needs section '.gnu.version_r' contains 2 entries:
 Addr: 0x0000000000037240  Offset: 0x037240  Link: 4 (.dynstr)
  000000: Version: 1120  File:   Cnt: 0
  0x0020: Version: 1  File: libc.so.6  Cnt: 5
  0x0030:   Name: GLIBC_2.4  Flags: none  Version: 10
  0x0040:   Name: GLIBC_2.3  Flags: none  Version: 8
  0x0050:   Name: GLIBC_2.7  Flags: none  Version: 7
  0x0060:   Name: GLIBC_2.3.4  Flags: none  Version: 6
  0x0070:   Name: GLIBC_2.2.5  Flags: none  Version: 5

Running ldd on the modified .so produces the following error message:

/tmp/libcrypto_modified.so: error while loading shared libraries: /tmp/libcrypto_modified.so: unsupported version 1120 of Verneed record

To Reproduce Steps to reproduce the behavior:

Run the following script:

import lief

def replace_needed(file_name, 
                   out_file_name,
                   so_name,
                   new_so_name):                                                                       
  elf = lief.parse(file_name)                                                                                                  
  for lib in elf.dynamic_entries:                                                                                              
      if lib.tag == lief.ELF.DYNAMIC_TAGS.NEEDED and lib.name == so_name:                                                      
          print("Replacing needed library: {} with {} in {}".format(
                      lib.name, new_so_name, file_name))
          lib.name = new_so_name                                                                                               
  elf.write(out_file_name)                                      

replace_needed("/usr/lib64/libcrypto.so.1.0.1e", 
               "/tmp/libcrypto_modified.so",
               "libz.so.1",
               "libz.so.1")

Check the resulting /tmp/libcrypto_modified.so file with readelf -V. The original libcrypto.so.1.0.1e is from the quay.io/pypa/manylinux2010_x86_64 image so the easiest might be to run the example in that Docker image. I can also upload the .so somewhere if you prefer.

Expected behavior A valid .so file.

Environment (please complete the following information):

  • System and Version : CentOS 6.10 (quay.io/pypa/manylinux2010_x86_64)
  • Target format: ELF
  • LIEF commit version: 0.10.1-bfe5414

lkollar avatar Dec 29 '19 17:12 lkollar

I think it is related to https://github.com/lief-project/LIEF/issues/369 and https://github.com/lief-project/LIEF/issues/239

romainthomas avatar Jan 04 '20 07:01 romainthomas

Indeed, the modified library seems to suffer from the same dynamic symbol number mismatch. I tried the workaround posted in #239 to use lief.ELF.DYNSYM_COUNT_METHODS.SECTION and that yields the correct number of symbols, but it doesn't change the invalid Verneed record. There might be two separate bugs here.

lkollar avatar Jan 04 '20 22:01 lkollar

Should have been fixed with the refactoring of the ELF builder.

romainthomas avatar Apr 28 '23 04:04 romainthomas