Skip to content

Commit 114e9e8

Browse files
committed
Clean up payload a bit
1 parent b7354b9 commit 114e9e8

File tree

3 files changed

+128
-80
lines changed

3 files changed

+128
-80
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ loader: loader.go assets.go
3535
cli: cli.go
3636
GOARCH=arm $(GO) build -o $@ $<
3737

38-
patcher-payload.so: patcher-payload.c
39-
$(CROSS)$(CC) $(TARGET_CFLAGS) -fPIC -shared -nostdlib -o $@ $<
38+
patcher-payload.so: patcher-payload.c patcher-payload.h
39+
$(CROSS)$(CC) $(TARGET_CFLAGS) -fPIC -shared -nostdlib -I. -o $@ $<
4040

4141
patcher: patcher.go
4242
GOARCH=arm $(GO) build -o $@ $<

patcher-payload.c

+60-78
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,24 @@
55
#include <string.h>
66
#include <time.h>
77

8+
#include "patcher-payload.h"
9+
810
#define LOGFILE "/data/local/tmp/patcher-payload.log"
911
#define LOGFILE_MODE "w" // fopen() mode; 'a'ppend or 'w'rite (with truncation).
1012

13+
#define OWN_LIBNAME "patcher-payload" // Our own name, for skipping the GOT patching.
14+
15+
#define MAIN_PROCESS_NAME "dtv_svc" // Name of the main binary (dl* just return "").
16+
17+
#define UNUSED __attribute__((unused))
18+
19+
// Log file, opened in init().
1120
static FILE *logf = NULL;
1221

22+
// A machine word.
23+
typedef uint32_t word;
24+
25+
// Log handler; to be used with the log() macro that passes file and line.
1326
__attribute__((format(printf, 3, 4)))
1427
static void do_log(const char *filename, int lineno, const char *format, ...) {
1528
if (!logf)
@@ -32,89 +45,56 @@ static void do_log(const char *filename, int lineno, const char *format, ...) {
3245
fflush(logf);
3346
}
3447

48+
// Log with current filename and line.
3549
#define log(...) do_log(__FILE__, __LINE__, __VA_ARGS__)
3650

37-
#define PROT_NONE 0x00
38-
#define PROT_READ 0x04
39-
#define PROT_WRITE 0x02
40-
#define PROT_EXEC 0x01
41-
extern int mprotect(void *addr, size_t len, int prot);
42-
43-
#define PF_X (1 << 0) /* Segment is executable */
44-
#define PF_W (1 << 1) /* Segment is writable */
45-
#define PF_R (1 << 2) /* Segment is readable */
46-
47-
#define PF_RW (PF_R|PF_W)
48-
49-
#define PT_NULL 0 /* Program header table entry unused */
50-
#define PT_LOAD 1 /* Loadable program segment */
51-
#define PT_DYNAMIC 2 /* Dynamic linking information */
52-
/* […] rest omitted for brevity */
53-
54-
struct elf32_phdr {
55-
uint32_t type; /* Segment type */
56-
uint32_t offset; /* Segment file offset */
57-
uint32_t vaddr; /* Segment virtual address */
58-
uint32_t paddr; /* Segment physical address */
59-
uint32_t filesz; /* Segment size in file */
60-
uint32_t memsz; /* Segment size in memory */
61-
uint32_t flags; /* Segment flags */
62-
uint32_t align; /* Segment alignment */
51+
// A request to patch each loaded library’s GOT with pointers to oldval pointing to newval.
52+
struct patch_got_req {
53+
word oldval;
54+
word newval;
6355
};
6456

65-
struct dl_phdr_info {
66-
void *addr; /* Base address of object */
67-
const char *name; /* Name of object */
68-
const struct elf32_phdr *phdr; /* Pointer to array of ELF program headers */
69-
uint16_t phnum; /* # of items in phdr */
57+
// An item in the undo_list, for unpatching GOTs in fini().
58+
struct undo_item {
59+
word *addr;
60+
word oldval;
7061
};
7162

72-
extern int dl_iterate_phdr(void *callback, void *data);
73-
74-
#define RTLD_LAZY 0x00001 /* Lazy function call binding. */
75-
#define RTLD_NOW 0x00002 /* Immediate function call binding. */
76-
#define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */
77-
#define RTLD_NOLOAD 0x00004 /* Do not load the object. */
78-
#define RTLD_DEEPBIND 0x00008 /* Use deep binding. */
63+
// Patched words with their old value.
64+
#define UNDO_LIST_SIZE 256
65+
static struct undo_item undo_list[UNDO_LIST_SIZE];
7966

80-
extern void *dlsym(void *hdl, const char *name);
81-
extern void *dlopen(const char *, int);
82-
extern int dlclose(void *);
83-
84-
#define UNUSED __attribute__((unused))
67+
// Number of undo_list elements populated. Top of stack is undo_list[undo_size-1].
68+
static unsigned int undo_size = 0;
8569

8670
static char *(*orig_getval)(int16_t grp, char *cfg, int32_t *value) = NULL;
8771

88-
struct patch_got_req {
89-
void *oldval;
90-
void *newval;
91-
};
92-
93-
struct undo_item {
94-
uint32_t *addr;
95-
uint32_t oldval;
96-
};
97-
98-
static struct undo_item undo[256];
99-
static unsigned int nundo = 0;
100-
10172
static char *my_getval(int16_t grp, char *cfg, int32_t *value) {
10273
char *ret = orig_getval(grp, cfg, value);
10374
log("orig_getval(grp=%d, cfg=%s, *value=%ld) = %s", grp, cfg, (value?*value:0xDEAD), ret);
10475
return ret;
10576
}
10677

107-
static int patch_got(void *start, void *end, const char *filename, struct patch_got_req *req) {
78+
// Patch a GOT as described in req. The GOT is expected to be within start-end and contain the
79+
// value to patch at most once. (We can’t really easily tell where the GOT in the ELF segment
80+
// is, so we search all of it and if we found the value twice, the chances of either of them
81+
// being some other random data is nonzero.)
82+
static int patch_got(word *start, word *end, const char *filename, struct patch_got_req *req) {
83+
if (strstr(filename, OWN_LIBNAME))
84+
return 0;
85+
10886
if (!*filename)
109-
filename = "dtv_svc";
87+
filename = MAIN_PROCESS_NAME;
11088

111-
uint32_t *off = NULL;
89+
word *off = NULL;
11290

113-
for (uint32_t *p = start; p < (uint32_t *)end; p++) {
114-
if (*p != (uint32_t)req->oldval)
91+
for (word *p = start; p < end; p++) {
92+
if (*p != req->oldval)
11593
continue;
94+
11695
if (off) {
117-
log("patch_got(%p, %p): found old value twice (%p and %p), not patching.", start, end, off, p);
96+
log("patch_got(%p, %p, \"%s\"): found old value twice (at %p and at %p), not patching.",
97+
start, end, filename, off, p);
11898
return -1;
11999
}
120100
off = p;
@@ -123,27 +103,27 @@ static int patch_got(void *start, void *end, const char *filename, struct patch_
123103
if (!off)
124104
return 0;
125105

126-
if (++nundo > sizeof(undo)) {
127-
log("can't store more than %d undo items", sizeof(undo));
106+
if (++undo_size > sizeof(undo_list)) {
107+
log("Can't store more than %d undo items, increase UNDO_LIST_SIZE.", sizeof(undo_list));
128108
return -1;
129109
}
130110

131-
undo[nundo-1].addr = off;
132-
undo[nundo-1].oldval = *off;
133-
*off = (uint32_t)req->newval;
134-
log("Patched %p 0x%08lx -> 0x%08lx", off, undo[nundo-1].oldval, *off);
111+
undo_list[undo_size-1].addr = off;
112+
undo_list[undo_size-1].oldval = *off;
113+
*off = req->newval;
114+
log("Patched GOT of %s at %p (from 0x%08lx to 0x%08lx)", filename, off,
115+
undo_list[undo_size-1].oldval, *off);
135116

136117
return 0;
137118
}
138119

120+
// Callback for dl_iterate_phdr. Walks over all ELF segments and calls patch_got for each that
121+
// is likely to contain a GOT (i.e. is of type LOAD and read- and writable).
139122
static int find_got_phdr(struct dl_phdr_info *info, size_t size UNUSED, struct patch_got_req *req) {
140123
for (int i = 0; i < info->phnum; i++) {
141124
if (info->phdr[i].type != PT_LOAD || (info->phdr[i].flags & PF_RW) != PF_RW)
142125
continue;
143126

144-
if (strstr(info->name, "patcher-payload")) // XXX
145-
continue;
146-
147127
void *start = info->addr + info->phdr[i].vaddr;
148128
void *end = start + info->phdr[i].memsz;
149129

@@ -155,6 +135,7 @@ static int find_got_phdr(struct dl_phdr_info *info, size_t size UNUSED, struct p
155135
}
156136

157137

138+
// Sets up logging and installs all hooks.
158139
__attribute__((constructor)) static void init() {
159140
if (logf)
160141
fclose(logf);
@@ -170,7 +151,7 @@ __attribute__((constructor)) static void init() {
170151
return;
171152
}
172153

173-
void *hdl = dlopen("libmtkapp.so", 6);
154+
void *hdl = dlopen("libmtkapp.so", RTLD_NOW|RTLD_NOLOAD);
174155
const char *symname = "a_mtktvapi_config_get_value";
175156
orig_getval = dlsym(hdl, symname);
176157

@@ -184,22 +165,23 @@ __attribute__((constructor)) static void init() {
184165

185166
log("Patching GOTs referencing %s = %p", symname, orig_getval);
186167
struct patch_got_req req = {
187-
.oldval = orig_getval,
188-
.newval = my_getval,
168+
.oldval = (word)orig_getval,
169+
.newval = (word)my_getval,
189170
};
190171
dl_iterate_phdr(find_got_phdr, &req);
191172

192173

193174
log("Initialized");
194175
}
195176

177+
// Uninstalls all hooks and closes the logfile.
196178
__attribute__((destructor)) static void fini() {
197179
log("Tearing down.");
198180

199-
while (nundo > 0) {
200-
*(undo[nundo-1].addr) = undo[nundo-1].oldval;
201-
log("Undid GOT patch at %p", undo[nundo-1].addr);
202-
nundo--;
181+
while (undo_size > 0) {
182+
*(undo_list[undo_size-1].addr) = undo_list[undo_size-1].oldval;
183+
log("Undid GOT patch at %p", undo_list[undo_size-1].addr);
184+
undo_size--;
203185
}
204186

205187
if (fclose(logf) == 0)

patcher-payload.h

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#ifndef _PATCHER_PAYLOAD_H
2+
#define _PATCHER_PAYLOAD_H 1
3+
4+
/*** Definitions missing from gcc-arm-none-eabi’s C headers. ***/
5+
6+
/** mprotect() - unused, but handy for making .text +w. **/
7+
8+
#define PROT_NONE 0x00
9+
#define PROT_READ 0x04
10+
#define PROT_WRITE 0x02
11+
#define PROT_EXEC 0x01
12+
extern int mprotect(void *addr, size_t len, int prot);
13+
14+
/** dl_iterate_phdr() and accompanying structs. Taken from glibc’s elf/elf.h. **/
15+
16+
#define PF_X (1 << 0) /* Segment is executable */
17+
#define PF_W (1 << 1) /* Segment is writable */
18+
#define PF_R (1 << 2) /* Segment is readable */
19+
20+
#define PF_RW (PF_R|PF_W) /* not in glibc */
21+
22+
#define PT_NULL 0 /* Program header table entry unused */
23+
#define PT_LOAD 1 /* Loadable program segment */
24+
#define PT_DYNAMIC 2 /* Dynamic linking information */
25+
/* etc. - we only really use PT_LOAD. */
26+
27+
struct elf32_phdr {
28+
uint32_t type; /* Segment type */
29+
uint32_t offset; /* Segment file offset */
30+
uint32_t vaddr; /* Segment virtual address */
31+
uint32_t paddr; /* Segment physical address */
32+
uint32_t filesz; /* Segment size in file */
33+
uint32_t memsz; /* Segment size in memory */
34+
uint32_t flags; /* Segment flags */
35+
uint32_t align; /* Segment alignment */
36+
};
37+
38+
struct dl_phdr_info {
39+
void *addr; /* Base address of object */
40+
const char *name; /* Name of object */
41+
const struct elf32_phdr *phdr; /* Pointer to array of ELF program headers */
42+
uint16_t phnum; /* # of items in phdr */
43+
/* glibc >= 2.4 has additional fields that we don’t care about. */
44+
};
45+
46+
extern int dl_iterate_phdr(void *callback, void *data);
47+
48+
/** dlopen() and friends. From glibc’s {bits,dlfcn}/dlfcn.h. */
49+
50+
/* dlopen flags. */
51+
#define RTLD_LAZY 0x00001 /* Lazy function call binding. */
52+
#define RTLD_NOW 0x00002 /* Immediate function call binding. */
53+
#define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */
54+
#define RTLD_NOLOAD 0x00004 /* Do not load the object. */
55+
#define RTLD_DEEPBIND 0x00008 /* Use deep binding. */
56+
57+
/* special dlsym handles. */
58+
#define RTLD_NEXT ((void *) -1l)
59+
#define RTLD_DEFAULT ((void *) 0) /* The code uses NULL actually. */
60+
61+
extern void *dlsym(void *hdl, const char *name);
62+
extern void *dlopen(const char *, int);
63+
extern int dlclose(void *);
64+
char *dlerror(void);
65+
66+
#endif /* _PATCHER_PAYLOAD_H */

0 commit comments

Comments
 (0)