ronaldoussoren / macholib

macholib can be used to analyze and edit Mach-O headers, the executable format used by Mac OS X. It's typically used as a dependency analysis tool, and also to rewrite dylib references in Mach-O headers to be @executable_path relative. Though this tool targets a platform specific file format, it is pure python code that is platform and endian independent.
MIT License
92 stars 19 forks source link

how do i add a load command? #42

Open DetachHead opened 2 years ago

DetachHead commented 2 years ago

i have no idea what i'm doing, so forgive me if i'm doing something (or everything) wrong.

context

even though i don't have a mac and know nothing about iOS development, i'm trying to fix a framework for a .ipa file that seems to have been compiled wrong. i get this error when trying to upload it to testflight:

ERROR ITMS-90125: "The binary is invalid. The encryption info in the LC_ENCRYPTION_INFO load command is either missing or invalid, or the binary is already encrypted. This binary does not seem to have been built with Apple's linker."

i used lipo as mentioned here to remove unused architectures but it didn't work. as far as i can tell that wasn't the only issue because i don't see the ITMS-90087 and ITMS-90209 errors

running out of options i decided to try and see if i can just add the load command to the binary manually. using both otool and this library i was able to verify that this framework does not contain the LC_ENCRYPTION_INFO command.

code

from macholib.mach_o import LC_ENCRYPTION_INFO_64, encryption_info_command_64
from macholib.MachO import MachO, load_command

mach_o_path = "Payload/foo.app/Frameworks/foo.framework/binary"

mach_o = MachO(mach_o_path)

mach_o.headers[-1].commands.append(
    (
        load_command(LC_ENCRYPTION_INFO_64, 24),
        encryption_info_command_64(
            cryptid=0, cryptoff=16384, cryptsize=27344896, pad=0
        ),
        b"",
    )
)

mach_o.write(open(mach_o_path, "wb"))

error

i don't get any errors when writing to the file but attempting to read the binary again after making that change cause the following error:

Traceback (most recent call last):
  File "c:\Users\amogus\asdf.py", line 9, in <module>
    mach_o = MachO(mach_o_path)
  File "C:\Users\amogus\.venv\lib\site-packages\macholib\MachO.py", line 122, in __init__
    self.load(fp)
  File "C:\Users\amogus\.venv\lib\site-packages\macholib\MachO.py", line 137, in load
    self.load_header(fh, 0, size)
  File "C:\Users\amogus\.venv\lib\site-packages\macholib\MachO.py", line 171, in load_header
    raise ValueError("Unknown Mach-O header: 0x%08x in %r" % (header, fh))
ValueError: Unknown Mach-O header: 0x00000000 in <_io.BufferedReader name='Payload/foo.app/Frameworks/foo.framework/binary'>
ronaldoussoren commented 2 years ago

Good questions, and I don't have a good answer at the moment.

The code currently cannot add or remove load commands, it just has limited support for changing existing load commands as long as those changes don't change the size of those commands. I have a use case for other changes to the load commands (in particular removing some load commands and moving around other load commands to redistribute "free" space to be able to grow some payloads), but that will require some research on my side.

ronaldoussoren commented 2 years ago

What I don't understand is why you're trying to use macholib for making these changes. Your question seems to indicate that there's something wrong with the build of an app bundle and IMHO that should be fixed at the source instead of trying to fix it during the upload to Apple.

jvolkman commented 1 year ago

It's pretty straightforward and you're close. You're missing:

  1. Make sure there's actually space: assert macho_header.low_offset - macho_header.total_size >= new_load_command.cmdsize
  2. pass the appropriate endianness to your new structs: load_command(..., _endian_= macho_header.endian) (same for encryption_info_command_64)
  3. macho_header.header.ncmds += 1
  4. macho_header.changeHeaderSizeBy(new_load_command.cmdsize)