lief-project / LIEF

LIEF - Library to Instrument Executable Formats (C++, Python, Rust)
https://lief.re
Apache License 2.0
4.44k stars 620 forks source link

Modifying symbol name or library_ordinal for the macho turns into a wrong mach-O file #585

Open jrodgut opened 3 years ago

jrodgut commented 3 years ago

macho_file.zip

Describe the bug Parsing a mach-O file and modifying the symbol names to something else with a different length, or the library indexes to something else creates a wrong mach-O file. After making any of these changes (and removing the signature either before or after), the file is written but parsing it again shows some warnings about an inconsistent state in the section (it shows warnings Unsupported opcode). Also if the file is resigned, signing tools fail because of a collision between the lazy binding info section and the exports info section (I guess the size and offsets of each are not updated properly).

To Reproduce I have been able to reproduce it with the last 2 versions of LIEF (0.11.4, 0.11.5).

These are 2 simple ways to reproduce it with the file attached in the issue. Though it can easily be reproduced doing similar things with other macho binaries (and not only in the lazy binding info opcodes, but also with the binding info ones).

Case 1: modify symbol name in a lazy binding opcode with a longer name

    print('Parsing...')
    fat_binary = lief.MachO.parse(macho_file_path, config=lief.MachO.ParserConfig.deep)

    for binary_index in range(0, fat_binary.size):
        binary = fat_binary.at(binary_index)

        if binary.has_code_signature:
            print(f'Removing signature')
            binary.remove_signature()

        print(f'Iterating over arch {binary.header.cpu_type}')
        for binding in binary.dyld_info.bindings:
            if binding.has_symbol and binding.symbol.name == '_NSStringFromClass':
                print(f'Bindind found {binding}')
                print(f'Modifying symbol name')
                binding.symbol.name = '_NSStringFromClassFOOOOO'
    fat_binary.write(modified_macho_path)

    # Parse the modified mach-O
    print('Parsing modified mach-O...')
    modified_fat_binary = lief.MachO.parse(modified_macho_path, config=lief.MachO.ParserConfig.deep)
    print('Parsed!...')

which turns into this output (note the "Unsupported opcode" lines when re-parsing)

Parsing...
Removing signature
Iterating over arch CPU_TYPES.ARM
Bindind found Class:       LAZY
Type:        POINTER
Address: 0x  7c2dc
Symbol:      _NSStringFromClass
Segment:     __DATA
Library:     /System/Library/Frameworks/Foundation.framework/Foundation

Modifying symbol name
Removing signature
Iterating over arch CPU_TYPES.ARM64
Bindind found Class:       LAZY
Type:        POINTER
Address: 0x  10006c268
Symbol:      _NSStringFromClass
Segment:     __DATA
Library:     /System/Library/Frameworks/Foundation.framework/Foundation

Modifying symbol name
Larger symbol names size is not supported yet
Parsing modified mach-O...
New symbol found: _NSStringFromClassFOOOOO
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Parsed!...

Case 2: modify the library index of a lazy binding opcode

    print('Parsing...')
    fat_binary = lief.MachO.parse(macho_file_path, config=lief.MachO.ParserConfig.deep)

    for binary_index in range(0, fat_binary.size):
        binary = fat_binary.at(binary_index)

        if binary.has_code_signature:
            print(f'Removing signature')
            binary.remove_signature()

        print(f'Iterating over arch {binary.header.cpu_type}')
        for binding in binary.dyld_info.bindings:
            if binding.has_symbol and binding.symbol.name == '_NSStringFromClass':
                print(f'Bindind found {binding}')
                print(f'Modifying library index from {binding.library_ordinal} to 16')
                binding.library_ordinal = 16
    fat_binary.write(modified_macho_path)

    # Parse the modified mach-O
    print('Parsing modified mach-O...')
    modified_fat_binary = lief.MachO.parse(modified_macho_path, config=lief.MachO.ParserConfig.deep)
    print('Parsed!...')

which turns into this output (note the "Unsupported opcode" lines when re-parsing)

Parsing...
Removing signature
Iterating over arch CPU_TYPES.ARM
Bindind found Class:       LAZY
Type:        POINTER
Address: 0x  7c2dc
Symbol:      _NSStringFromClass
Segment:     __DATA
Library:     /System/Library/Frameworks/Foundation.framework/Foundation

Modifying library index from 2 to 16
Removing signature
Iterating over arch CPU_TYPES.ARM64
Bindind found Class:       LAZY
Type:        POINTER
Address: 0x  10006c268
Symbol:      _NSStringFromClass
Segment:     __DATA
Library:     /System/Library/Frameworks/Foundation.framework/Foundation

Modifying library index from 2 to 16
Parsing modified mach-O...
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Unsupported opcode: 0x50
Parsed!...

Expected behavior Modifying symbol names or library indexes for binding opcodes should work ok, so parsing the generated file should not print any error (and the binary can be later resigned). It is also strange that the parser prints errors, but it doesn't raise Exceptions (or I haven't found a way to configure it to raise Exceptions if it cannot parse it correctly).

Environment (please complete the following information):

Additional context None.

romainthomas commented 3 years ago

Hi @jrodgut

For the first issue (extending symbol's name), it's not supported yet: https://github.com/lief-project/LIEF/blob/158f291b1f1beec2e420e6624ec7833add0fb1e4/src/MachO/Builder.tcc#L703-L706

For the second, it comes from the fact that your ordinal is above 15 which leads in extending the lazy bind opcodes:

https://github.com/lief-project/LIEF/blob/158f291b1f1beec2e420e6624ec7833add0fb1e4/src/MachO/DyldInfo.cpp#L1596-L1603 but here you don't have the warning that says that it's not supported by LIEF.

A workaround for this second case is to set an ordinal number below or equal to 15

jrodgut commented 3 years ago

OK, thanks for checking. I guess this could turn into a feature request for the future.

keroushe commented 1 year ago

Is this problem solved?

HMaker commented 1 year ago

@keroushe no, it's not possible to change library ordinal to a value greater than 15. You found any workaround?