PatchMaker troubleshooting guide
The PatchMaker is a powerful tool that can be used to inject code in an existing binary, using assembly or C source code. The PatchMaker uses a selected toolchain to compile/assemble source code/assembly into bytes, to be injected in the target binary.
During this process, it is possible to encounter assembler/compiler/linker errors in the corresponding OFRAK log. This troubleshooting guide describes common errors and how to address them.
Missing function definition and symbol
Error:
/path/to/linker-ld: /path/to/obj_file.c.o: in function `function_name':
/path/to/source.c:39: undefined reference to `printf'
- find the address of that function in the binary, for example through reverse engineering
- define the function's prototype, for example:
extern void printf(const char *restrict format, ...);
- define a symbol for the function, then pass it to the PatchMaker in the
base_symbols
field, for example like this:patch_maker = PatchMaker([...], base_symbols=["printf": 0x1234],)
Missing compiler builtins or libraries
Users might want to use math operators in C code, like divide or modulo operators, which may rely on compiler builtins. Users might also want to use commonly used standard functions which come from libraries.
In this section we take the example of math operators.
Error:
/path/to/linker-ld: /path/to/obj_file.c.o: in function `function_name':
/path/to/source.c:14: undefined reference to `__aeabi_uidivmod'
__aeabi_uidivmod
or __aeabi_idivmod
if compiling
with GCC or __umodsi3
if compiling with Clang.
To address this, find the function in the binary and define a symbol for it, then pass it to the PatchMaker in the base_symbols
field. Note that in the math operators case, there is no need to define the function's prototype like in the above section, as those types of functions are compiler builtins.
Note that the PatchMaker implementation doesn't yet allow linking against libraries.
Multiple symbol definitions
Error:
/path/to/linker-ld: /path/to/obj_file.c.o: in function `function_name':
(.text+0x0): multiple definition of `function_name'; /path/to/obj_file.c.o:/path/to/source.c:26: first defined here
base_symbols
field. In case such as these, the symbol should only be defined in the source, and
not passed to the PatchMaker.
Patch doesn't fit in patch range
Error:
/path/to/linker-ld: address 0xabcd of /path/to/FEM_executable section `.rbs_function_name_text' is not within region `.rbs_function_name_text_mem'
# or:
/path/to/linker-ld: /path/to/FEM_executable section `.text' will not fit in region `.rbs_name_text_mem'
# or:
/path/to/linker-ld: region `.region_name_text_mem' overflowed by 2 bytes
segments
field of
the PatchRegionConfig
).
There are three approaches for addressing this:
- if it's not already the case, use
CompilerOptimizationLevel.SPACE,
for thecompiler_optimization_level
field of theToolchainConfig
. - create a bigger segment for the patch, if additional space is available
- consider extending the binary to make as much space as needed for the patch. An example is OFRAK example 7.
Use of read-only data
Error:
/path/to/linker-ld: /path/to/FEM_executable section `.rodata' will not fit in region `.rbs_region_name_text_mem'
A common case for this is using strings. If the target string is already available in the binary,
consider using a pointer to it instead of re-defining the string. The string can be defined like
this: extern char string1;
, and then used like this: printf (&string1);
. Finally, a symbol must be
defined for it and passed to the PatchMaker
in the base_symbols
field.
Alternatively, a read-only segment part of the segments
field of the PatchRegionConfig
can be
defined. Note that additional space will be required for that segment.
Use of global variables
Error:
ofrak_patch_maker.model.PatchMakerException: .bss found but `no_bss_section` is set in the provided ToolchainConfig!
no_bss_section=True
is set in the ToolchainConfig
, but the code still
employs uninitialized data. This is usually a result of one or more uninitialized global variable.
If that global variable was already present in the binary and needs to be reused it, a user can
find its address, provide it as a symbol to the PatchMaker
in the base_symbols
field, and define
it in the C code as extern: extern uint8_t variable_name;
.
Alternatively, a .bss
segment can be defined, and passed to make_fem
in the unsafe_bss_segment
argument. Note that additional space will be required for that segment.
Another error can be:
/path/to/linker-ld: warning: start of section .igot.plt changed by 1
/path/to/linker-ld: /path/to/FEM_executable section `.data' will not fit in region `.rbs_region_name_data_mem'
.data
readable and writable (RW)
section. There are two approaches for addressing this:
- reuse the variable in the binary, if present, as described above.
- define a read-write segment part of the
segments
field of thePatchRegionConfig
. Note that additional space will be required for that segment.
Linker errors
In the case of linker errors, helpful troubleshooting approaches are:
- find the linker command line in the OFRAK log, then:
- inspect the generated linker script (
-T/path/to/linker_script.ld
in the command line). Check if the addresses, sizes, alignments match the expected values. - inspect the map file (
-Map /path/to/map_file.map
in the command line). Check if things land where they're expected to, compared to the linker script. - inspect the linker options to check if they are the expected ones
- inspect the generated linker script (
If anything strange stands up during the above inspection, use the toolchains directly to diagnose what the PatchMaker needs (fixups in the linker script generation, change in the command line arguments, etc.), and then fix it in the PatchMaker setup/usage.
General troubleshooting tips:
- the standalone assembler/compiler/linker commands can be run on the command line manually while experimenting, avoiding running the full OFRAK script
- different toolchains will handle compilation differently, and unless prohibited by the use case, it may be helpful to change the employed toolchain to further triage the error (or even get past it)