Skip to content

Patcherex

Patcherex

The main class of the library. This is how you are intended to interact with patches.

Source code in src/patcherex2/patcherex.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
class Patcherex:
    """
    The main class of the library. This is how you are intended to interact with patches.
    """

    def __init__(
        self,
        binary_path: str,
        target_cls: type[Target] | None = None,
        target_opts: dict[str, str] | None = None,
        components_opts: dict[str, dict[str, str]] | None = None,
    ) -> None:
        """
        Constructor.

        :param binary_path: The path of the binary to patch.
        :param target_cls: Specified architecture class to use, otherwise it is automatically detected, defaults to None
        :param target_opts: Options to specify components for the target, defaults to None
        :param components_opts: Options for configuring each component for the target, defaults to None
        """
        if target_opts is None:
            target_opts = {}
        if components_opts is None:
            components_opts = {}
        self.binary_path = binary_path
        if target_cls is None:
            self.target = Target.detect_target(self, binary_path)
        else:
            self.target = target_cls(self, binary_path)

        self.symbols = {}
        self.sypy_info = {"patcherex_added_functions": []}
        self.patches = []

        # Initialize components
        components = [
            "assembler",
            "disassembler",
            "compiler",
            "binary_analyzer",
            "allocation_manager",
            "binfmt_tool",
            "utils",
            "archinfo",
        ]
        for component in components:
            setattr(
                self,
                component,
                self.target.get_component(
                    component,
                    target_opts.get(component),
                    components_opts.get(component),
                ),
            )

        # Chosen patch order, making sure all are accounted for
        self.patch_order = (
            ModifyRawBytesPatch,
            RemoveDataPatch,
            InsertDataPatch,
            ModifyDataPatch,
            RemoveLabelPatch,
            ModifyLabelPatch,
            InsertLabelPatch,
            RemoveInstructionPatch,
            InsertInstructionPatch,
            ModifyInstructionPatch,
            RemoveFunctionPatch,
            InsertFunctionPatch,
            ModifyFunctionPatch,
        )
        assert len(self.patch_order) == len(all_patches)

    def apply_patches(self) -> None:
        """
        Applies all added patches to the binary. Call this when you have added all the patches you want.
        """
        # TODO: sort patches properly
        # self.patches.sort(key=lambda x: self.patch_order.index(type(x)))
        self.patches.sort(
            key=lambda x: not isinstance(
                x, (ModifyDataPatch, InsertDataPatch, RemoveDataPatch)
            )
        )
        logger.debug(f"Applying patches: {self.patches}")
        for patch in self.patches:
            patch.apply(self)
        self.binfmt_tool.finalize()

    def save_binary(self, filename: str = None) -> None:
        """
        Save the patched binary to a file.

        :param filename: Name of file to save to, defaults to None
        """
        logger.warning(
            "p.save_binary() is deprecated, use p.binfmt_tool.save_binary() instead."
        )
        self.binfmt_tool.save_binary(filename)

__init__(binary_path, target_cls=None, target_opts=None, components_opts=None)

Constructor.

Parameters:

Name Type Description Default
binary_path str

The path of the binary to patch.

required
target_cls type[Target] | None

Specified architecture class to use, otherwise it is automatically detected, defaults to None

None
target_opts dict[str, str] | None

Options to specify components for the target, defaults to None

None
components_opts dict[str, dict[str, str]] | None

Options for configuring each component for the target, defaults to None

None
Source code in src/patcherex2/patcherex.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def __init__(
    self,
    binary_path: str,
    target_cls: type[Target] | None = None,
    target_opts: dict[str, str] | None = None,
    components_opts: dict[str, dict[str, str]] | None = None,
) -> None:
    """
    Constructor.

    :param binary_path: The path of the binary to patch.
    :param target_cls: Specified architecture class to use, otherwise it is automatically detected, defaults to None
    :param target_opts: Options to specify components for the target, defaults to None
    :param components_opts: Options for configuring each component for the target, defaults to None
    """
    if target_opts is None:
        target_opts = {}
    if components_opts is None:
        components_opts = {}
    self.binary_path = binary_path
    if target_cls is None:
        self.target = Target.detect_target(self, binary_path)
    else:
        self.target = target_cls(self, binary_path)

    self.symbols = {}
    self.sypy_info = {"patcherex_added_functions": []}
    self.patches = []

    # Initialize components
    components = [
        "assembler",
        "disassembler",
        "compiler",
        "binary_analyzer",
        "allocation_manager",
        "binfmt_tool",
        "utils",
        "archinfo",
    ]
    for component in components:
        setattr(
            self,
            component,
            self.target.get_component(
                component,
                target_opts.get(component),
                components_opts.get(component),
            ),
        )

    # Chosen patch order, making sure all are accounted for
    self.patch_order = (
        ModifyRawBytesPatch,
        RemoveDataPatch,
        InsertDataPatch,
        ModifyDataPatch,
        RemoveLabelPatch,
        ModifyLabelPatch,
        InsertLabelPatch,
        RemoveInstructionPatch,
        InsertInstructionPatch,
        ModifyInstructionPatch,
        RemoveFunctionPatch,
        InsertFunctionPatch,
        ModifyFunctionPatch,
    )
    assert len(self.patch_order) == len(all_patches)

apply_patches()

Applies all added patches to the binary. Call this when you have added all the patches you want.

Source code in src/patcherex2/patcherex.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def apply_patches(self) -> None:
    """
    Applies all added patches to the binary. Call this when you have added all the patches you want.
    """
    # TODO: sort patches properly
    # self.patches.sort(key=lambda x: self.patch_order.index(type(x)))
    self.patches.sort(
        key=lambda x: not isinstance(
            x, (ModifyDataPatch, InsertDataPatch, RemoveDataPatch)
        )
    )
    logger.debug(f"Applying patches: {self.patches}")
    for patch in self.patches:
        patch.apply(self)
    self.binfmt_tool.finalize()

save_binary(filename=None)

Save the patched binary to a file.

Parameters:

Name Type Description Default
filename str

Name of file to save to, defaults to None

None
Source code in src/patcherex2/patcherex.py
103
104
105
106
107
108
109
110
111
112
def save_binary(self, filename: str = None) -> None:
    """
    Save the patched binary to a file.

    :param filename: Name of file to save to, defaults to None
    """
    logger.warning(
        "p.save_binary() is deprecated, use p.binfmt_tool.save_binary() instead."
    )
    self.binfmt_tool.save_binary(filename)