Skip to content

Commit 89881ae

Browse files
committed
added tiny_interpose
1 parent 7e9fb8a commit 89881ae

File tree

8 files changed

+133
-27
lines changed

8 files changed

+133
-27
lines changed

.clang-format

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ Language: Cpp
77
# Force pointers to the type for C++.
88
ColumnLimit: 120
99
AlignConsecutiveMacros: AcrossEmptyLines
10-
AllowShortFunctionsOnASingleLine: None
11-
...
10+
AllowShortFunctionsOnASingleLine: false
11+
AllowShortIfStatementsOnASingleLine: true
12+
BreakBeforeBraces: Custom
13+
BraceWrapping:
14+
BeforeElse: true

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ int tiny_hook_ex(th_bak_t *bak, void *function, void *destination, void **origin
2121

2222
int tiny_unhook_ex(th_bak_t *bak);
2323

24-
/*
25-
insert a function call (bl / call) at `address`, auto select far or near
26-
*/
24+
/* interpose a symbol in a given image */
25+
int tiny_interpose(uint32_t image_index, const char *symbol_name, void *replacement);
26+
27+
/* insert a function call (bl / call) at `address`, auto select far or near */
2728
int tiny_insert(void *address, void *destination);
2829
```
2930

include/tinyhook.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ int tiny_unhook_ex(th_bak_t *bak);
3131

3232
int tiny_insert(void *address, void *destination);
3333

34+
/* interpose */
35+
int tiny_interpose(uint32_t image_index, const char *symbol_name, void *replacement);
36+
3437
/* objective-c runtime */
3538
int ocrt_hook(const char *cls, const char *sel, void *destination, void **origin);
3639

src/interpose.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include "../include/tinyhook.h"
2+
#include "private.h"
3+
4+
#include <mach-o/dyld.h>
5+
#include <mach-o/loader.h>
6+
#include <mach-o/nlist.h>
7+
#include <mach/mach_init.h> // mach_task_self()
8+
#include <mach/mach_vm.h> // mach_vm_*
9+
#include <stdint.h>
10+
#include <string.h>
11+
12+
static int update_pointer(struct section_64 *sym_sec, intptr_t slide, char *str_tbl, struct nlist_64 *nl_tbl,
13+
uint32_t *indirect_sym_tbl, const char *symbol_name, void *replacement) {
14+
if (!sym_sec) return 1;
15+
void **sym_ptrs = (void *)sym_sec->addr + slide;
16+
uint32_t *indirect_sym_entry = indirect_sym_tbl + sym_sec->reserved1; // reserved1 is an index!
17+
int nptrs = sym_sec->size / sizeof(void *);
18+
for (int i = 0; i < nptrs; i++) {
19+
uint32_t symtab_index = indirect_sym_entry[i];
20+
if (symtab_index == INDIRECT_SYMBOL_LOCAL || symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
21+
continue;
22+
}
23+
if (strcmp(symbol_name, str_tbl + nl_tbl[symtab_index].n_un.n_strx) == 0) {
24+
int kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)sym_ptrs, sym_sec->size, FALSE,
25+
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
26+
if (kr != 0) return 1;
27+
sym_ptrs[i] = replacement;
28+
return 0;
29+
}
30+
}
31+
return 1;
32+
}
33+
34+
int tiny_interpose(uint32_t image_index, const char *symbol_name, void *replacement) {
35+
intptr_t image_slide = _dyld_get_image_vmaddr_slide(image_index);
36+
struct mach_header_64 *mh_header = (struct mach_header_64 *)_dyld_get_image_header(image_index);
37+
struct load_command *ld_command = (void *)mh_header + sizeof(struct mach_header_64);
38+
struct section_64 *nl_sym_sect = NULL;
39+
struct section_64 *la_sym_sect = NULL;
40+
struct symtab_command *symtab_cmd = NULL;
41+
struct dysymtab_command *dysymtab_cmd = NULL;
42+
struct segment_command_64 *linkedit_cmd = NULL;
43+
for (int i = 0; i < mh_header->ncmds; i++) {
44+
if (ld_command->cmd == LC_SEGMENT_64) {
45+
const struct segment_command_64 *segment = (struct segment_command_64 *)ld_command;
46+
if (strcmp(segment->segname, "__DATA_CONST") == 0) {
47+
if (nl_sym_sect) continue;
48+
struct section_64 *data_const_sect = (void *)(segment + 1);
49+
for (int i = 0; i < segment->nsects; i++) {
50+
if ((data_const_sect[i].flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
51+
nl_sym_sect = data_const_sect + i;
52+
}
53+
}
54+
}
55+
else if (strcmp(segment->segname, "__DATA") == 0) {
56+
if (la_sym_sect) continue;
57+
struct section_64 *data_sect = (void *)(segment + 1);
58+
for (int i = 0; i < segment->nsects; i++) {
59+
if ((data_sect[i].flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
60+
la_sym_sect = data_sect + i;
61+
}
62+
}
63+
}
64+
else if (strcmp(segment->segname, "__LINKEDIT") == 0) {
65+
linkedit_cmd = (struct segment_command_64 *)ld_command;
66+
}
67+
}
68+
else if (ld_command->cmd == LC_SYMTAB) {
69+
symtab_cmd = (struct symtab_command *)ld_command;
70+
}
71+
else if (ld_command->cmd == LC_DYSYMTAB) {
72+
dysymtab_cmd = (struct dysymtab_command *)ld_command;
73+
}
74+
ld_command = (void *)ld_command + ld_command->cmdsize;
75+
}
76+
intptr_t linkedit_base = image_slide + linkedit_cmd->vmaddr - linkedit_cmd->fileoff;
77+
char *str_tbl = (void *)linkedit_base + symtab_cmd->stroff;
78+
struct nlist_64 *nl_tbl = (void *)linkedit_base + symtab_cmd->symoff;
79+
uint32_t *indirect_sym_tbl = (void *)linkedit_base + dysymtab_cmd->indirectsymoff;
80+
81+
if ((update_pointer(nl_sym_sect, image_slide, str_tbl, nl_tbl, indirect_sym_tbl, symbol_name, replacement) != 0) &&
82+
(update_pointer(la_sym_sect, image_slide, str_tbl, nl_tbl, indirect_sym_tbl, symbol_name, replacement) != 0)) {
83+
LOG_ERROR("tiny_interpose: no matching indirect symbol found!");
84+
return 1;
85+
}
86+
return 0;
87+
}

src/symsolve/symexport.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ static void *trie_query(const uint8_t *export, const char *name) {
3636
}
3737
}
3838
break;
39-
} else {
39+
}
40+
else {
4041
go_child = false;
4142
cur_pos = child_off;
4243
uint8_t child_count = *(uint8_t *)cur_pos++;
@@ -73,7 +74,8 @@ void *symexp_solve(uint32_t image_index, const char *symbol_name) {
7374
if (strcmp(segment->segname, "__LINKEDIT") == 0) {
7475
linkedit_cmd = (struct segment_command_64 *)ld_command;
7576
}
76-
} else if (ld_command->cmd == LC_DYLD_INFO_ONLY || ld_command->cmd == LC_DYLD_INFO) {
77+
}
78+
else if (ld_command->cmd == LC_DYLD_INFO_ONLY || ld_command->cmd == LC_DYLD_INFO) {
7779
dyldinfo_cmd = (struct dyld_info_command *)ld_command;
7880
if (linkedit_cmd != NULL) {
7981
break;
@@ -87,13 +89,14 @@ void *symexp_solve(uint32_t image_index, const char *symbol_name) {
8789
}
8890
// stroff and strtbl are in the __LINKEDIT segment
8991
// Its offset will change when loaded into the memory, so we need to add this slide
90-
intptr_t linkedit_slide = linkedit_cmd->vmaddr - linkedit_cmd->fileoff;
91-
uint8_t *export_offset = (uint8_t *)image_slide + linkedit_slide + dyldinfo_cmd->export_off;
92+
intptr_t linkedit_base = image_slide + linkedit_cmd->vmaddr - linkedit_cmd->fileoff;
93+
uint8_t *export_offset = (uint8_t *)linkedit_base + dyldinfo_cmd->export_off;
9294
symbol_address = trie_query(export_offset, symbol_name);
9395

9496
if (symbol_address != NULL) {
9597
symbol_address += image_slide;
96-
} else {
98+
}
99+
else {
97100
LOG_ERROR("symexp_solve: symbol not found!");
98101
}
99102
return symbol_address;

src/symsolve/symtable.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ void *symtbl_solve(uint32_t image_index, const char *symbol_name) {
2222
if (strcmp(segment->segname, "__LINKEDIT") == 0) {
2323
linkedit_cmd = (struct segment_command_64 *)ld_command;
2424
}
25-
} else if (ld_command->cmd == LC_SYMTAB) {
25+
}
26+
else if (ld_command->cmd == LC_SYMTAB) {
2627
symtab_cmd = (struct symtab_command *)ld_command;
2728
if (linkedit_cmd != NULL) {
2829
break;
@@ -32,9 +33,9 @@ void *symtbl_solve(uint32_t image_index, const char *symbol_name) {
3233
}
3334
// stroff and strtbl are in the __LINKEDIT segment
3435
// Its offset will change when loaded into the memory, so we need to add this slide
35-
intptr_t linkedit_slide = linkedit_cmd->vmaddr - linkedit_cmd->fileoff;
36-
struct nlist_64 *nl_tbl = (void *)image_slide + linkedit_slide + symtab_cmd->symoff;
37-
char *str_tbl = (void *)image_slide + linkedit_slide + symtab_cmd->stroff;
36+
intptr_t linkedit_base = image_slide + linkedit_cmd->vmaddr - linkedit_cmd->fileoff;
37+
struct nlist_64 *nl_tbl = (void *)linkedit_base + symtab_cmd->symoff;
38+
char *str_tbl = (void *)linkedit_base + symtab_cmd->stroff;
3839
for (int j = 0; j < symtab_cmd->nsyms; j++) {
3940
if ((nl_tbl[j].n_type & N_TYPE) == N_SECT) {
4041
if (strcmp(symbol_name, str_tbl + nl_tbl[j].n_un.n_strx) == 0) {
@@ -45,7 +46,8 @@ void *symtbl_solve(uint32_t image_index, const char *symbol_name) {
4546
}
4647
if (symbol_address != NULL) {
4748
symbol_address += image_slide;
48-
} else {
49+
}
50+
else {
4951
LOG_ERROR("symtbl_solve: symbol not found!");
5052
}
5153
return symbol_address;

src/tinyhook.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,6 @@ static int calc_far_jump(uint8_t *output, void *src, void *dst, bool link) {
5252
return jump_size;
5353
}
5454

55-
int position = 0;
56-
mach_vm_address_t vm;
57-
5855
bool need_far_jump(void *src, void *dst) {
5956
long long distance = dst > src ? dst - src : src - dst;
6057
#ifdef __aarch64__
@@ -111,7 +108,8 @@ static int save_head(void *src, void *dst, size_t min_len, int *skip_lenp, int *
111108
*(uint16_t *)(bytes_out + head_len) = X86_64_MOV_RM64;
112109
bytes_out[head_len + 2] = insn.modrm_reg << 3 | insn.modrm_reg;
113110
head_len += 3;
114-
} else {
111+
}
112+
else {
115113
memcpy(bytes_out + head_len, bytes_in + skip_len, insn.len);
116114
head_len += insn.len;
117115
}
@@ -133,22 +131,25 @@ int tiny_hook(void *src, void *dst, void **orig) {
133131
if (orig == NULL) {
134132
jump_size = calc_jump(jump_insns, src, dst, false);
135133
kr = write_mem(src, jump_insns, jump_size);
136-
} else {
137-
if (!position) {
134+
}
135+
else {
136+
static int position = 0;
137+
static mach_vm_address_t trampoline = 0;
138+
if (!trampoline) {
138139
// alloc a vm to store headers and jumps
139-
kr = mach_vm_allocate(mach_task_self(), &vm, PAGE_SIZE, VM_FLAGS_ANYWHERE);
140+
kr = mach_vm_allocate(mach_task_self(), &trampoline, PAGE_SIZE, VM_FLAGS_ANYWHERE);
140141
if (kr != 0) {
141142
LOG_ERROR("mach_vm_allocate: %s", mach_error_string(kr));
142143
}
143144
}
144145
int skip_len, head_len;
145-
*orig = (void *)(vm + position);
146+
*orig = (void *)(trampoline + position);
146147
jump_size = calc_jump(jump_insns, src, dst, false);
147-
kr |= save_head(src, (void *)(vm + position), jump_size, &skip_len, &head_len);
148+
kr |= save_head(src, (void *)(trampoline + position), jump_size, &skip_len, &head_len);
148149
kr |= write_mem(src, jump_insns, jump_size);
149150
position += head_len;
150-
jump_size += calc_jump(jump_insns, (void *)(vm + position), src + skip_len, false);
151-
kr |= write_mem((void *)(vm + position), jump_insns, jump_size);
151+
jump_size += calc_jump(jump_insns, (void *)(trampoline + position), src + skip_len, false);
152+
kr |= write_mem((void *)(trampoline + position), jump_insns, jump_size);
152153
position += jump_size;
153154
}
154155
return kr;

test/hook/example.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@ int fake_add(int a, int b) {
2525
}
2626

2727
__attribute__((visibility("default"))) void exported_func() {
28+
printf("This is an exported function\n");
2829
return;
2930
}
3031

3132
__attribute__((constructor(0))) int load() {
3233
fprintf(stderr, "=== libexample loading...\n");
3334

3435
// get an exported symbol address
35-
void *func_fake = symexp_solve(1, "_exported_func");
36+
void (*func_fake)(void) = symexp_solve(1, "_exported_func");
3637
fprintf(stderr, "=== exported_func() address: %p\n", func_fake);
38+
func_fake();
3739

3840
// hook a function by symbol
3941
void *func_add = symtbl_solve(0, "_add");
@@ -49,5 +51,9 @@ __attribute__((constructor(0))) int load() {
4951
fprintf(stderr, "=== Removing hook\n");
5052
tiny_unhook_ex(&printf_bak);
5153
printf("Hook is removed!\n");
54+
55+
// or use interpose to hook it!
56+
fprintf(stderr, "=== Now interposing printf\n");
57+
tiny_interpose(0, "_printf", my_printf);
5258
return 0;
5359
}

0 commit comments

Comments
 (0)