1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#include <assert.h>
#include <sys/types.h>
#include <gelf.h>
#include <libelfu/libelfu.h>
/*
* Insert space at a given position in the file by moving everything
* after it towards the end of the file, and everything before it
* towards lower memory regions where it is mapped.
*
* off must not be in the middle of any data structure, such as
* PHDRs, SHDRs, or sections. Behaviour is undefined if it is.
*
* PHDRs will be patched such that everything AFTER off is mapped to
* the same address in memory, and everything BEFORE it is shifted to
* lower addresses, making space for the new data in-between.
*/
GElf_Xword elfu_mInsertBefore(ElfuElf *me, GElf_Off off, GElf_Xword size)
{
ElfuScn *ms;
ElfuPhdr *mp;
assert(me);
// TODO: Take p_align into account
/* Move SHDRs and PHDRs */
if (me->ehdr.e_shoff >= off) {
me->ehdr.e_shoff += size;
}
if (me->ehdr.e_phoff >= off) {
me->ehdr.e_phoff += size;
}
/* Patch PHDRs to include new data */
CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz;
if (mp->phdr.p_offset >= off) {
/* Insertion before PHDR's content, so it's just shifted */
mp->phdr.p_offset += size;
} else {
/* mp->phdr.p_offset < off */
if (off < end) {
/* Mark this as a modified area */
/* Insertion in the middle of PHDR, so let it span the new data */
mp->phdr.p_filesz += size;
mp->phdr.p_memsz += size;
mp->phdr.p_vaddr -= size;
mp->phdr.p_paddr -= size;
} else {
/* Insertion after PHDR's content, so it may need to be
remapped. This will happen in a second pass.
*/
}
}
}
/* For each LOAD header, find clashing headers that need to be
remapped to lower memory areas.
*/
CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
if (mp->phdr.p_type == PT_LOAD) {
ElfuPhdr *mp2;
CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
if (mp2->phdr.p_type != PT_LOAD
&& mp2->phdr.p_offset + mp2->phdr.p_filesz <= off) {
/* The PHDR ends in the file before the injection site */
GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz;
GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz;
GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz;
GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz;
/* If mp and mp2 now overlap in memory */
if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr)
|| (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) {
/* Move mp2 down in memory, as mp has been resized.
Maintaining the relative offset between them is the best
guess at maintaining consistency.
*/
mp2->phdr.p_vaddr -= size;
mp2->phdr.p_paddr -= size;
}
}
}
}
}
/* Move the sections themselves */
CIRCLEQ_FOREACH(ms, &me->scnList, elem) {
if (ms->shdr.sh_offset >= off) {
ms->shdr.sh_offset += size;
} else {
/* sh_offset < off */
/* If this was in a LOAD segment, it has been adjusted there
and this synchronises it.
If not, it doesn't matter anyway.
*/
ms->shdr.sh_addr -= size;
}
}
return size;
}
|