/
vio.c
332 lines (314 loc) · 10.6 KB
/
vio.c
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/* radare - LGPL - Copyright 2014 - pancake, condret */
#include <r_io.h>
#define VIO_DEBUG 0
/*
| io.va | | io.ff |
.------------------.
| |
| VADDR ---> MADDR | ---> PADDR ---> PLUGINS
| | maddr->paddr+fd
`------------------'
virtual addresses are used when io.va is enabled
and it checks for sections in order to find an address
inside a mapped range.
If a virtual address is not found in any section,
then it looks into the map addresses.
mapped addresses are used to map an RIODesc at a
specific physical address. A file can be opened, but
not mapped, in this case it will not be visible.
cache: where should it
undo api: works at vaddr level too
TODO:
- How to share data pointers?
- Load RBuffer as file?
maddr -> vaddr
it is possible to get a list of virtual addresses that
point to a given physical address from a virtual one.
*/
/* Virtual Addressing API */
#define isInMapRange(m,x) ((m->from <= x) && (x <= m->to))
/* Idea: At first, get all sections in vrange, meanwhile record all unsectioned area and store it via RIORange in a list,
read all sections via mread, resolve maps for unsectioned areas and fill the gaps. last point must always prefere using io->desc*/
R_API int r_io_vread (RIO *io, ut64 vaddr, ut8 *buf, int len) {
int tmp_len = len;
ut8 *tmp_buf = buf;
ut64 vendaddr, maddr, tmp_vaddr = vaddr;
RIOMap *map;
RIOSection *section;
RIORange *range;
RList *sections, *ranges = NULL, *maps;
RListIter *iter, *ator;
if (!io->desc) {
eprintf ("r_io_vread: desc is NULL, WTF!\n");
return R_ERROR;
}
if (len < 0) {
eprintf ("r_io_vread: wrong usage; len is smaller than 0. len: %i\n", len);
return R_FAIL;
}
sections = r_io_section_get_in_vaddr_range (io, vaddr, vaddr+len);
if (!r_list_empty (sections)) { //check if there is any section
ranges = r_list_new();
ranges->free = free;
r_list_foreach (sections, iter, section) {
if (section->vaddr==0)
continue;
if (section->vaddr > tmp_vaddr) {
range = r_io_range_new(); //create a new range
range->from = tmp_vaddr; //record unsectioned area
range->to = section->vaddr;
r_list_append (ranges, range); //store the range
tmp_vaddr = section->vaddr; //prepare for resolving the maddr
tmp_len -= (tmp_vaddr - vaddr);
tmp_buf += (tmp_vaddr - vaddr); //adjust buffer
}
vendaddr = tmp_vaddr + tmp_len; //calculate the virtual end address
if (vendaddr > (section->vaddr + section->vsize)) //check if the virual end address is in the section too
vendaddr = section->vaddr + section->vsize; //if not, size it down
maddr = tmp_vaddr - section->vaddr + section->offset; //calculate the map address (address inside the map)
if (maddr > ( section->offset + section->size)) { //check if the maddr is inside the physical section, if not, skip some things
} else {
if ((vendaddr - section->vaddr + section->offset) > (section->offset + section->size)) { //check if the virtual part of the section fits into the physical part
r_io_mread (io, section->fd, maddr, tmp_buf, (section->offset + section->size) - maddr);//if not, read as far as possible
} else {
r_io_mread (io, section->fd, maddr, tmp_buf, vendaddr - tmp_vaddr); //read from the sections fd
}
}
tmp_buf += (vendaddr - tmp_vaddr); //adjust buffer
tmp_len -= (vendaddr - tmp_vaddr); //adjust length
tmp_vaddr = vendaddr; //adjust address
}
}
r_list_free (sections);
if (ranges) { //this is all might be too slow
r_list_foreach (ranges, iter, range) {
maps = r_io_map_get_maps_in_range (io, range->from, range->to - range->from); //get all maps in the range
tmp_vaddr = range->from;
tmp_len = range->to - range->from; //adjust length
tmp_buf = buf + (tmp_vaddr - vaddr); //adjust pointer
r_list_foreach (maps, ator, map) { //start filling the gaps
r_io_mread (io, map->fd, tmp_vaddr, tmp_buf, tmp_len); //read from maps, the ranges will adjusted in mread
}
r_list_free (maps); //free the list for the next iteration
r_io_mread (io, io->desc->fd, tmp_vaddr, tmp_buf, tmp_len); //ensure that io->desc is always on the top
}
r_list_free (ranges);
} else {
maps = r_io_map_get_maps_in_range (io, vaddr, vaddr + len); //get all maps
r_list_foreach (maps, iter, map) {
r_io_mread (io, map->fd, vaddr, buf, len); //read from the maps, the ranges will be adjusted in mread
}
r_list_free (maps); //free the list
r_io_mread (io, io->desc->fd, vaddr, buf, len); //ensure that io->desc is always on the top
}
return R_TRUE;
}
/*you can throw any fd on this beast, it's important that len is equal or smaller than the size of buf*/
R_API int r_io_mread (RIO *io, int fd, ut64 maddr, ut8 *buf, int len) {
int read_bytes = len;
ut64 endaddr, paddr, d;
RIODesc *desc; //desc for tmp use
RIOMap *map; //map
if (len < 0) { //len must be bigger then -1
eprintf ("r_io_mread: wrong usage; len is smaller than 0. len: %i\n", len);
return R_FAIL;
}
if ((UT64_MAX - len) < maddr) { //say no to integer-overflows
eprintf ("r_io_mread: sorry, but I won't let you overflow this ut64\n");
read_bytes = UT64_MAX - maddr; //shrink len/read_bytes
}
endaddr = maddr + read_bytes; //get endaddr
map = r_io_map_resolve_in_range (io, maddr, endaddr, fd); //resolve map for fd in range
if (!map)
map = r_io_map_resolve (io, fd); //try to resolve map if it is not in range
if (!map) { //check if map exists
eprintf ("r_io_mread: cannot resolve map for fd %i\n", fd);
return R_ERROR;
}
if (endaddr > map->to) { //check if endaddr is in the map
if (maddr > map->to) //check segfault
return R_FAIL;
endaddr = map->to; //adjust endaddr
read_bytes = endaddr - maddr; //adjust read_bytes
}
if (maddr < map->from) { //adjusting things here will make vread very easy, because you can just get a list of fds in the range and the throw every fd on this function
if (endaddr < map->from) //check segfaults
return R_FAIL;
d = map->from - maddr; //get difference between maddr and start of the map
if (read_bytes < d) //check if adjusting would cause segfaults
return R_FAIL;
buf += d; //adjust buf-ptr
read_bytes -= d; //this is dangerous and can overflow
maddr += d; //adjust maddr
}
paddr = maddr - map->from + map->delta; //resolve paddr
desc = io->desc; //save io->desc
io->desc = r_io_desc_get (io, fd); //resolve desc for fd
if (!io->desc) { //check if desc exists
eprintf ("r_io_mread: cannot get desc for fd %i\n", fd);
io->desc = desc; //restore io->desc
return R_ERROR;
}
read_bytes = r_io_pread (io, paddr, buf, read_bytes); //read
io->desc = desc; //restore io->desc
return read_bytes; //return bytes read
}
R_API int r_io_pread (RIO *io, ut64 paddr, ut8 *buf, int len) {
int bytes_read = 0;
#if VIO_DEBUG
char *read_from = NULL;
#endif
if (!io) {
#if VIO_DEBUG //show debug-info
eprintf ("r_io_pread: io is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
}
if (paddr == UT64_MAX) {
if (io->ff) {
memset (buf, 0xff, len);
return len;
}
return R_FAIL;
}
r_io_seek (io, paddr, R_IO_SEEK_SET);
if (io->buffer_enabled){
#if VIO_DEBUG
read_from = "buffer";
#endif
bytes_read = r_io_buffer_read (io, io->off, buf, len);
} else {
if (io->desc && io->desc->plugin && io->desc->plugin->read){
#if VIO_DEBUG
read_from = io->desc->plugin->name;
#endif
bytes_read = io->desc->plugin->read (io, io->desc, buf, len);
} else if (!io->desc) {
#if VIO_DEBUG
eprintf ("r_io_pread: io->desc is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
} else {
#if VIO_DEBUG
read_from = "File";
#endif
bytes_read = read (io->desc->fd, buf, len);
}
if (bytes_read<0) {
#if VIO_DEBUG
eprintf ("r_io_pread: bytes_read %i\n"
"from: %s\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", bytes_read, read_from, paddr, len);
r_sys_backtrace();
#endif
}
}
return bytes_read;
}
// This is not so good commented, because it's mostly copy-pasta from mread
R_API int r_io_mwrite (RIO *io, int fd, ut64 maddr, ut8 *buf, int len) {
int write_bytes = len;
ut64 endaddr, paddr, d;
RIODesc *desc; //desc for tmp use
RIOMap *map; //map
if (len<0) {
eprintf ("r_io_mwrite: wrong usage; len is smaller than 0, len: %i\n", len);
return R_FAIL;
}
if ((UT64_MAX - len) < maddr) { //no overflows please
eprintf ("r_io_mwrite: no, you cannot overflow this ut64\n");
write_bytes = UT64_MAX - maddr;
}
endaddr = maddr + write_bytes;
map = r_io_map_resolve_in_range (io, maddr, endaddr, fd);
if (!map)
map = r_io_map_resolve (io, fd);
if (!map) {
eprintf ("r_io_mwrite: cannot resolve map for fd%i\n", fd);
return R_ERROR;
}
if (endaddr > map->to) {
if (maddr > map->to)
return R_FAIL;
endaddr = map->to;
write_bytes = endaddr - maddr;
}
if (maddr < map->from) {
if (endaddr < map->from)
return R_FAIL;
d = map->from - maddr;
if (write_bytes < d)
return R_FAIL;
buf += d;
write_bytes -= d;
maddr += d;
}
if (!(map->flags & R_IO_WRITE)) //check if the map allows writing
return write_bytes;
paddr = maddr - map->from + map->delta;
desc = io->desc;
io->desc = r_io_desc_get (io, fd);
if (!io->desc) {
eprintf ("r_io_mwrite: cannot get desc for fd %i\n", fd);
io->desc = desc;
return R_ERROR;
}
write_bytes = r_io_pwrite (io, paddr, buf, write_bytes);
io->desc = desc;
return write_bytes;
}
R_API int r_io_pwrite (RIO *io, ut64 paddr, const ut8 *buf, int len)
{
int bytes_written = 0;
#if VIO_DEBUG
char *written_to = NULL;
#endif
if (!io) {
#if VIO_DEBUG
eprintf ("r_io_pwrite: io is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
}
if ((UT64_MAX - len) < paddr) //prevent overflows
len = UT64_MAX - paddr;
r_io_seek (io, paddr, R_IO_SEEK_SET);
if (io->desc && io->desc->plugin && io->desc->plugin->write) {
#if VIO_DEBUG
written_to = io->desc->plugin->name;
#endif
bytes_written = io->desc->plugin->write (io, io->desc, buf, len);
} else if (!io->desc) {
#if VIO_DEBUG //show debug-info
eprintf ("r_io_pwrite: io->desc is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
} else {
#if VIO_DEBUG
written_to = "File";
#endif
bytes_written = write (io->desc->fd, buf, len);
}
if (bytes_written < 0) {
#if VIO_DEBUG
eprintf ("r_io_pwrite: bytes_written: %i\n"
"to: %s\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", bytes_written, written_to, paddr, len);
r_sys_backtrace();
#endif
}
return bytes_written;
}