Add dsc:// io plugin for dyld cache ##io

This commit is contained in:
Francesco Tamagni 2023-08-23 09:30:31 +02:00 committed by pancake
parent 7e5429f7ee
commit baf371bdd8
20 changed files with 850 additions and 64 deletions

View File

@ -74,6 +74,7 @@ io.http
io.bfdbg
io.mmap
io.default
io.dsc
io.self
io.mach
io.malloc

View File

@ -214,6 +214,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.fd
io.gdb
io.gprobe

View File

@ -224,6 +224,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.fd
io.gdb
io.gprobe

View File

@ -64,6 +64,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.ewf
io.gdb
io.haret

View File

@ -63,6 +63,7 @@ io.mmap
io.r2pipe
io.r2web
io.default
io.dsc
io.self
io.malloc
io.sparse

View File

@ -69,6 +69,7 @@ io.http
io.bfdbg
io.mmap
io.default
io.dsc
io.self
io.mach
io.malloc

View File

@ -196,6 +196,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.gprobe
io.gdb
io.qnx

View File

@ -183,6 +183,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.gprobe
io.gdb
io.qnx

View File

@ -81,6 +81,7 @@ fs.posix
io.bfdbg
io.debug
io.default
io.dsc
io.gdb
io.bochs
io.gzip

View File

@ -164,6 +164,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.gdb
io.qnx
io.r2pipe

View File

@ -132,6 +132,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.gdb
io.qnx
io.r2pipe

View File

@ -175,6 +175,7 @@ io.bfdbg
io.bochs
io.debug
io.default
io.dsc
io.gprobe
io.gdb
io.qnx

218
libr/bin/format/mach0/dsc.c Normal file
View File

@ -0,0 +1,218 @@
#ifndef _INCLUDE_R_BIN_DSC_C_
#define _INCLUDE_R_BIN_DSC_C_
#define R_IS_PTR_AUTHENTICATED(x) B_IS_SET(x, 63)
#define MAX_N_HDR 128
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
} RDyldRebaseInfo;
typedef struct {
ut64 start;
ut64 end;
RDyldRebaseInfo *info;
} RDyldRebaseInfosEntry;
typedef struct {
RDyldRebaseInfosEntry *entries;
size_t length;
} RDyldRebaseInfos;
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
ut16 *page_starts;
ut32 page_starts_count;
ut64 delta_mask;
ut32 delta_shift;
ut64 auth_value_add;
} RDyldRebaseInfo3;
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
ut16 *page_starts;
ut32 page_starts_count;
ut16 *page_extras;
ut32 page_extras_count;
ut64 delta_mask;
ut64 value_mask;
ut32 delta_shift;
ut64 value_add;
} RDyldRebaseInfo2;
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
ut16 *toc;
ut32 toc_count;
ut8 *entries;
ut32 entries_size;
} RDyldRebaseInfo1;
typedef struct {
const char * format;
const char * name;
} RDSCField;
typedef struct {
const RDSCField * fields;
RBuffer * buf;
ut8 * data;
} RDSCHeader;
static RDSCHeader * dsc_header_new(ut8 * data, ut64 data_len, const RDSCField fields[]) {
RDSCHeader * self = R_NEW0 (RDSCHeader);
if (!self) {
return NULL;
}
RBuffer * buf = r_buf_new_with_bytes (data, data_len);
if (!buf) {
free (self);
return NULL;
}
self->fields = fields;
self->buf = buf;
self->data = data;
return self;
}
static void dsc_header_free(RDSCHeader * self) {
if (!self) {
return;
}
r_buf_free (self->buf);
free(self->data);
free(self);
}
static bool dsc_header_get_field(RDSCHeader * self, const char * name, ut8 * out_value, size_t size) {
ut64 cursor;
ut64 data_len;
const RDSCField * field;
ut8 tmp[32];
data_len = r_buf_size (self->buf);
cursor = 0;
for (field = self->fields; field->name != NULL && cursor < data_len; field++) {
st64 field_size = r_buf_fread_at (self->buf, cursor, tmp, field->format, 1);
if (field_size < 0) {
return false;
}
if (!strcmp (field->name, name)) {
if (size < field_size) {
return false;
}
memcpy (out_value, tmp, field_size);
return true;
}
cursor += field_size;
}
return false;
}
static bool dsc_header_get_u64(RDSCHeader * self, const char * name, ut64 * out_value) {
return dsc_header_get_field (self, name, (ut8 *) out_value, sizeof (ut64));
}
static bool dsc_header_get_u32(RDSCHeader * self, const char * name, ut32 * out_value) {
return dsc_header_get_field (self, name, (ut8 *) out_value, sizeof (ut32));
}
static const RDSCField dsc_header_fields[] = {
{ "16c", "magic" },
{ "i", "mappingOffset" },
{ "i", "mappingCount" },
{ "i", "imagesOffsetOld" },
{ "i", "imagesCountOld" },
{ "l", "dyldBaseAddress" },
{ "l", "codeSignatureOffset" },
{ "l", "codeSignatureSize" },
{ "l", "slideInfoOffsetUnused" },
{ "l", "slideInfoSizeUnused" },
{ "l", "localSymbolsOffset" },
{ "l", "localSymbolsSize" },
{ "16c", "uuid" },
{ "l", "cacheType" },
{ "i", "branchPoolsOffset" },
{ "i", "branchPoolsCount" },
{ "l", "dyldInCacheMH" },
{ "l", "dyldInCacheEntry" },
{ "l", "imagesTextOffset" },
{ "l", "imagesTextCount" },
{ "l", "patchInfoAddr" },
{ "l", "patchInfoSize" },
{ "l", "otherImageGroupAddrUnused" },
{ "l", "otherImageGroupSizeUnused" },
{ "l", "progClosuresAddr" },
{ "l", "progClosuresSize" },
{ "l", "progClosuresTrieAddr" },
{ "l", "progClosuresTrieSize" },
{ "i", "platform" },
{ "i", "flags" },
{ "l", "sharedRegionStart" },
{ "l", "sharedRegionSize" },
{ "l", "maxSlide" },
{ "l", "dylibsImageArrayAddr" },
{ "l", "dylibsImageArraySize" },
{ "l", "dylibsTrieAddr" },
{ "l", "dylibsTrieSize" },
{ "l", "otherImageArrayAddr" },
{ "l", "otherImageArraySize" },
{ "l", "otherTrieAddr" },
{ "l", "otherTrieSize" },
{ "i", "mappingWithSlideOffset" },
{ "i", "mappingWithSlideCount" },
{ "l", "dylibsPBLStateArrayAddrUnused" },
{ "l", "dylibsPBLSetAddr" },
{ "l", "programsPBLSetPoolAddr" },
{ "l", "programsPBLSetPoolSize" },
{ "l", "programTrieAddr" },
{ "i", "programTrieSize" },
{ "i", "osVersion" },
{ "i", "altPlatform" },
{ "i", "altOsVersion" },
{ "l", "swiftOptsOffset" },
{ "l", "swiftOptsSize" },
{ "i", "subCacheArrayOffset" },
{ "i", "subCacheArrayCount" },
{ "16c", "symbolFileUUID" },
{ "l", "rosettaReadOnlyAddr" },
{ "l", "rosettaReadOnlySize" },
{ "l", "rosettaReadWriteAddr" },
{ "l", "rosettaReadWriteSize" },
{ "i", "imagesOffset" },
{ "i", "imagesCount" },
{ "i", "cacheSubType" },
{ "l", "objcOptsOffset" },
{ "l", "objcOptsSize" },
{ "l", "cacheAtlasOffset" },
{ "l", "cacheAtlasSize" },
{ "l", "dynamicDataOffset" },
{ "l", "dynamicDataMaxSize" },
{ NULL, NULL }
};
#endif

View File

@ -6,69 +6,7 @@
#define R_BIN_MACH064 1
#include "../format/mach0/mach0.h"
#include "objc/mach0_classes.h"
#define R_IS_PTR_AUTHENTICATED(x) B_IS_SET(x, 63)
#define MAX_N_HDR 128
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
} RDyldRebaseInfo;
typedef struct {
ut64 start;
ut64 end;
RDyldRebaseInfo *info;
} RDyldRebaseInfosEntry;
typedef struct {
RDyldRebaseInfosEntry *entries;
size_t length;
} RDyldRebaseInfos;
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
ut16 *page_starts;
ut32 page_starts_count;
ut64 delta_mask;
ut32 delta_shift;
ut64 auth_value_add;
} RDyldRebaseInfo3;
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
ut16 *page_starts;
ut32 page_starts_count;
ut16 *page_extras;
ut32 page_extras_count;
ut64 delta_mask;
ut64 value_mask;
ut32 delta_shift;
ut64 value_add;
} RDyldRebaseInfo2;
typedef struct {
ut8 version;
ut64 slide;
ut8 *one_page_buf;
ut32 page_size;
ut64 start_of_data;
ut16 *toc;
ut32 toc_count;
ut8 *entries;
ut32 entries_size;
} RDyldRebaseInfo1;
#include "../format/mach0/dsc.c"
typedef struct {
ut64 local_symbols_offset;

View File

@ -610,6 +610,7 @@ extern RIOPlugin r_io_plugin_w32;
extern RIOPlugin r_io_plugin_zip;
extern RIOPlugin r_io_plugin_mmap;
extern RIOPlugin r_io_plugin_default;
extern RIOPlugin r_io_plugin_dsc;
extern RIOPlugin r_io_plugin_ihex;
extern RIOPlugin r_io_plugin_self;
extern RIOPlugin r_io_plugin_gzip;

View File

@ -22,6 +22,7 @@ r_io_sources = [
'p/io_debug.c',
'p/io_default.c',
'p/io_sysgdb.c',
'p/io_dsc.c',
'p/io_gdb.c',
'p/io_gprobe.c',
'p/io_gzip.c',

View File

@ -15,7 +15,7 @@ endif
all: ${ALL_TARGETS}
ALL_TARGETS=
PLUGINS=ptrace.mk debug.mk gdb.mk malloc.mk shm.mk mach.mk w32dbg.mk procpid.mk winkd.mk bochs.mk qnx.mk r2k.mk ar.mk rbuf.mk gprobe.mk isotp.mk treebuf.mk cyclic.mk
PLUGINS=ptrace.mk debug.mk gdb.mk malloc.mk shm.mk mach.mk w32dbg.mk procpid.mk winkd.mk bochs.mk qnx.mk r2k.mk ar.mk rbuf.mk gprobe.mk isotp.mk treebuf.mk cyclic.mk dsc.mk
#zip.mk
#PLUGINS=ptrace.mk debug.mk gdb.mk malloc.mk mach.mk w32dbg.mk procpid.mk
include ${PLUGINS}

16
libr/io/p/dsc.mk Normal file
View File

@ -0,0 +1,16 @@
OBJ_DSC=io_dsc.o
STATIC_OBJ+=${OBJ_DSC}
TARGET_DSC=io_dsc.${EXT_SO}
ALL_TARGETS+=${TARGET_DSC}
ifeq (${WITHPIC},0)
LINKFLAGS+=../../util/libr_util.a
LINKFLAGS+=../../io/libr_io.a
else
LINKFLAGS+=-L../../util -lr_util
LINKFLAGS+=-L.. -lr_io
endif
${TARGET_DSC}: ${OBJ_DSC}
${CC_LIB} $(call libname,io_dsc) ${CFLAGS} -o ${TARGET_DSC} ${OBJ_DSC} ${LINKFLAGS}

599
libr/io/p/io_dsc.c Normal file
View File

@ -0,0 +1,599 @@
/* radare - LGPL - Copyright 2008-2023 - mrmacete, pancake */
#include <r_io.h>
#include <r_lib.h>
#include "../../bin/format/mach0/dsc.c"
typedef struct {
int fd;
ut64 start;
ut64 end;
} RIODscSlice;
static void r_io_dsc_slice_free(RIODscSlice * slice);
R_VEC_TYPE_WITH_FINI (RIODscSlices, RIODscSlice, r_io_dsc_slice_free);
typedef struct {
char *filename;
int mode;
int perm;
bool nocache;
RIO *io_backref;
RIODscSlices slices;
ut64 total_size;
} RIODscObject;
typedef enum {
SUBCACHE_FORMAT_UNDEFINED,
SUBCACHE_FORMAT_V1,
SUBCACHE_FORMAT_V2
} RDscSubcacheFormat;
typedef struct {
ut8 uuid[16];
ut64 cacheVMOffset;
} RDscSubcacheEntryV1;
typedef struct {
ut8 uuid[16];
ut64 cacheVMOffset;
char suffix[32];
} RDscSubcacheEntryV2;
#define URL_SCHEME "dsc://"
static RIODscObject *r_io_dsc_object_new(RIO *io, const char *filename, int perm, int mode);
static void r_io_dsc_object_free(RIODscObject *dsc);
static RDSCHeader * r_io_dsc_read_header(int fd, ut64 offset);
static int r_io_internal_read(RIODscObject * dsc, ut64 off, ut8 *buf, int count);
static int r_io_posix_open(const char *file, int perm, int mode, bool nocache);
static int r_io_dsc_object_read(RIO *io, RIODesc *fd, ut8 *buf, int count);
static ut64 r_io_dsc_object_seek(RIO *io, RIODscObject *dsc, ut64 offset, int whence);
static bool r_io_dsc_object_dig_slices(RIODscObject * dsc);
static bool r_io_dsc_detect_subcache_format(int fd, ut32 sc_offset, ut32 sc_count, ut64 size, ut64 * out_entry_size, RDscSubcacheFormat * out_format);
static bool r_io_dsc_dig_subcache(RIODscObject * dsc, const char * filename, ut64 start, ut8 * check_uuid, ut64 * out_size);
static bool r_io_dsc_object_dig_one_slice(RIODscObject * dsc, int fd, ut64 start, ut64 end, ut8 * check_uuid, RDSCHeader * header, bool walk_monocache);
static RIODscSlice * r_io_dsc_object_get_slice(RIODscObject * dsc, ut64 off_global);
static bool is_valid_magic(ut8 magic[16]);
static bool is_null_uuid(ut8 uuid[16]);
static bool __check(RIO *io, const char *file, bool many) {
return r_str_startswith (file, URL_SCHEME);
}
static RIODesc *__open(RIO *io, const char *file, int perm, int mode) {
if (*file && __check (io, file, false)) {
RIODscObject *dsc = r_io_dsc_object_new (io, file, perm, mode);
if (!dsc) {
return NULL;
}
RIODesc *d = r_io_desc_new (io, &r_io_plugin_dsc, dsc->filename, perm, mode, dsc);
if (!d->name) {
d->name = strdup (dsc->filename);
}
return d;
}
return NULL;
}
static int __read(RIO *io, RIODesc *fd, ut8 *buf, int len) {
return r_io_dsc_object_read (io, fd, buf, len);
}
static bool __close(RIODesc *fd) {
r_return_val_if_fail (fd, false);
if (fd->data) {
r_io_dsc_object_free ((RIODscObject *) fd->data);
fd->data = NULL;
}
return true;
}
static ut64 __lseek_dsc(RIO *io, RIODesc *fd, ut64 offset, int whence) {
r_return_val_if_fail (fd && fd->data, UT64_MAX);
return r_io_dsc_object_seek (io, (RIODscObject *)fd->data, offset, whence);
}
static RIODscObject *r_io_dsc_object_new(RIO *io, const char *filename, int perm, int mode) {
r_return_val_if_fail (io && filename, NULL);
RIODscObject *dsc = R_NEW0 (RIODscObject);
if (!dsc) {
return NULL;
}
if (r_str_startswith (filename, URL_SCHEME)) {
filename += strlen (URL_SCHEME);
}
dsc->filename = strdup (filename);
dsc->perm = perm;
dsc->mode = mode;
dsc->nocache = false;
dsc->io_backref = io;
if (!r_io_dsc_object_dig_slices (dsc)) {
r_io_dsc_object_free (dsc);
return NULL;
}
return dsc;
}
static void r_io_dsc_object_free(RIODscObject *dsc) {
if (dsc) {
free (dsc->filename);
RIODscSlices_fini (&dsc->slices);
free (dsc);
}
}
static bool r_io_dsc_object_dig_slices(RIODscObject * dsc) {
int fd = r_io_posix_open (dsc->filename, O_RDONLY, dsc->mode, dsc->nocache);
if (fd == -1) {
return false;
}
RDSCHeader * header = r_io_dsc_read_header (fd, 0);
if (!header) {
R_LOG_ERROR ("Could not parse header");
goto error;
}
ut32 subCacheArrayOffset;
ut32 subCacheArrayCount;
ut64 codeSignatureOffset, codeSignatureSize;
dsc_header_get_u64 (header, "codeSignatureOffset", &codeSignatureOffset);
dsc_header_get_u64 (header, "codeSignatureSize", &codeSignatureSize);
ut64 next_or_end = codeSignatureOffset + codeSignatureSize;
if (!dsc_header_get_u32 (header, "subCacheArrayOffset", &subCacheArrayOffset)) {
// not a multi-file cache
dsc->total_size = next_or_end;
return r_io_dsc_object_dig_one_slice (dsc, fd, 0, next_or_end, NULL, header, false);
} else {
if (!dsc_header_get_u32 (header, "subCacheArrayCount", &subCacheArrayCount)) {
R_LOG_ERROR ("Malformed multi file cache");
goto error;
}
if (subCacheArrayCount == 0) {
R_LOG_ERROR ("Please open the first file of the cache");
goto error;
}
if (lseek (fd, next_or_end, SEEK_SET) >= 0) {
ut8 tmp[16];
if (read (fd, tmp, 16) == 16) {
if (is_valid_magic (tmp)) {
// cache files are cat together ("monocache")
dsc->total_size = next_or_end;
return r_io_dsc_object_dig_one_slice (dsc, fd, 0, next_or_end, NULL, header, true);
}
}
}
ut64 sc_entry_size;
RDscSubcacheFormat sc_format = SUBCACHE_FORMAT_UNDEFINED;
if (!r_io_dsc_detect_subcache_format(fd, subCacheArrayOffset, subCacheArrayCount, next_or_end, &sc_entry_size, &sc_format)) {
R_LOG_ERROR ("Could not detect subcache entry format");
goto error;
}
if (sc_format == SUBCACHE_FORMAT_UNDEFINED) {
R_LOG_ERROR ("Ambiguous or unsupported subcache entry format");
goto error;
}
ut64 cursor = 0;
int i;
r_io_dsc_object_dig_one_slice (dsc, fd, 0, next_or_end, NULL, header, false);
cursor = next_or_end;
ut64 sc_entry_cursor = subCacheArrayOffset;
for (i = 0; i != subCacheArrayCount; i++) {
char * suffix;
ut8 check_uuid[16];
if (lseek (fd, sc_entry_cursor, SEEK_SET) < 0) {
goto error;
}
switch (sc_format) {
case SUBCACHE_FORMAT_V1:
{
RDscSubcacheEntryV1 entry;
if (read (fd, &entry, sc_entry_size) != sc_entry_size) {
goto error;
}
suffix = r_str_newf (".%d", i + 1);
memcpy (check_uuid, entry.uuid, 16);
break;
}
case SUBCACHE_FORMAT_V2:
{
RDscSubcacheEntryV2 entry;
if (read (fd, &entry, sc_entry_size) != sc_entry_size) {
return false;
}
suffix = malloc (33);
if (!suffix) {
goto error;
}
memcpy (suffix, entry.suffix, 32);
suffix[32] = 0;
memcpy (check_uuid, entry.uuid, 16);
break;
}
case SUBCACHE_FORMAT_UNDEFINED:
suffix = NULL;
break;
}
char * subcache_filename = r_str_newf ("%s%s", dsc->filename, suffix);
free (suffix);
if (!subcache_filename) {
goto error;
}
ut64 size;
bool success = r_io_dsc_dig_subcache (dsc, subcache_filename, cursor, check_uuid, &size);
free (subcache_filename);
if (!success) {
goto error;
}
cursor += size;
sc_entry_cursor += sc_entry_size;
}
ut8 sym_uuid[16];
if (dsc_header_get_field (header, "symbolFileUUID", sym_uuid, 16) && !is_null_uuid (sym_uuid)) {
ut64 size;
char * subcache_filename = r_str_newf ("%s.symbols", dsc->filename);
if (!subcache_filename) {
goto error;
}
bool success = r_io_dsc_dig_subcache (dsc, subcache_filename, cursor, sym_uuid, &size);
free (subcache_filename);
if (!success) {
goto error;
}
cursor += size;
}
dsc->total_size = cursor;
}
dsc_header_free (header);
return true;
error:
dsc_header_free (header);
close (fd);
return false;
}
static bool r_io_dsc_detect_subcache_format(int fd, ut32 sc_offset, ut32 sc_count, ut64 size, ut64 * out_entry_size, RDscSubcacheFormat * out_format) {
RDscSubcacheFormat sc_format = SUBCACHE_FORMAT_UNDEFINED;
ut64 sc_entry_size = 0;
if (sc_count != 0) {
ut64 array_size_v1 = sizeof (RDscSubcacheEntryV1) * sc_count;
ut64 array_size_v2 = sizeof (RDscSubcacheEntryV2) * sc_count;
char test_v1, test_v2;
if (array_size_v1 + 1 >= size || array_size_v2 + 1 >= size) {
R_LOG_ERROR ("Malformed subcache entries");
return false;
}
if (lseek (fd, sc_offset + array_size_v1, SEEK_SET) < 0) {
return false;
}
if (read (fd, &test_v1, 1) != 1) {
return false;
}
if (lseek (fd, sc_offset + array_size_v2, SEEK_SET) < 0) {
return false;
}
if (read (fd, &test_v2, 1) != 1) {
return false;
}
if (test_v1 == '/' && test_v2 != '/') {
sc_format = SUBCACHE_FORMAT_V1;
sc_entry_size = sizeof (RDscSubcacheEntryV1);
} else if (test_v1 != '/' && test_v2 == '/') {
sc_format = SUBCACHE_FORMAT_V2;
sc_entry_size = sizeof (RDscSubcacheEntryV2);
}
}
*out_entry_size = sc_entry_size;
*out_format = sc_format;
return true;
}
static bool r_io_dsc_dig_subcache(RIODscObject * dsc, const char * filename, ut64 start, ut8 * check_uuid, ut64 * out_size) {
int sc_fd = r_io_posix_open (filename, O_RDONLY, dsc->mode, dsc->nocache);
if (sc_fd == -1) {
R_LOG_ERROR ("Could not open subcache %s", filename);
return false;
}
RDSCHeader * sc_header = r_io_dsc_read_header (sc_fd, 0);
if (!sc_header) {
close (sc_fd);
R_LOG_ERROR ("Could not parse header");
return false;
}
ut64 codeSignatureOffset, codeSignatureSize;
dsc_header_get_u64 (sc_header, "codeSignatureOffset", &codeSignatureOffset);
dsc_header_get_u64 (sc_header, "codeSignatureSize", &codeSignatureSize);
ut64 size = codeSignatureOffset + codeSignatureSize;
*out_size = size;
if (!r_io_dsc_object_dig_one_slice (dsc, sc_fd, start, start + size, check_uuid, sc_header, false)) {
close (sc_fd);
dsc_header_free (sc_header);
return false;
}
dsc_header_free (sc_header);
return true;
}
static bool r_io_dsc_object_dig_one_slice(RIODscObject * dsc, int fd, ut64 start, ut64 end, ut8 * check_uuid, RDSCHeader * header, bool walk_monocache) {
if (check_uuid) {
ut8 uuid[16];
if (!dsc_header_get_field (header, "uuid", uuid, 16)) {
R_LOG_ERROR ("Malformed subcache");
return false;
}
if (memcmp (uuid, check_uuid, 16) != 0) {
R_LOG_ERROR ("Mismatched uuid for subcache");
return false;
}
}
RIODscSlice * slice = RIODscSlices_emplace_back (&dsc->slices);
if (!slice) {
return false;
}
slice->fd = fd;
slice->start = start;
if (walk_monocache) {
ut64 cursor = end;
while (true) {
RDSCHeader * sc_header = r_io_dsc_read_header (fd, cursor);
if (!sc_header) {
break;
}
// TODO: parse rebase info
ut64 codeSignatureOffset, codeSignatureSize;
dsc_header_get_u64 (sc_header, "codeSignatureOffset", &codeSignatureOffset);
dsc_header_get_u64 (sc_header, "codeSignatureSize", &codeSignatureSize);
ut64 size = codeSignatureOffset + codeSignatureSize;
dsc_header_free (sc_header);
cursor += size;
}
slice->end = cursor;
dsc->total_size = cursor;
} else {
slice->end = end;
}
return true;
}
static void r_io_dsc_slice_free(RIODscSlice * slice) {
if (!slice) {
return;
}
close (slice->fd);
}
static int r_io_posix_open(const char *file, int perm, int mode, bool nocache) {
int fd;
#if R2__WINDOWS__
fd = r_sandbox_open (file, O_RDONLY | O_BINARY, 0);
#else
fd = r_sandbox_open (file, O_RDONLY, mode);
#endif
#ifdef F_NOCACHE
if (nocache) {
fcntl (fd, F_NOCACHE, 1);
}
#endif
return fd;
}
static int r_io_dsc_object_read(RIO *io, RIODesc *fd, ut8 *buf, int count) {
r_return_val_if_fail (fd && fd->data && buf, -1);
if (io->off == UT64_MAX) {
memset (buf, 0xff, count);
return count;
}
RIODscObject *dsc = fd->data;
if (!dsc) {
return -1;
}
int r = r_io_internal_read (dsc, io->off, buf, count);
if (r > 0) {
io->off += r;
}
return r;
}
static int r_io_internal_read(RIODscObject * dsc, ut64 off_global, ut8 *buf, int count) {
RIODscSlice * slice = r_io_dsc_object_get_slice(dsc, off_global);
if (!slice) {
return -1;
}
ut64 off_local = off_global - slice->start;
if (lseek (slice->fd, off_local, SEEK_SET) < 0) {
return -1;
}
return read (slice->fd, buf, count);
}
static ut64 r_io_dsc_object_seek(RIO *io, RIODscObject *dsc, ut64 offset, int whence) {
if (!dsc || offset == UT64_MAX) {
return UT64_MAX;
}
ut64 off_global;
switch (whence) {
case SEEK_SET:
off_global = offset;
break;
case SEEK_CUR:
off_global = io->off + offset;
break;
case SEEK_END:
off_global = dsc->total_size + offset;
break;
}
RIODscSlice * slice = r_io_dsc_object_get_slice(dsc, off_global);
if (!slice) {
if (whence == SEEK_END && off_global >= dsc->total_size) {
io->off = dsc->total_size;
return io->off;
}
return UT64_MAX;
}
ut64 off_local = off_global - slice->start;
off_local = lseek (slice->fd, off_local, SEEK_SET);
if (off_local == UT64_MAX) {
return UT64_MAX;
}
io->off = off_local + slice->start;
return io->off;
}
static RIODscSlice * r_io_dsc_object_get_slice(RIODscObject * dsc, ut64 off_global) {
RIODscSlice * slice;
R_VEC_FOREACH (&dsc->slices, slice) {
if (slice->start <= off_global && slice->end > off_global) {
return slice;
}
}
return NULL;
}
#if R2__UNIX__
static bool __is_blockdevice(RIODesc *desc) {
return false;
}
#endif
static RDSCHeader * r_io_dsc_read_header(int fd, ut64 offset) {
ut8 tmp[16];
if (lseek (fd, offset, SEEK_SET) < 0) {
return NULL;
}
if (read (fd, tmp, 16) != 16) {
return NULL;
}
if (!is_valid_magic (tmp)) {
return NULL;
}
if (read (fd, tmp, 4) != 4) {
return NULL;
}
if (lseek (fd, offset, SEEK_SET) < 0) {
return NULL;
}
ut32 header_size = r_read_le32 (tmp);
if (header_size > 4096 || header_size == 0) {
return NULL;
}
ut8 * header_data = malloc (header_size);
if (read (fd, header_data, header_size) != header_size) {
free (header_data);
return NULL;
}
return dsc_header_new (header_data, header_size, dsc_header_fields);
}
static bool is_valid_magic(ut8 magic[16]) {
return !strcmp ((char *) magic, "dyld_v1 arm64")
|| !strcmp ((char *) magic, "dyld_v1 arm64e")
|| !strcmp ((char *) magic, "dyld_v1 x86_64")
|| !strcmp ((char *) magic, "dyld_v1 x86_64h");
}
static bool is_null_uuid(ut8 uuid[16]) {
int i;
ut64 sum = 0;
for (i = 0; i != 16; i+= 8) {
sum |= *(ut64*)&uuid[i];
}
return sum == 0;
}
RIOPlugin r_io_plugin_dsc = {
.meta = {
.name = "dsc",
.desc = "Open dyld shared library caches",
.license = "LGPL3",
},
.uris = URL_SCHEME,
.open = __open,
.close = __close,
.read = __read,
.check = __check,
.seek = __lseek_dsc,
#if R2__UNIX__
.is_blockdevice = __is_blockdevice,
#endif
};
#ifndef R2_PLUGIN_INCORE
R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_IO,
.data = &r_io_plugin_dsc,
.version = R2_VERSION
};
#endif

View File

@ -26,6 +26,7 @@ endif
# meson split is results in 1 empty element when splitting an empty string :facepalm:
if user_plugins.contains('apple') or user_plugins.contains('macos') or user_plugins.contains('ios')
io_plugins += [
'dsc',
'mach',
]
bin_plugins += [