mirror of
https://github.com/lief-project/LIEF
synced 2026-06-08 15:30:44 +00:00
699 lines
22 KiB
Python
699 lines
22 KiB
Python
#!/usr/bin/env python
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import pathlib
|
|
import os
|
|
import pytest
|
|
import random
|
|
from pathlib import Path
|
|
from subprocess import Popen
|
|
|
|
import lief
|
|
from utils import get_sample, is_apple_m1, is_osx, is_x86_64, sign, chmod_exe, is_github_ci
|
|
|
|
def align_to(value, alignment):
|
|
# llvm::alignTo
|
|
assert (alignment & (alignment - 1)) == 0 # is power of two
|
|
return (value + alignment - 1) & ~(alignment - 1)
|
|
|
|
def dyld_check(path: Path):
|
|
dyld_info_path = Path("/usr/bin/dyld_info")
|
|
if not dyld_info_path.exists():
|
|
dyld_info_path = shutil.which("dyld_info")
|
|
|
|
if dyld_info_path is None:
|
|
return
|
|
|
|
cmd = [
|
|
str(dyld_info_path),
|
|
"-validate_only",
|
|
str(path)
|
|
]
|
|
kwargs = {
|
|
"universal_newlines": True,
|
|
"stdout": subprocess.PIPE,
|
|
"stderr": subprocess.STDOUT
|
|
}
|
|
lief.logging.info("Running {}".format(" ".join(cmd))) # pylint: disable=consider-using-f-string
|
|
with Popen(cmd, **kwargs) as proc: # type: ignore[call-overload]
|
|
lief.logging.info(proc.stdout.read())
|
|
proc.poll()
|
|
assert proc.returncode == 0, f"Return code: {proc.returncode}"
|
|
|
|
def run_program(path: Path, args=None):
|
|
if is_apple_m1():
|
|
sign(path)
|
|
|
|
# Make sure the program has exec permission
|
|
chmod_exe(path)
|
|
dyld_check(path)
|
|
|
|
env = os.environ
|
|
env["DYLD_PRINT_APIS"] = "1"
|
|
env["DYLD_PRINT_WARNINGS"] = "1"
|
|
|
|
kwargs = {
|
|
"universal_newlines": True,
|
|
"stdout": subprocess.PIPE,
|
|
"stderr": subprocess.STDOUT,
|
|
"env": env,
|
|
}
|
|
|
|
prog_args = path if args is None else [path] + args
|
|
with Popen(prog_args, **kwargs) as proc: # type: ignore[call-overload]
|
|
proc.poll()
|
|
lief.logging.info(f"{path} exited with {proc.returncode}")
|
|
return proc.stdout.read()
|
|
|
|
def test_id(tmp_path: Path):
|
|
original = lief.MachO.parse(get_sample('MachO/MachO64_x86-64_binary_id.bin')).at(0)
|
|
output = tmp_path / "test_id.bin"
|
|
original.write(output)
|
|
modified = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(modified)
|
|
assert checked, err
|
|
|
|
|
|
def test_add_command(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample('MachO/MachO64_x86-64_binary_id.bin'))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
|
|
output = tmp_path / "test_add_command.id.bin"
|
|
|
|
LIB_NAME = "/usr/lib/libSystem.B.dylib"
|
|
|
|
dylib_1 = lief.MachO.DylibCommand.lazy_load_dylib(LIB_NAME)
|
|
dylib_2 = lief.MachO.DylibCommand.weak_lib(LIB_NAME)
|
|
|
|
original.add(dylib_1)
|
|
original.add(dylib_2, 0)
|
|
|
|
original.remove_signature()
|
|
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
assert len([lib for lib in new.libraries if lib.name == LIB_NAME]) > 0
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
|
|
stdout = run_program(output)
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'uid=', stdout) is not None
|
|
|
|
|
|
def test_remove_cmd(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample('MachO/MachO64_x86-64_binary_id.bin'))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
|
|
output = tmp_path / "test_remove_cmd.id.bin"
|
|
|
|
uuid_cmd = original[lief.MachO.LoadCommand.TYPE.UUID]
|
|
original.remove(uuid_cmd)
|
|
original.remove_command(len(original.commands) - 1)
|
|
|
|
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
assert lief.MachO.LoadCommand.TYPE.UUID not in new
|
|
assert lief.MachO.LoadCommand.TYPE.CODE_SIGNATURE not in new
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
|
|
stdout = run_program(output)
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'uid=', stdout) is not None
|
|
|
|
def test_extend_cmd(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_id.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
|
|
output = tmp_path / "test_extend_cmd.id.bin"
|
|
|
|
# Extend UUID
|
|
uuid_cmd = original[lief.MachO.LoadCommand.TYPE.UUID]
|
|
original_size = uuid_cmd.size
|
|
original.extend(uuid_cmd, 0x4000)
|
|
|
|
original.remove_signature()
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
assert new[lief.MachO.LoadCommand.TYPE.UUID].size == original_size + 0x4000
|
|
|
|
def test_add_section_id(tmp_path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_id.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = f"{tmp_path}/test_add_section_id.id.bin"
|
|
|
|
checked, err = lief.MachO.check_layout(original)
|
|
assert checked, err
|
|
|
|
# Add 50 sections
|
|
for i in range(50):
|
|
section = lief.MachO.Section(f"__lief_{i}", [0x90] * 0x100)
|
|
original.add_section(section)
|
|
|
|
assert original.virtual_size % original.page_size == 0
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'uid=', stdout) is not None
|
|
|
|
def test_extend_section_1(tmp_path: Path):
|
|
""" This test calls add_section followed by extend_section repeatedly.
|
|
"""
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_id.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / "test_extend_section.bin"
|
|
|
|
text_segment = original.get_segment("__TEXT")
|
|
|
|
for i in range(9, -1, -1):
|
|
section = lief.MachO.Section(f"__lief_{i}")
|
|
section.alignment = i
|
|
section = original.add_section(text_segment, section)
|
|
assert original.extend_section(section, 1 << section.alignment)
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
def test_extend_section_2(tmp_path: Path):
|
|
""" This test makes multiple calls to add_section, and then it
|
|
extends each added section using extend_section.
|
|
"""
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_id.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / "test_extend_section.bin"
|
|
|
|
text_segment = original.get_segment("__TEXT")
|
|
|
|
sections = []
|
|
for i in range(3):
|
|
section = lief.MachO.Section(f"__lief_{i}")
|
|
section.alignment = 2 # 2^2 == 4 bytes
|
|
sections.append(original.add_section(text_segment, section))
|
|
|
|
for section in sections:
|
|
assert original.extend_section(section, 1000)
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
@pytest.mark.skipif(is_github_ci(), reason="sshd does not work on Github Action")
|
|
def test_add_section_ssh(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_sshd.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / "test_add_section_sshd.sshd.bin"
|
|
page_size = original.page_size
|
|
|
|
# Add 3 section into __TEXT
|
|
__text = original.get_segment("__TEXT")
|
|
for i in range(3):
|
|
section = lief.MachO.Section(f"__text_{i}")
|
|
section.content = [0xC3] * 0x100 # type: ignore[assignment]
|
|
original.add_section(__text, section)
|
|
|
|
assert original.virtual_size % page_size == 0
|
|
assert __text.virtual_size % page_size == 0
|
|
|
|
original.remove_signature()
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path, args=["--help"])
|
|
stdout = run_program(output, args=["--help"])
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'OpenSSH_6.9p1, LibreSSL 2.1.8', stdout) is not None
|
|
|
|
|
|
def test_add_segment_nm(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_nm.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / "test_add_segment_nm.nm.bin"
|
|
|
|
# Add segment without section
|
|
segment = lief.MachO.SegmentCommand("__LIEF", [0x60] * 0x100)
|
|
original.add(segment)
|
|
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output, ["-version"])
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'Default target:', stdout) is not None
|
|
|
|
def test_add_segment_all(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_all.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / "test_add_segment_all.all.bin"
|
|
|
|
# Add segment with sections
|
|
segment = lief.MachO.SegmentCommand("__LIEF_2")
|
|
for i in range(5):
|
|
section = lief.MachO.Section(f"__lief_2_{i}", [i] * 0x100)
|
|
segment.add_section(section)
|
|
original.add(segment)
|
|
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'Hello World: 1', stdout) is not None
|
|
|
|
@pytest.mark.skipif(is_github_ci(), reason="sshd does not work on Github Action")
|
|
def test_ssh_segments(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_sshd.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / "ssh_with_segments.bin"
|
|
|
|
# Add segment with sections
|
|
for i in range(10):
|
|
segment = lief.MachO.SegmentCommand(f"__LIEF_{i}", [i] * (0x457 + i))
|
|
original.add(segment)
|
|
|
|
original.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
assert len(new.segments) == len(original.segments)
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path, args=["--help"])
|
|
stdout = run_program(output, args=["--help"])
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'OpenSSH_6.9p1, LibreSSL 2.1.8', stdout) is not None
|
|
|
|
def test_remove_section(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_section_to_remove.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / bin_path.name
|
|
|
|
original.remove_section("__to_remove")
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
assert new.get_section("__to_remove") is None
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'Hello World', stdout) is not None
|
|
|
|
def test_remove_section_with_segment_name(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/MachO64_x86-64_binary_section_to_remove.bin"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / bin_path.name
|
|
|
|
original.remove_section("__DATA", "__to_remove")
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
assert new.get_section("__DATA", "__to_remove") is None
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'Hello World', stdout) is not None
|
|
|
|
def test_objc_arm64(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/test_objc_arm64.macho"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / bin_path.name
|
|
|
|
for i in range(50):
|
|
segment = lief.MachO.SegmentCommand(f"__LIEF_{i}", [i] * (0x457 + i))
|
|
original.add(segment)
|
|
|
|
# Extend the symbols table
|
|
for i in range(10):
|
|
sym = f"_foooo_{i}"
|
|
original.add_exported_function(original.imagebase + i * 8, sym)
|
|
|
|
sym = f"_foooo2_{i}"
|
|
original.add_local_symbol(original.entrypoint + i * 8, sym)
|
|
|
|
functions = original.function_starts.functions
|
|
functions *= 2
|
|
sorted(functions)
|
|
original.function_starts.functions = functions
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
if is_apple_m1():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'Printing Process Completed', stdout) is not None
|
|
|
|
|
|
def test_objc_x86_64(tmp_path: Path):
|
|
bin_path = pathlib.Path(get_sample("MachO/test_objc_x86_64.macho"))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / bin_path.name
|
|
|
|
for i in range(50):
|
|
segment = lief.MachO.SegmentCommand(f"__LIEF_{i}", [i] * (0x457 + i))
|
|
original.add(segment)
|
|
|
|
# Extend the symbols table
|
|
for i in range(10):
|
|
sym = f"_foooo_{i}"
|
|
original.add_exported_function(original.imagebase + i * 8, sym)
|
|
|
|
sym = f"_foooo2_{i}"
|
|
original.add_local_symbol(original.entrypoint + i * 8, sym)
|
|
|
|
functions = original.function_starts.functions
|
|
functions *= 2
|
|
sorted(functions)
|
|
original.function_starts.functions = functions
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
|
|
if is_osx() and is_x86_64():
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'Printing Process Completed', stdout) is not None
|
|
|
|
def test_break(tmp_path: Path):
|
|
FILES = [
|
|
"MachO/mbedtls_selftest_arm64.bin",
|
|
"MachO/mbedtls_selftest_x86_64.bin"
|
|
]
|
|
def swap(target: lief.MachO.Binary, lhs: str, rhs: str):
|
|
lhs_sec = target.get_section(lhs)
|
|
if lhs_sec is None:
|
|
lief.logging.info(f"Can't find section '{lhs_sec}'")
|
|
return
|
|
rhs_sec = target.get_section(rhs)
|
|
if rhs_sec is None:
|
|
lief.logging.info(f"Can't find section '{rhs_sec}'")
|
|
return
|
|
|
|
tmp = lhs_sec.name
|
|
rhs_sec.name = tmp
|
|
|
|
def shuffle(target: lief.MachO.Binary, name: str):
|
|
section = target.get_section(name)
|
|
if section is None:
|
|
return
|
|
lief.logging.info(f"[+] Shuffling '{name}'")
|
|
section_content = list(section.content)
|
|
random.shuffle(section_content)
|
|
section.content = section_content # type: ignore[assignment]
|
|
|
|
def corrupt_function_starts(target: lief.MachO.Binary):
|
|
fstart = target[lief.MachO.LoadCommand.TYPE.FUNCTION_STARTS]
|
|
assert isinstance(fstart, lief.MachO.FunctionStarts)
|
|
if fstart is None:
|
|
return
|
|
fstart.functions = [f + 5 for f in fstart.functions]
|
|
|
|
def process_exports(target: lief.MachO.Binary, sym: lief.MachO.Symbol):
|
|
#print(sym.export_info.address)
|
|
original_name = sym.export_info.symbol.name
|
|
assert isinstance(original_name, str)
|
|
name = list(original_name)
|
|
random.shuffle(name)
|
|
new_name = "_" + "".join(name)
|
|
address = sym.export_info.address
|
|
target.add_local_symbol(address, new_name)
|
|
|
|
def process_imports(target: lief.MachO.Binary, sym: lief.MachO.Symbol):
|
|
original_name = sym.binding_info.symbol.name
|
|
assert isinstance(original_name, str)
|
|
name = list(original_name)
|
|
random.shuffle(name)
|
|
new_name = "_" + "".join(name)
|
|
address = sym.binding_info.address - target.imagebase
|
|
target.add_local_symbol(address, new_name)
|
|
|
|
def process_local_symbol(target: lief.MachO.Binary, sym: lief.MachO.Symbol):
|
|
original_name = sym.name
|
|
assert isinstance(original_name, str)
|
|
name = list(sym.name)
|
|
random.shuffle(name)
|
|
sym.name = "_" + "".join(name)
|
|
sym.raw_type = 0xf
|
|
sym.description = 0x300
|
|
sym.numberof_sections = 1
|
|
sym.value += 2
|
|
|
|
def process_symbols(target: lief.MachO.Binary):
|
|
exports = []
|
|
imports = []
|
|
for sym in target.symbols:
|
|
if sym.has_export_info:
|
|
#print(f"[EXPORT]: {sym.name}")
|
|
exports.append(sym)
|
|
elif sym.has_binding_info:
|
|
#print(f"[IMPORT]: {sym.name}")
|
|
imports.append(sym)
|
|
else:
|
|
# "classical" symbol
|
|
process_local_symbol(target, sym)
|
|
|
|
for sym in exports:
|
|
process_exports(target, sym)
|
|
|
|
for sym in imports:
|
|
process_imports(target, sym)
|
|
|
|
|
|
def fake_objc(target: lief.MachO.Binary):
|
|
segment = lief.MachO.SegmentCommand("__DATA_LIEF")
|
|
|
|
__objc_classlist = lief.MachO.Section("__objc_classlist",
|
|
[random.randint(0, 255) for _ in range(0x100)])
|
|
__objc_const = lief.MachO.Section("__objc_const",
|
|
[random.randint(0, 255) for _ in range(0x100)])
|
|
__objc_classrefs = lief.MachO.Section("__objc_classrefs",
|
|
[random.randint(0, 255) for _ in range(0x100)])
|
|
|
|
__objc_classlist = segment.add_section(__objc_classlist)
|
|
__objc_const = segment.add_section(__objc_const)
|
|
__objc_classrefs = segment.add_section(__objc_classrefs)
|
|
|
|
objc_section = [
|
|
__objc_classlist,
|
|
__objc_const,
|
|
__objc_classrefs
|
|
]
|
|
section: lief.MachO.Section
|
|
for section in objc_section:
|
|
section.type = lief.MachO.Section.TYPE.REGULAR
|
|
section.flags = lief.MachO.Section.FLAGS.NO_DEAD_STRIP
|
|
section.alignment = 0x3
|
|
|
|
__data_lief: lief.MachO.SegmentCommand = target.add(segment) # type: ignore[assignment]
|
|
__data_lief.init_protection = 3
|
|
__data_lief.max_protection = 3
|
|
|
|
for file in FILES:
|
|
bin_path = pathlib.Path(get_sample(file))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / bin_path.name
|
|
|
|
SWAP_LIST = [
|
|
("__text", "__stubs"),
|
|
("__cstring", "__unwind_info"),
|
|
]
|
|
for (lhs, rhs) in SWAP_LIST:
|
|
swap(original, lhs, rhs)
|
|
|
|
process_symbols(original)
|
|
fake_objc(original)
|
|
corrupt_function_starts(original)
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|
|
should_run = (original.header.cpu_type == lief.MachO.Header.CPU_TYPE.X86_64 and is_osx()) or \
|
|
(original.header.cpu_type == lief.MachO.Header.CPU_TYPE.ARM64 and is_apple_m1())
|
|
|
|
if should_run:
|
|
assert run_program(bin_path)
|
|
stdout = run_program(output)
|
|
|
|
lief.logging.info(stdout)
|
|
assert re.search(r'All tests PASS', stdout) is not None
|
|
|
|
def test_issue_726(tmp_path: Path):
|
|
for filename in ("MachO/mbedtls_selftest_arm64.bin", "MachO/mbedtls_selftest_x86_64.bin"):
|
|
bin_path = pathlib.Path(get_sample(filename))
|
|
original = lief.MachO.parse(bin_path).at(0)
|
|
output = tmp_path / bin_path.name
|
|
|
|
original.write(output)
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
for parsed in (original, new):
|
|
assert parsed.get_segment("__LINKEDIT").virtual_size % parsed.page_size == 0
|
|
|
|
|
|
def test_rpath(tmp_path: Path):
|
|
# c.f. https://github.com/lief-project/LIEF/issues/1074
|
|
macho = lief.MachO.parse(get_sample("MachO/rpath_291.bin")).at(0)
|
|
rpaths = list(macho.rpaths)
|
|
|
|
assert rpaths[0].path == "/tmp"
|
|
assert rpaths[1].path == "/var"
|
|
|
|
rpaths[0].path = "/foo"
|
|
rpaths[1].path = "/bar"
|
|
|
|
output = tmp_path / "rpath.bin"
|
|
|
|
macho.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
|
|
new_rpaths = list(new.rpaths)
|
|
assert new_rpaths[0].path == "/foo"
|
|
assert new_rpaths[1].path == "/bar"
|
|
|
|
|
|
def test_encryption_info(tmp_path: Path):
|
|
# c.f. https://github.com/lief-project/LIEF/issues/1173
|
|
macho = lief.MachO.parse(get_sample("MachO/RNCryptor.bin")).at(0)
|
|
assert macho.encryption_info.crypt_offset != 0
|
|
macho.encryption_info.crypt_offset = 0
|
|
macho.encryption_info.crypt_size = 0
|
|
macho.encryption_info.crypt_id = 0
|
|
|
|
output = tmp_path / "new.macho"
|
|
macho.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
assert new.encryption_info.crypt_id == 0
|
|
assert new.encryption_info.crypt_size == 0
|
|
assert new.encryption_info.crypt_offset == 0
|
|
|
|
|
|
def test_issue_1206(tmp_path: Path):
|
|
# c.f. https://github.com/lief-project/LIEF/issues/1206
|
|
# c.f. https://github.com/lief-project/LIEF/issues/1173
|
|
macho = lief.MachO.parse(get_sample("MachO/issue_1206.bin")).at(0)
|
|
|
|
output = tmp_path / "new.macho"
|
|
macho.write(output)
|
|
|
|
new = lief.MachO.parse(output).at(0)
|
|
assert lief.MachO.check_layout(new)[0]
|
|
|
|
def test_issue_1204(tmp_path: Path):
|
|
macho = lief.MachO.parse(get_sample("MachO/lief-dwarf-plugin-darwin-arm64.dylib")).at(0)
|
|
macho.rpath.path += '/a/very/long/path/that/needs/expansion'
|
|
out = tmp_path / "out.macho"
|
|
macho.write(out)
|
|
|
|
new = lief.MachO.parse(out).at(0)
|
|
lief.MachO.check_layout(new)
|
|
assert new.rpath.path == '@loader_path/../a/very/long/path/that/needs/expansion'
|
|
|
|
macho = lief.MachO.parse(get_sample("MachO/lief-dwarf-plugin-darwin-arm64.dylib")).at(0)
|
|
macho.rpath.path += '/a/very/long/path/that/needs/expansion/' + 'a' * (macho.available_command_space + 10)
|
|
rpath = macho.rpath.path
|
|
out = tmp_path / "out2.macho"
|
|
macho.write(out)
|
|
|
|
new = lief.MachO.parse(out).at(0)
|
|
lief.MachO.check_layout(new)
|
|
assert new.rpath.path == rpath
|
|
|
|
def test_issue_1236(tmp_path: Path):
|
|
macho = lief.MachO.parse(get_sample("MachO/libmamba.4.0.1.dylib")).at(0)
|
|
|
|
checked, err = lief.MachO.check_layout(macho)
|
|
assert checked, err
|
|
|
|
for cmd in macho.commands:
|
|
if isinstance(cmd, lief.MachO.DylibCommand):
|
|
cmd.name = "/Users/random" + cmd.name
|
|
|
|
output = tmp_path / "out.macho"
|
|
macho.write(output)
|
|
new = lief.MachO.parse(output)
|
|
|
|
checked, err = lief.MachO.check_layout(new)
|
|
assert checked, err
|