Summary
On ARMv7 (ARM mode), gum_interceptor_replace() sometimes produces a broken trampoline when the overwritten prologue contains a PC-relative conditional branch (e.g. beq). The branch instruction appears to be copied into the trampoline without relocating/fixing up its target, so the branch target becomes incorrect when executed from the trampoline address.
GumReplaceReturn gum_interceptor_replace (GumInterceptor * self,
gpointer function_address, gpointer replacement_function,
gpointer replacement_data, gpointer * original_function);
If the original function’s prologue includes a PC-relative beq, the trampoline’s copy of that instruction branches to the wrong target (because it’s now running from a different address). This can lead to incorrect control-flow / crashes.
Reproduction notes
- Hook created with
gum_interceptor_replace()
function_address redirects correctly to replacement_function
- Calling
original_function executes the trampoline
- Trampoline does not correctly handle relocation of PC-relative conditional branches in the copied prologue
Original function (ARM, disassembly)
000002d8 <BEF_RIO_EBD1318_ReadDin>:
2d8: e3500000 cmp r0, #0
2dc: 0a000012 beq 32c <BEF_RIO_EBD1318_ReadDin+0x54>
2e0: e92d4070 push {r4, r5, r6, lr}
2e4: e1a05002 mov r5, r2
2e8: e5902004 ldr r2, [r0, #4]
2ec: e3a03001 mov r3, #1
2f0: e1a03113 lsl r3, r3, r1
2f4: e1a04001 mov r4, r1
2f8: e1d33002 bics r3, r3, r2
2fc: 0a000007 beq 320 <BEF_RIO_EBD1318_ReadDin+0x48>
300: e5900000 ldr r0, [r0]
304: ebfffffe bl 0 <ebd1318_get_input>
308: e7eb3050 ubfx r3, r0, #0, #12
30c: e3a00000 mov r0, #0
310: e1a03453 asr r3, r3, r4
314: e2033001 and r3, r3, #1
318: e5c53000 strb r3, [r5]
31c: e8bd8070 pop {r4, r5, r6, pc}
320: e30b05c4 movw r0, #46532 ; 0xb5c4
324: e34f0fff movt r0, #65535 ; 0xffff
328: e8bd8070 pop {r4, r5, r6, pc}
32c: e3e0000d mvn r0, #13
330: e12fff1e bx lr
Trampoline (disassembly)
BEF_RIO_EBD1318_ReadDin:
0x00000000b6ef0924: 00 00 50 E3 cmp r0, #0
0x00000000b6ef0928: 00 F0 9F E5 ldr pc, [pc]
0x00000000b6ef092c: 00 F0 9F E5 ldr pc, [pc]
0x00000000b67aa90c: 70 D7 14 00 address: 0x14d770
in my binary BEF_RIO_EBD1318_ReadDin was located at 0x14d71c so jump to 0x14d770 is an offset of 0x54 the error branch of the original function.
Why this looks wrong
beq encodes a PC-relative offset. If it’s copied into a trampoline at a different address (without rewriting it to an absolute/relocated form), the branch destination changes. The trampoline needs to relocate conditional branches (e.g. transform into an inverted conditional + absolute jump, or similar).
Environment
-
CPU: Cortex-A9, ARMv7l, Little Endian (ARM mode, not Thumb)
-
OS: Linux 5.10
-
frida-gum: version 17.7.2
Summary
On ARMv7 (ARM mode),
gum_interceptor_replace()sometimes produces a broken trampoline when the overwritten prologue contains a PC-relative conditional branch (e.g.beq). The branch instruction appears to be copied into the trampoline without relocating/fixing up its target, so the branch target becomes incorrect when executed from the trampoline address.If the original function’s prologue includes a PC-relative
beq, the trampoline’s copy of that instruction branches to the wrong target (because it’s now running from a different address). This can lead to incorrect control-flow / crashes.Reproduction notes
gum_interceptor_replace()function_addressredirects correctly toreplacement_functionoriginal_functionexecutes the trampolineOriginal function (ARM, disassembly)
Trampoline (disassembly)
in my binary BEF_RIO_EBD1318_ReadDin was located at 0x14d71c so jump to 0x14d770 is an offset of 0x54 the error branch of the original function.
Why this looks wrong
beqencodes a PC-relative offset. If it’s copied into a trampoline at a different address (without rewriting it to an absolute/relocated form), the branch destination changes. The trampoline needs to relocate conditional branches (e.g. transform into an inverted conditional + absolute jump, or similar).Environment
CPU: Cortex-A9, ARMv7l, Little Endian (ARM mode, not Thumb)
OS: Linux 5.10
frida-gum: version 17.7.2