1 /*
2 ** remap.c for elfsh
3 **
4 ** elfsh_reloc_pht/sht/symtab() are based on spacewalker's
5 ** original modremap.c
6 **
7 ** Started on  Mon Mar 17 09:30:33 2003 mayhem
8 ** Last update Tue Jun  3 14:01:26 2003 mayhem
9 */
10 #include "libelfsh.h"
11 
12 
13 /* Remap the Program Header Table */
elfsh_reloc_pht(elfshobj_t * file,u_long diff)14 int		elfsh_reloc_pht(elfshobj_t *file, u_long diff)
15 {
16   u_int		i;
17   u_int		count;
18   u_long	base;
19 
20   base = elfsh_get_object_baseaddr(file);
21   if (file == NULL || file->pht == NULL || file->hdr->e_phnum == 0)
22     ELFSH_SETERROR("[libelfsh:reloc_pht] Invalid NULL parameter\n", 0);
23   for (count = i = 0; i < file->hdr->e_phnum; i++)
24     {
25       if (file->pht[i].p_vaddr >= base)
26 	{
27 	  file->pht[i].p_vaddr += diff;
28 	  count++;
29 	}
30       if (file->pht[i].p_paddr >= base)
31 	{
32 	  file->pht[i].p_paddr += diff;
33 	  count++;
34 	}
35     }
36   return (count);
37 }
38 
39 /* Remap the Section Header Table */
elfsh_reloc_sht(elfshobj_t * file,u_long diff)40 int		elfsh_reloc_sht(elfshobj_t *file, u_long diff)
41 {
42   u_int		i;
43   u_int		count;
44   u_long	base;
45 
46   if (file == NULL || file->sht == NULL || file->hdr->e_shnum == 0)
47     ELFSH_SETERROR("[libelfsh:reloc_sht] Invalid NULL parameter\n", -1);
48   base = elfsh_get_object_baseaddr(file);
49   for (count = i = 0; i < file->hdr->e_shnum; i++)
50     if (file->sht[i].sh_addr > base)
51       {
52 	file->sht[i].sh_addr += diff;
53 	count++;
54       }
55   return (count);
56 }
57 
58 
59 
60 /* Remap a section which type is SHT_SYMTAB or SHT_DYNSYM */
elfsh_reloc_symtab(elfshsect_t * s,u_long diff)61 int		elfsh_reloc_symtab(elfshsect_t *s, u_long diff)
62 {
63   Elf32_Sym	*symtab;
64   u_int		i;
65   u_int		vaddr;
66   u_int		count;
67   u_long	base;
68 
69   if (s == NULL || s->shdr == NULL)
70     ELFSH_SETERROR("[libelfsh:reloc_symtab] Invalid NULL parameter\n", -1);
71   else if (s->shdr->sh_type != SHT_SYMTAB && s->shdr->sh_type != SHT_DYNSYM)
72     ELFSH_SETERROR("[libelfsh:reloc_symtab] Unexpected section type\n", -1);
73   symtab = s->data;
74   base = elfsh_get_object_baseaddr(s->parent);
75   for (count = i = 0; i < s->shdr->sh_size / sizeof(Elf32_Sym); i++)
76     {
77       vaddr = elfsh_get_symbol_value(symtab + i);
78       if (vaddr > base)
79 	{
80 	  elfsh_set_symbol_value(symtab + i, vaddr + diff);
81 	  count++;
82 	}
83     }
84 
85   /* Synchronize the symtab hash table */
86   elfsh_sync_sorted_symtab(s);
87   return (count);
88 }
89 
90 
91 /* Remap a section using its extra relocation entries */
elfsh_reloc_raw(elfshsect_t * cur,u_long diff)92 int		elfsh_reloc_raw(elfshsect_t *cur, u_long diff)
93 {
94   u_int		index;
95   u_long	addr;
96   elfshsect_t	*target;
97   char		*str;
98 
99   if (cur == NULL || cur->shdr == NULL)
100     ELFSH_SETERROR("[libelfsh:reloc_raw] Invalid NULL parameter\n", -1);
101   if (cur->data == NULL || cur->rel == NULL)
102     return (0);
103 
104   /* Read the actual section and find valid references */
105   for (index = 0; index < cur->srcref; index++)
106     switch (cur->rel[index].type)
107       {
108 
109 	/* Relocate by : section[idx_dst].vaddr + off_dst */
110       case ELFSH_RELOC_SECTBASE:
111 	target = elfsh_get_section_by_index(cur->parent,
112 					    cur->rel[index].idx_dst,
113 					    NULL, NULL);
114 	if (target == NULL)
115 	  ELFSH_SETERROR("[libelfsh:reloc_raw] Invalid IDX_DST\n", -1);
116 	str = cur->data + cur->rel[index].off_src;
117 	addr = target->shdr->sh_addr + cur->rel[index].off_dst + diff;
118 	memcpy(str, &addr, sizeof(u_long));
119 
120 	/* Do not relocate */
121       case ELFSH_RELOC_FP:
122       default:
123 	break;
124       }
125 
126   return (cur->srcref);
127 }
128 
129 
130 
131 /* Remap the .dynamic section */
elfsh_reloc_dynamic(elfshsect_t * sect,u_long diff)132 int		elfsh_reloc_dynamic(elfshsect_t *sect, u_long diff)
133 {
134   elfshsect_t	*parent;
135   Elf32_Dyn	*dyn;
136   u_int		index;
137   u_int		count;
138   u_long	val;
139   u_int		nbr;
140 
141   if (sect == NULL || sect->shdr == NULL)
142     ELFSH_SETERROR("[libelfsh:reloc_rel] Invalid NULL parameter\n", -1);
143   else if (sect->shdr->sh_type != SHT_DYNAMIC)
144     ELFSH_SETERROR("[libelfsh:reloc_rel] Unexpected section type\n", -1);
145 
146   nbr = sect->shdr->sh_size / sizeof(Elf32_Dyn);
147   for (dyn = sect->data, count = index = 0; index < nbr; index++)
148     {
149       val = elfsh_get_dynentry_val(dyn + index);
150       parent = elfsh_get_parent_section(sect->parent, val, NULL);
151       if (val && parent != NULL && parent->shdr->sh_addr != NULL)
152 	{
153 	  elfsh_set_dynentry_val(dyn + index, val + diff);
154 	  count++;
155 	}
156     }
157   return (count);
158 }
159 
160 
161 /* Remap sections of type SHT_REL and SHT_RELA */
elfsh_reloc_rel(elfshsect_t * sect,u_long diff)162 int		elfsh_reloc_rel(elfshsect_t *sect, u_long diff)
163 {
164   elfshsect_t	*parent;
165   Elf32_Rel	*rel;
166   u_int		index;
167   u_int		count;
168   u_int		nbr;
169 
170   if (sect == NULL || sect->shdr == NULL)
171     ELFSH_SETERROR("[libelfsh:reloc_rel] Invalid NULL parameter\n", -1);
172   else if (sect->shdr->sh_type != SHT_REL && sect->shdr->sh_type != SHT_RELA)
173     ELFSH_SETERROR("[libelfsh:reloc_rel] Unexpected section type\n", -1);
174 
175   nbr = sect->shdr->sh_size / sizeof(Elf32_Rel);
176   for (rel = sect->data, count = index = 0; index < nbr; index++)
177     {
178       parent = elfsh_get_parent_section(sect->parent,
179 					rel[index].r_offset,
180 					NULL);
181       if (rel[index].r_offset && parent != NULL && parent->shdr->sh_addr != NULL)
182 	{
183 	  rel[index].r_offset += diff;
184 	  count++;
185 	}
186     }
187   return (count);
188 }
189 
190 
191 
192 /* Remap section's whoose type is a data array (GOT, CTORS, DTORS ..) */
elfsh_reloc_array(elfshobj_t * file,u_long * array,u_int size,u_long diff)193 int		elfsh_reloc_array(elfshobj_t *file, u_long *array, u_int size, u_long diff)
194 {
195   elfshsect_t	*parent;
196   u_int		index;
197   u_int		count;
198 
199   if (file == NULL || array == NULL)
200     ELFSH_SETERROR("[libelfsh:reloc_array] Invalid NULL paramater\n", -1);
201   for (count = index = 0; index < size; index++)
202     {
203       parent = elfsh_get_parent_section(file, array[index], NULL);
204       if (parent != NULL && parent->shdr->sh_addr != NULL && array[index] != NULL)
205 	{
206 	  array[index] += diff;
207 	  count++;
208 	}
209     }
210   return (count);
211 }
212 
213 
214 /* Remap Global Offset Table */
elfsh_reloc_got(elfshsect_t * sect,u_long diff)215 int		elfsh_reloc_got(elfshsect_t *sect, u_long diff)
216 {
217   if (sect == NULL || sect->shdr == NULL)
218     ELFSH_SETERROR("[libelfsh:reloc_got] Invalid NULL parameter\n", -1);
219   else if (strcmp(sect->name, ELFSH_SECTION_NAME_GOT))
220     ELFSH_SETERROR("[libelfsh:reloc_got] Unexpected section name\n", -1);
221   return (elfsh_reloc_array(sect->parent, sect->data,
222 			    sect->shdr->sh_size / sizeof(u_long), diff));
223 }
224 
225 /* Remap .ctors section */
elfsh_reloc_ctors(elfshsect_t * sect,u_long diff)226 int		elfsh_reloc_ctors(elfshsect_t *sect, u_long diff)
227 {
228   if (sect == NULL || sect->shdr == NULL)
229     ELFSH_SETERROR("[libelfsh:reloc_ctors] Invalid NULL parameter\n", -1);
230   else if (strcmp(sect->name, ELFSH_SECTION_NAME_CTORS))
231     ELFSH_SETERROR("[libelfsh:reloc_ctors] Unexpected section name\n", -1);
232   return (elfsh_reloc_array(sect->parent, sect->data,
233 			    sect->shdr->sh_size / sizeof(u_long), diff));
234 }
235 
236 /* Remap .dtors section */
elfsh_reloc_dtors(elfshsect_t * sect,u_long diff)237 int		elfsh_reloc_dtors(elfshsect_t *sect, u_long diff)
238 {
239   if (sect == NULL || sect->shdr == NULL)
240     ELFSH_SETERROR("[libelfsh:reloc_dtors] Invalid NULL parameter\n", -1);
241   else if (strcmp(sect->name, ELFSH_SECTION_NAME_DTORS))
242     ELFSH_SETERROR("[libelfsh:reloc_dtors] Unexpected section name\n", -1);
243   return (elfsh_reloc_array(sect->parent, sect->data,
244 			    sect->shdr->sh_size / sizeof(u_long), diff));
245 }
246 
247 
248 /* Not used ATM since it triggers more false positives ;P */
elfsh_reloc_hash(elfshsect_t * sect,u_long diff)249 int		elfsh_reloc_hash(elfshsect_t *sect, u_long diff)
250 {
251   if (sect == NULL || sect->shdr == NULL)
252     ELFSH_SETERROR("[libelfsh:reloc_hash] Invalid NULL parameter\n", -1);
253   else if (sect->shdr->sh_type != SHT_HASH)
254     ELFSH_SETERROR("[libelfsh:reloc_hash] Unexpected section type\n", -1);
255   return (elfsh_reloc_array(sect->parent, sect->data,
256 			    sect->shdr->sh_size / sizeof(u_long), diff));
257 }
258 
259 
260 /* Call the type dependant remapping routine for this section */
elfsh_relocate_section(elfshsect_t * sect,u_long diff)261 int		elfsh_relocate_section(elfshsect_t *sect, u_long diff)
262 {
263   int		ret;
264 
265   ret = 0;
266   if (sect == NULL || sect->data == NULL)
267     ELFSH_SETERROR("[libelfsh:reloc_section] Invalid NULL paramater\n", -1);
268   if (sect->shdr->sh_addr != NULL)
269     elfsh_find_rel(sect);
270 
271   if (sect->shdr->sh_type == SHT_SYMTAB)
272     ret = elfsh_reloc_symtab(sect, diff);
273   else if (sect->shdr->sh_type == SHT_DYNSYM)
274     ret = elfsh_reloc_symtab(sect, diff);
275   else if (sect->shdr->sh_type == SHT_RELA || sect->shdr->sh_type == SHT_REL)
276     ret = elfsh_reloc_rel(sect, diff);
277   else if (sect->shdr->sh_type == SHT_DYNAMIC)
278     ret = elfsh_reloc_dynamic(sect, diff);
279   else if (!strcmp(sect->name, ELFSH_SECTION_NAME_CTORS))
280     ret = elfsh_reloc_ctors(sect, diff);
281   else if (!strcmp(sect->name, ELFSH_SECTION_NAME_DTORS))
282     ret = elfsh_reloc_dtors(sect, diff);
283   else if (!strcmp(sect->name, ELFSH_SECTION_NAME_GOT))
284     ret = elfsh_reloc_got(sect, diff);
285   else if (sect->shdr->sh_addr != NULL)
286     ret = elfsh_reloc_raw(sect, diff);
287   if (ret < 0)
288     return (-1);
289   return (ret);
290 }
291 
292 
293 
294 /*
295 ** XXX .::. TODO for ET_EXEC to ET_DYN :
296 ** - Insertion a new .rel section
297 ** - Change objtype and entryp in ELF header
298 ** - Relocate PHT and SHT
299 ** - Change reloc section size in .dynamic *
300 */
elfsh_remap(elfshobj_t * file,Elf32_Addr new_addr)301 int		elfsh_remap(elfshobj_t *file, Elf32_Addr new_addr)
302 {
303   elfshsect_t	*sect;
304   u_int		diff;
305   u_int		count;
306   int		ret;
307 
308   count = 0;
309   if (file == NULL)
310     ELFSH_SETERROR("[libelfsh:relocate] Invalid NULL parameter\n", -1);
311   else if (elfsh_read_obj(file) < 0)
312     return (-1);
313   diff = elfsh_get_object_baseaddr(file);
314   if (diff == (u_int) -1);
315     ELFSH_SETERROR("[libelfsh:relocate] Object base address is NULL\n", -1);
316   for (sect = file->sectlist; sect != NULL; sect = sect->next)
317     {
318       ret = elfsh_relocate_section(sect, -diff);
319       printf("Relocation number found for %-20s : %d \n", sect->name, ret);
320       if (ret < 0)
321 	return (-1);
322       count += ret;
323     }
324   return (count);
325 }
326