https://mp.weixin.qq.com/s/-WNNaAyUetiP5UWC5Re0SQ
Step 1 – Original Program Instrumentation
LDR X0, [TARGET_ADDRESS]
BR X0
STP X1, X0, [SP, #-0x10]
LDR X0, [TARGET_ADDRESS]
BR X0
STP X1, X0, [SP, #-0x10]
LDR X0, [TARGET_ADDRESS] ; TARGET_ADDRESS is a dynamic value, more instructions will be needed after compilation to achieve this
BR X0
LDR X0, [SP, -0x8]
Step 2 – Hook Program
sub sp, sp, #0x20 ; sp = sp - 0x20, moving the pointer up by 2
mrs x0, NZCV ; store the status register value in x0
str x0, [sp, #0x10] ; store the x0 (status register) value at sp + 0x10, which is the PSR in the diagram
str x30, [sp] ; store the x30 value at sp, which is the X30 LR in the diagram
add x30, sp, #0x20 ; X30 = sp + 0x20
str x30, [sp, #0x8] ; store the value at sp + 0x20 at sp + 0x8, which is the original sp value in the diagram
ldr x0, [sp, #0x18] ; set x0 to point to the stack top
sub sp, sp, #0xf0 ; allocate space to store X0 - X29 registers
stp X0, X1, [SP]
stp X2, X3, [SP,#0x10]
stp X4, X5, [SP,#0x20]
stp X6, X7, [SP,#0x30]
stp X8, X9, [SP,#0x40]
stp X10, X11, [SP,#0x50]
stp X12, X13, [SP,#0x60]
stp X14, X15, [SP,#0x70]
stp X16, X17, [SP,#0x80]
stp X18, X19, [SP,#0x90]
stp X20, X21, [SP,#0xa0]
stp X22, X23, [SP,#0xb0]
stp X24, X25, [SP,#0xc0]
stp X26, X27, [SP,#0xd0]
stp X28, X29, [SP,#0xe0]
ldr x0, [sp, #0x100] ; assign the address storing PSR to X0
msr NZCV, x0 ; restore the value of the status register
ldp X0, X1, [SP] ; restore the values of X0 and X1
ldp X2, X3, [SP,#0x10]
ldp X4, X5, [SP,#0x20]
ldp X6, X7, [SP,#0x30]
ldp X8, X9, [SP,#0x40]
ldp X10, X11, [SP,#0x50]
ldp X12, X13, [SP,#0x60]
ldp X14, X15, [SP,#0x70]
ldp X16, X17, [SP,#0x80]
ldp X18, X19, [SP,#0x90]
ldp X20, X21, [SP,#0xa0]
ldp X22, X23, [SP,#0xb0]
ldp X24, X25, [SP,#0xc0]
ldp X26, X27, [SP,#0xd0]
ldp X28, X29, [SP,#0xe0]
add sp, sp, #0xf0 ; set sp to point to X30 LR location
ldr x30, [sp] ; restore X30 register
add sp, sp, #0x20 ; restore the sp value, origin_sp is unused
ldr x0, ret_addr
br x0
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <dlfcn.h>
int main()
{
void *handle = dlopen("libc.so", RTLD_NOW);
void *hook_addr = dlsym(handle, "fopen");
printf("hook_addr = %p\n", hook_addr);
return 0;
}
sailfish:/data/local/tmp # ./inlinehook
hook_addr = 0x724f5433d4
void __attribute__((naked)) hook_func()
{
}
mprotect((void *)((uint64_t)hook_addr & 0xfffffffffffff000), 0x1000, PROT_WRITE | PROT_EXEC | PROT_READ);
7454e64000-7454edc000 r-xp 00000000 103:12 946 /system/lib64/libc.so
7454edc000-7454edd000 rwxp 00078000 103:12 946 /system/lib64/libc.so
7454edd000-7454f2c000 r-xp 00079000 103:12 946 /system/lib64/libc.so
LDR pc, [pc-4]
hook_func_addr
LDR X0, [TARGET_ADDRESS]
BR X0
LDR X0, [SP, -0x8]
STP X1, X0, [SP, #-0x10] ; store x0, x1
LDR X0, 8 ; load the content at the address pc + 8 into x0
BR X0 ; jump to the address corresponding to x0
ADDR(64) ; target address
LDR X0, [SP, -0x8] ; set x0 to point to the stack top
LDR X0, .+8
// Instructions are 4 bytes, using uint32, addresses use unit64
// STP X8, X0, [SP, #-0x60] -> E8 03 3A A9
(*(uint32_t *)(hook_addr + 0)) = 0xA93A03E8;
// LDR X0, 8 -> 40 00 00 58
(*(uint32_t *)(hook_addr + 4)) = 0x58000040;
// BR X0 -> 00 00 1f d6
(*(uint32_t *)(hook_addr + 8)) = 0xd61f0000;
// ADDR -> 00 00 1f d6, here needs to be 64-bit
(*(uint64_t *)(hook_addr + 12)) = hook_func;
// The overwritten instruction operated on sp, so we need to calculate the sp value (sp - 0x60 + 0x50)
// LDR X0, [SP, #-0x10] -> E0 03 5F F8
(*(uint32_t *)(hook_addr + 20)) = 0xF85F03E0;
-
We stored X8 and X0 at the position sp – 0x60 because the overwritten fopen instruction would operate on sp. Its function stack size is 0x50, so I placed X8 and X0 in a position that fopen cannot reach, to avoid losing our stored data due to the overwritten instruction modifying stack data. -
Store X8 because later, when generating the instruction to jump back, GCC uses X8. -
Store X0 because we use the X0 register for jumping, so it needs to be saved and restored later.
void __attribute__((naked)) hook_func()
{
// Get parameters
asm("ldr x0, [sp, #-0x58]");
asm("str x0,%0":"=m"(x0));
// Execute the overwritten instructions
// .text:00000000000783D4 FF 43 01 D1 SUB SP, SP, #0x50 ; Alternative name is 'fopen'
// .text:00000000000783D8 F7 0B 00 F9 STR X23, [SP,#0x10]
// .text:00000000000783DC F6 57 02 A9 STP X22, X21, [SP,#0x20]
// .text:00000000000783E0 F4 4F 03 A9 STP X20, X19, [SP,#0x30]
// .text:00000000000783E4 FD 7B 04 A9 STP X29, X30, [SP,#0x40]
// .text:00000000000783E8 FD 03 01 91 ADD X29, SP, #0x40
asm("SUB SP, SP, #0x50");
asm("STR X23, [SP,#0x10]");
asm("STP X22, X21, [SP,#0x20]");
asm("STP X20, X19, [SP,#0x30]");
asm("STP X29, X30, [SP,#0x40]");
asm("ADD X29, SP, #0x40");
// Jump to return address; this statement generates assembly that uses the x8 register
asm("ldr x0, %0" : :"m"(hook_return_addr));
// Restore the x8 register value
asm("ldr x8, [sp, #-0x8]");
asm("br x0");
}
-
Get the value of X0, as X0 is the first parameter; we can assign X0 to a global variable and print it to check if the hook was successful. -
Execute the overwritten instructions, which is straightforward; just copy the first six instructions of fopen. -
After execution, we need to jump back to fopen to continue executing; we first calculate the instruction address, then use inline assembly to generate the corresponding instructions.
.global _shellcode_start_s
_shellcode_start_s:
sub sp, sp, #0x20
extern unsigned long _shellcode_start_s;
void *p_shellcode_start_s = &_shellcode_start_s;
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <dlfcn.h>
static uint64_t hook_return_addr;
static uint64_t x0, x1;
void __attribute__((naked)) hook_func()
{
// Get parameters
asm("ldr x0, [sp, #-0x58]");
asm("str x0,%0" : "=m"(x0));
asm("str x1,%0" : "=m"(x1));
// Execute the overwritten instructions
// .text:00000000000783D4 FF 43 01 D1 SUB SP, SP, #0x50 ; Alternative name is 'fopen'
// .text:00000000000783D8 F7 0B 00 F9 STR X23, [SP,#0x10]
// .text:00000000000783DC F6 57 02 A9 STP X22, X21, [SP,#0x20]
// .text:00000000000783E0 F4 4F 03 A9 STP X20, X19, [SP,#0x30]
// .text:00000000000783E4 FD 7B 04 A9 STP X29, X30, [SP,#0x40]
// .text:00000000000783E8 FD 03 01 91 ADD X29, SP, #0x40
// .text:00000000000783EC 56 D0 3B D5 MRS X22, #3, c13, c0, #2
// .text:00000000000783F0 C9 16 40 F9 LDR X9, [X22,#0x28]
asm("SUB SP, SP, #0x50");
asm("STR X23, [SP,#0x10]");
asm("STP X22, X21, [SP,#0x20]");
asm("STP X20, X19, [SP,#0x30]");
asm("STP X29, X30, [SP,#0x40]");
asm("ADD X29, SP, #0x40");
// Jump to return address; this statement generates assembly that uses the x8 register
asm("ldr x0, %0" : :"m"(hook_return_addr));
// Restore the x8 register value
asm("ldr x8, [sp, #-0x8]");
asm("br x0");
}
int main()
{
void *handle = dlopen("libc.so", RTLD_NOW);
void *hook_addr = dlsym(handle, "fopen");
if (hook_addr != NULL)
{
hook_return_addr = hook_addr + 20;
}
else
{
return -1;
}
printf("hook_addr = %p\n", hook_addr);
// Here we change the properties of the data in the range 0x1000
mprotect((void *)((uint64_t)hook_addr & 0xfffffffffffff000), 0x1000, PROT_WRITE | PROT_EXEC | PROT_READ);
// getchar();
// Instructions are 4 bytes, using uint32, addresses use unit64
// STP X8, X0, [SP, #-0x60] -> E8 03 3A A9
(*(uint32_t *)(hook_addr + 0)) = 0xA93A03E8;
// LDR X0, 8 -> 40 00 00 58
(*(uint32_t *)(hook_addr + 4)) = 0x58000040;
// BR X0 -> 00 00 1f d6
(*(uint32_t *)(hook_addr + 8)) = 0xd61f0000;
// ADDR -> 00 00 1f d6, here needs to be 64-bit
(*(uint64_t *)(hook_addr + 12)) = hook_func;
// The overwritten instruction operated on sp, so we need to restore
// LDR X0, [SP, #-0x10] -> E0 03 5F F8
(*(uint32_t *)(hook_addr + 20)) = 0xF85F03E0;
printf("hook_func = %p\n", hook_func);
getchar();
FILE *fp = fopen("/data/local/tmp/android_server64", "rb");
uint32_t data;
fread(&data, 4, 1, fp);
fclose(fp);
printf("data = %p\n", data);
printf("x0 = %s\n", x0);
printf("x1 = %s\n", x1);
return 0;
}
sailfish:/data/local/tmp # ./inlinehook
hook_addr = 0x7c1015a3d4
hook_func = 0x5d3d3099d4
data = 0x464c457f
x0 = /data/local/tmp/android_server64
x1 = rb
-
data is the first 4 bytes of the so file, which is .elf -
x0 and x1 are parameters
Leave a Comment
Your email address will not be published. Required fields are marked *