mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-28 15:41:38 +00:00
340 lines
11 KiB
C
340 lines
11 KiB
C
/* radare - LGPL - Copyright 2014-2016 - 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_newf (free);
|
||
if (!ranges) {
|
||
r_list_free (sections);
|
||
return false;
|
||
}
|
||
r_list_foreach (sections, iter, section) {
|
||
if (!section->vaddr) continue;
|
||
if (section->vaddr > tmp_vaddr) {
|
||
range = r_io_range_new (); //create a new range
|
||
if (!range) {
|
||
r_list_free (ranges);
|
||
r_list_free (sections);
|
||
return false;
|
||
}
|
||
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 virtual 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 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;
|
||
}
|