mirror of
https://github.com/radareorg/r2flutter.git
synced 2026-11-07 06:26:00 -10:00
Object pool pointer parser
This commit is contained in:
1 parent
f16826e33d
commit
ae901bb0ee
14 files changed
+1015
-8
No files matched your search
@@ -13,7 +13,7 @@ BUILD_DIR = build
|
||||
BIN_DIR = bin
|
||||
|
||||
# Source files
|
||||
LIB_SRC = $(SRC_DIR)/dart_app.c $(SRC_DIR)/dart_dumper.c $(SRC_DIR)/dart_pool_snapshot.c $(SRC_DIR)/dart_pool_discovery.c $(SRC_DIR)/dart_pool_names.c $(SRC_DIR)/dart_pool_strings.c $(SRC_DIR)/dart_pool_data_image.c $(SRC_DIR)/dart_pool_it.c $(SRC_DIR)/dart_pool_xrefs.c $(SRC_DIR)/dart_sbom.c $(SRC_DIR)/dart_pool_clusters.c $(SRC_DIR)/dart_pool_classes.c $(SRC_DIR)/dart_pool_model.c $(SRC_DIR)/dart_pool_parse.c $(SRC_DIR)/dart_pool_modern.c $(SRC_DIR)/dart_version.c $(SRC_DIR)/dart_cid.c $(SRC_DIR)/dart_obf.c
|
||||
LIB_SRC = $(SRC_DIR)/dart_app.c $(SRC_DIR)/dart_dumper.c $(SRC_DIR)/dart_pool_snapshot.c $(SRC_DIR)/dart_pool_discovery.c $(SRC_DIR)/dart_pool_names.c $(SRC_DIR)/dart_pool_strings.c $(SRC_DIR)/dart_pool_data_image.c $(SRC_DIR)/dart_pool_it.c $(SRC_DIR)/dart_pool_xrefs.c $(SRC_DIR)/dart_object.c $(SRC_DIR)/dart_sbom.c $(SRC_DIR)/dart_pool_clusters.c $(SRC_DIR)/dart_pool_classes.c $(SRC_DIR)/dart_pool_model.c $(SRC_DIR)/dart_pool_parse.c $(SRC_DIR)/dart_pool_modern.c $(SRC_DIR)/dart_version.c $(SRC_DIR)/dart_cid.c $(SRC_DIR)/dart_obf.c
|
||||
MAIN_SRC = src/tool/main.c
|
||||
ANALYSIS_SRC = src/r2/flutter_analysis.c
|
||||
|
||||
|
||||
@@ -963,3 +963,19 @@ Dart package versions are not normally serialized in AOT snapshots. Treat
|
||||
is explicit packaged metadata. The report carries `source`, `confidence`, and
|
||||
`evidence` so downstream tooling can distinguish hard metadata from URI/string
|
||||
inference. The top-level JSON intentionally says `complete: false`.
|
||||
|
||||
## Object And PP Slot Inspector
|
||||
|
||||
Runtime Dart values and static ObjectPool serialization need separate decode
|
||||
paths. `-O <addr>` treats the input as a Dart tagged value first: low-bit-clear
|
||||
values are Smis, low-bit-set values are untagged before reading an object
|
||||
header, and plain aligned addresses are also tried as object starts. When the
|
||||
CID resolves to a string class, the length Smi is the exact code-unit length;
|
||||
NUL padding is only display padding.
|
||||
|
||||
`-O pp+<off>` resolves against the same reconstructed static PP image as `-p`.
|
||||
The synthetic PP stores immediate entries directly, but tagged-object entries
|
||||
are zero placeholders because the serialized cluster stores reference IDs, not
|
||||
live object pointers. The inspector therefore also walks the first decoded
|
||||
ObjectPool cluster entry at that PP offset and reports entry bits, ref ID,
|
||||
resolved kind/name/CID, and code index when available.
|
||||
@@ -67,6 +67,23 @@ value, which may be a string object pointer, a Smi, a class, a type, a code
|
||||
object, or something else. The length comes only after resolving and decoding
|
||||
the target object.
|
||||
|
||||
### Decision Rule
|
||||
|
||||
Exact string length is available only when the parser has structured Dart
|
||||
metadata:
|
||||
|
||||
1. a snapshot string record boundary, where `encoded >> 1` gives the code-unit
|
||||
length; or
|
||||
2. a resolved Dart string object, where the object `length_` Smi gives the
|
||||
code-unit length.
|
||||
|
||||
If the parser only has a raw address into bytes, and it cannot prove that the
|
||||
address is the start of a serialized string record or a valid Dart string
|
||||
object, then the exact Dart string length is not knowable. Reading until
|
||||
`0x00`, until a non-printable byte, until padding, or until the next plausible
|
||||
record is carving. That can be useful for `-zz` triage, but it must not be
|
||||
treated as a non-heuristic string length.
|
||||
|
||||
## Tagged Pointers
|
||||
|
||||
Dart heap object pointers are tagged. Smis and heap objects are distinguished
|
||||
@@ -90,6 +107,19 @@ The CID table is version-dependent. Do not hardcode string CIDs globally. Use
|
||||
the resolved `DartVerLayout` values for `cid_string`, `cid_one_byte_string`, and
|
||||
`cid_two_byte_string`.
|
||||
|
||||
`r2flutter -O <addr>` is the direct inspector for this path. It accepts a Smi,
|
||||
a tagged heap pointer such as `0x1651`, or an untagged object address such as
|
||||
`0x1650`. For string objects it reports the object header, CID, length Smi,
|
||||
payload address, code-unit length, encoding, and a bounded value preview. With
|
||||
`-j`, the same fields are emitted as JSON.
|
||||
|
||||
`r2flutter -O pp+<off>` resolves a reconstructed ObjectPool PP slot. For
|
||||
immediate entries it decodes the raw tagged value. For tagged-object entries it
|
||||
reports the serialized ObjectPool entry bits, target reference ID, and any
|
||||
cluster-resolved kind/name/CID. The synthetic PP image contains zero
|
||||
placeholders for those object refs, so the command must not treat that zero as
|
||||
the runtime object value.
|
||||
|
||||
## NUL Termination
|
||||
|
||||
NUL bytes are not the Dart string delimiter.
|
||||
|
||||
@@ -356,6 +356,7 @@ name, then applies it to:
|
||||
| `-HH` | Dump the snapshot header plus VM/isolate cluster allocation/fill layout. |
|
||||
| `-HHH` | Add selected cluster payload diagnostics, currently ObjectPool entry decoding/status plus conservative ref resolution. |
|
||||
| `-i` | Dump instruction-table entries. |
|
||||
| `-O <addr\|pp+off>` | Decode a Dart Smi/tagged heap pointer/object address, or decode a reconstructed ObjectPool PP slot such as `pp+0x68`. Honors `-j`, `-q`, and `-r`. |
|
||||
| `-p` | Print the reconstructed static ObjectPool PP address pair; with `-r`, map the synthetic pool image and set `anal.gp`/`x27` from the synthetic vaddr. |
|
||||
| `-R` | Dump a radare2 script for applying method flags/comments and PP helpers. |
|
||||
| `-S` | Dump best-effort recovered components/SBOM with source, confidence, evidence, and nullable versions. |
|
||||
@@ -404,6 +405,7 @@ Plugin actions:
|
||||
| `r2flutter -HH` | Print snapshot header info plus VM/isolate cluster allocation/fill layout. |
|
||||
| `r2flutter -HHH` | Print cluster layout plus selected payload diagnostics, currently ObjectPool entries/status and ref resolution. |
|
||||
| `r2flutter -i` | Print instruction-table entries. |
|
||||
| `r2flutter -O addr` | Decode a Dart Smi/tagged heap pointer/object address, or decode a reconstructed ObjectPool PP slot such as `pp+0x68`. Honors `-j`, `-q`, and `-r`. |
|
||||
| `r2flutter -p` | Print the reconstructed static ObjectPool PP address pair. |
|
||||
| `r2flutter -R` | Print full radare2 script output. |
|
||||
| `r2flutter -S` | Dump best-effort recovered components/SBOM from the current binary context. |
|
||||
|
||||
@@ -165,6 +165,7 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_header_ext(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_header_deep(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_pp(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_object(DartCtx *ctx, const char *spec, int fmt);
|
||||
|
||||
// ============================================================================
|
||||
// Class Extraction API
|
||||
|
||||
@@ -0,0 +1,723 @@
|
||||
/* r2flutter - MIT - Copyright 2026 - pancake */
|
||||
|
||||
#include "dart_pool_parse_priv.h"
|
||||
|
||||
#define DART_OBJECT_STRING_PREVIEW_BYTES 4096ULL
|
||||
#define DART_OBJECT_MAX_STRING_BYTES (1024ULL * 1024ULL)
|
||||
#define DART_OBJECT_MAX_CANDIDATES 8
|
||||
#define DART_OBJECT_SYNTHETIC_PP_BASE 0x100000000ULL
|
||||
|
||||
typedef struct {
|
||||
bool valid;
|
||||
bool recognized_cid;
|
||||
bool is_string;
|
||||
bool is_two_byte;
|
||||
bool value_truncated;
|
||||
ut64 addr;
|
||||
ut64 header;
|
||||
ut64 length_raw;
|
||||
ut64 length;
|
||||
ut64 payload_addr;
|
||||
ut64 payload_size;
|
||||
int cid;
|
||||
const char *cid_name;
|
||||
char *value;
|
||||
} DartObjectInfo;
|
||||
|
||||
typedef struct {
|
||||
ut64 addr;
|
||||
const char *kind;
|
||||
bool ok;
|
||||
DartObjectInfo object;
|
||||
} DartObjectCandidate;
|
||||
|
||||
typedef struct {
|
||||
bool is_smi;
|
||||
st64 smi;
|
||||
ut64 raw;
|
||||
int ncandidates;
|
||||
int selected;
|
||||
DartObjectCandidate candidates[DART_OBJECT_MAX_CANDIDATES];
|
||||
} DartValueInfo;
|
||||
|
||||
typedef struct {
|
||||
bool present;
|
||||
bool read_ok;
|
||||
bool bits_ok;
|
||||
bool slot_ok;
|
||||
ut64 offset;
|
||||
ut64 addr;
|
||||
ut64 raw;
|
||||
ut64 index;
|
||||
ut8 bits;
|
||||
DartPpInfo pp;
|
||||
DartModernPoolSlotInfo slot;
|
||||
} DartPpSlotInfo;
|
||||
|
||||
static void dart_object_info_fini(DartObjectInfo *info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
free (info->value);
|
||||
memset (info, 0, sizeof (*info));
|
||||
}
|
||||
|
||||
static void dart_value_info_fini(DartValueInfo *info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < info->ncandidates; i++) {
|
||||
dart_object_info_fini (&info->candidates[i].object);
|
||||
}
|
||||
memset (info, 0, sizeof (*info));
|
||||
}
|
||||
|
||||
static void dart_pp_slot_info_fini(DartPpSlotInfo *info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
dart_pp_info_fini (&info->pp);
|
||||
dart_modern_pool_slot_info_fini (&info->slot);
|
||||
memset (info, 0, sizeof (*info));
|
||||
}
|
||||
|
||||
static int dart_object_heap_tag(DartCtx *ctx) {
|
||||
const int tag = ctx && ctx->layout && ctx->layout->heap_object_tag > 0? ctx->layout->heap_object_tag: 1;
|
||||
return tag;
|
||||
}
|
||||
|
||||
static int dart_object_cid_from_header(DartCtx *ctx, ut64 header) {
|
||||
DartTagStyle style = ctx && ctx->layout? ctx->layout->tag_style: DART_TAG_STYLE_OBJECT_HEADER;
|
||||
switch (style) {
|
||||
case DART_TAG_STYLE_CID_INT32:
|
||||
return (int) (header & 0xffffffffU);
|
||||
case DART_TAG_STYLE_CID_SHIFT1:
|
||||
return (int) ((header >> 1) & 0xffffffffU);
|
||||
case DART_TAG_STYLE_OBJECT_HEADER:
|
||||
default:
|
||||
return (int) ((header >> 12) & 0xfffffU);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *dart_object_cid_name(DartCtx *ctx, int cid) {
|
||||
const DartVerLayout *layout = ctx? ctx->layout: NULL;
|
||||
for (int i = 0; i < DART_CID_KIND_COUNT; i++) {
|
||||
if (dart_cid_get (layout, (DartCidKind)i) == cid) {
|
||||
return dart_cid_kind_name ((DartCidKind)i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool dart_object_parse_u64(const char *s, ut64 *out) {
|
||||
if (R_STR_ISEMPTY (s) || !out) {
|
||||
return false;
|
||||
}
|
||||
while (isspace ((ut8)*s)) {
|
||||
s++;
|
||||
}
|
||||
if (!*s) {
|
||||
return false;
|
||||
}
|
||||
char *end = NULL;
|
||||
ut64 value = (ut64)strtoull (s, &end, 0);
|
||||
if (end == s) {
|
||||
return false;
|
||||
}
|
||||
while (isspace ((ut8)*end)) {
|
||||
end++;
|
||||
}
|
||||
if (*end) {
|
||||
return false;
|
||||
}
|
||||
*out = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dart_object_parse_pp_spec(const char *spec, ut64 *out) {
|
||||
if (R_STR_ISEMPTY (spec) || !out) {
|
||||
return false;
|
||||
}
|
||||
const char *p = r_str_trim_head_ro (spec);
|
||||
if (*p == '@') {
|
||||
p++;
|
||||
}
|
||||
if (tolower ((ut8)p[0]) != 'p' || tolower ((ut8)p[1]) != 'p' || (p[2] != '+' && p[2] != ':')) {
|
||||
return false;
|
||||
}
|
||||
return dart_object_parse_u64 (p + 3, out);
|
||||
}
|
||||
|
||||
static char *dart_object_bytes_preview(const ut8 *buf, ut64 len) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
for (ut64 i = 0; i < len; i++) {
|
||||
ut8 b = buf[i];
|
||||
if (b == '\\') {
|
||||
r_strbuf_append (sb, "\\\\");
|
||||
} else if (b == '"') {
|
||||
r_strbuf_append (sb, "\\\"");
|
||||
} else if (b >= 0x20 && b < 0x7f) {
|
||||
r_strbuf_appendf (sb, "%c", b);
|
||||
} else {
|
||||
r_strbuf_appendf (sb, "\\x%02x", b);
|
||||
}
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
static bool dart_object_read_one_byte_string(DartCtx *ctx, DartObjectInfo *info) {
|
||||
const ut64 nread = R_MIN (info->payload_size, DART_OBJECT_STRING_PREVIEW_BYTES);
|
||||
if (nread == 0) {
|
||||
info->value = strdup ("");
|
||||
return true;
|
||||
}
|
||||
ut8 *buf = (ut8 *)malloc ((size_t)nread);
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
bool ok = read_mem (ctx, info->payload_addr, buf, (int)nread);
|
||||
if (ok) {
|
||||
info->value = dart_object_bytes_preview (buf, nread);
|
||||
info->value_truncated = info->payload_size > nread;
|
||||
}
|
||||
free (buf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool dart_object_read_two_byte_string(DartCtx *ctx, DartObjectInfo *info) {
|
||||
ut64 nbytes = R_MIN (info->payload_size, DART_OBJECT_STRING_PREVIEW_BYTES * 2ULL);
|
||||
nbytes &= ~1ULL;
|
||||
if (nbytes == 0) {
|
||||
info->value = strdup ("");
|
||||
return true;
|
||||
}
|
||||
ut8 *buf = (ut8 *)malloc ((size_t)nbytes);
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
bool ok = read_mem (ctx, info->payload_addr, buf, (int)nbytes);
|
||||
if (ok) {
|
||||
info->value = dart_utf16le_to_utf8 (buf, nbytes);
|
||||
if (!info->value) {
|
||||
info->value = dart_object_bytes_preview (buf, nbytes);
|
||||
}
|
||||
info->value_truncated = info->payload_size > nbytes;
|
||||
}
|
||||
free (buf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool dart_object_read_string_payload(DartCtx *ctx, DartObjectInfo *info, bool two_byte, ut64 payload_addr) {
|
||||
info->is_string = true;
|
||||
info->is_two_byte = two_byte;
|
||||
info->payload_addr = payload_addr;
|
||||
info->payload_size = two_byte? info->length * 2ULL: info->length;
|
||||
if (info->payload_size > DART_OBJECT_MAX_STRING_BYTES) {
|
||||
info->value_truncated = true;
|
||||
return true;
|
||||
}
|
||||
return two_byte? dart_object_read_two_byte_string (ctx, info): dart_object_read_one_byte_string (ctx, info);
|
||||
}
|
||||
|
||||
static bool dart_object_decode_string(DartCtx *ctx, DartObjectInfo *info, const ut8 *hdr) {
|
||||
const bool two_byte_cid = dart_cid_is (ctx->layout, info->cid, DART_CID_TWO_BYTE_STRING);
|
||||
const bool string_cid = dart_cid_is (ctx->layout, info->cid, DART_CID_STRING);
|
||||
const bool one_byte_cid = dart_cid_is (ctx->layout, info->cid, DART_CID_ONE_BYTE_STRING);
|
||||
if (!string_cid && !one_byte_cid && !two_byte_cid) {
|
||||
return true;
|
||||
}
|
||||
if (ctx->compressed_word_size == 4) {
|
||||
info->length_raw = r_read_le32 (hdr + 8);
|
||||
} else {
|
||||
info->length_raw = r_read_le64 (hdr + 8);
|
||||
}
|
||||
if (info->length_raw & 1ULL) {
|
||||
return true;
|
||||
}
|
||||
info->length = info->length_raw >> 1;
|
||||
ut64 payload_candidates[2];
|
||||
int npayloads = 0;
|
||||
if (ctx->compressed_word_size == 4) {
|
||||
payload_candidates[npayloads++] = info->addr + 12;
|
||||
payload_candidates[npayloads++] = info->addr + 16;
|
||||
} else {
|
||||
payload_candidates[npayloads++] = info->addr + 16;
|
||||
}
|
||||
for (int i = 0; i < npayloads; i++) {
|
||||
if (two_byte_cid) {
|
||||
if (dart_object_read_string_payload (ctx, info, true, payload_candidates[i])) {
|
||||
return true;
|
||||
}
|
||||
} else if (dart_object_read_string_payload (ctx, info, false, payload_candidates[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dart_object_decode_at(DartCtx *ctx, ut64 addr, DartObjectInfo *out) {
|
||||
if (!ctx || !out) {
|
||||
return false;
|
||||
}
|
||||
memset (out, 0, sizeof (*out));
|
||||
ut8 hdr[32];
|
||||
if (!read_mem (ctx, addr, hdr, sizeof (hdr))) {
|
||||
return false;
|
||||
}
|
||||
out->addr = addr;
|
||||
out->header = r_read_le64 (hdr);
|
||||
out->cid = dart_object_cid_from_header (ctx, out->header);
|
||||
if (out->cid <= 0 || out->cid >= 0xfffff) {
|
||||
return false;
|
||||
}
|
||||
out->cid_name = dart_object_cid_name (ctx, out->cid);
|
||||
out->recognized_cid = R_STR_ISNOTEMPTY (out->cid_name);
|
||||
if (!out->recognized_cid) {
|
||||
return false;
|
||||
}
|
||||
out->valid = true;
|
||||
dart_object_decode_string (ctx, out, hdr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dart_value_add_candidate(DartValueInfo *value, ut64 addr, const char *kind) {
|
||||
if (!value || value->ncandidates >= DART_OBJECT_MAX_CANDIDATES) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < value->ncandidates; i++) {
|
||||
if (value->candidates[i].addr == addr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
DartObjectCandidate *candidate = &value->candidates[value->ncandidates++];
|
||||
candidate->addr = addr;
|
||||
candidate->kind = kind;
|
||||
}
|
||||
|
||||
static void dart_value_add_compressed_candidates(DartCtx *ctx, DartValueInfo *value, ut64 raw) {
|
||||
if (!ctx || ctx->compressed_word_size != 4 || raw > UT32_MAX) {
|
||||
return;
|
||||
}
|
||||
const int tag = dart_object_heap_tag (ctx);
|
||||
ut64 data_base = 0;
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (ctx->iso_data && dart_snapshot_header_read (ctx, ctx->iso_data, &sh) && sh.ok) {
|
||||
ut64 align = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
data_base = ctx->iso_data + ((sh.total_len + (align - 1)) & ~ (align - 1));
|
||||
}
|
||||
ut64 full = (data_base & ~0xffffffffULL) | (raw & 0xffffffffULL);
|
||||
if ((full & (ut64)tag) == (ut64)tag) {
|
||||
dart_value_add_candidate (value, full - (ut64)tag, "compressed_tagged_heap");
|
||||
}
|
||||
dart_value_add_candidate (value, data_base + (raw & ~3ULL), "compressed_data_image");
|
||||
}
|
||||
|
||||
static void dart_value_decode(DartCtx *ctx, ut64 raw, DartValueInfo *value) {
|
||||
memset (value, 0, sizeof (*value));
|
||||
value->raw = raw;
|
||||
value->selected = -1;
|
||||
value->is_smi = (raw & 1ULL) == 0;
|
||||
if (value->is_smi) {
|
||||
value->smi = ((st64)raw) >> 1;
|
||||
}
|
||||
const int tag = dart_object_heap_tag (ctx);
|
||||
if ((raw & (ut64)tag) == (ut64)tag && raw >= (ut64)tag) {
|
||||
dart_value_add_candidate (value, raw - (ut64)tag, "tagged_heap");
|
||||
}
|
||||
dart_value_add_candidate (value, raw, "raw_address");
|
||||
if ((raw & 3ULL) != 0) {
|
||||
dart_value_add_candidate (value, raw & ~3ULL, "low_bits_stripped");
|
||||
}
|
||||
dart_value_add_compressed_candidates (ctx, value, raw);
|
||||
for (int i = 0; i < value->ncandidates; i++) {
|
||||
DartObjectCandidate *candidate = &value->candidates[i];
|
||||
candidate->ok = dart_object_decode_at (ctx, candidate->addr, &candidate->object);
|
||||
if (candidate->ok && value->selected < 0) {
|
||||
value->selected = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool dart_pp_slot_decode(DartCtx *ctx, ut64 offset, DartPpSlotInfo *slot) {
|
||||
memset (slot, 0, sizeof (*slot));
|
||||
slot->present = true;
|
||||
slot->offset = offset;
|
||||
if (!dart_resolve_pp_info (ctx, &slot->pp)) {
|
||||
return false;
|
||||
}
|
||||
if (offset + (ut64)slot->pp.word_size > slot->pp.size) {
|
||||
return false;
|
||||
}
|
||||
slot->addr = slot->pp.base + offset;
|
||||
slot->raw = slot->pp.word_size == 4? (ut64)r_read_le32 (slot->pp.image + offset): r_read_le64 (slot->pp.image + offset);
|
||||
slot->read_ok = true;
|
||||
if (offset >= slot->pp.entries_offset && offset < slot->pp.entry_bits_offset) {
|
||||
ut64 entry_off = offset - slot->pp.entries_offset;
|
||||
if (slot->pp.word_size > 0 && entry_off % (ut64)slot->pp.word_size == 0) {
|
||||
slot->index = entry_off / (ut64)slot->pp.word_size;
|
||||
if (slot->index < slot->pp.length) {
|
||||
slot->bits = slot->pp.image[slot->pp.entry_bits_offset + slot->index];
|
||||
slot->bits_ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slot->bits_ok) {
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (dart_snapshot_header_read (ctx, slot->pp.snapshot_base, &sh) && sh.ok) {
|
||||
const DartModernClusterRequest req = {
|
||||
ctx,
|
||||
sh.cluster_start,
|
||||
slot->pp.snapshot_base + sh.total_len,
|
||||
sh.nc,
|
||||
sh.nb
|
||||
};
|
||||
slot->slot_ok = dart_modern_resolve_pp_slot (&req, offset, &slot->slot);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dart_object_json_object_fields(PJ *pj, const DartObjectInfo *obj) {
|
||||
pj_kn (pj, "addr", obj->addr);
|
||||
pj_kn (pj, "header", obj->header);
|
||||
pj_ki (pj, "cid", obj->cid);
|
||||
pj_ks (pj, "cid_name", obj->cid_name? obj->cid_name: "unknown");
|
||||
pj_kb (pj, "recognized_cid", obj->recognized_cid);
|
||||
if (obj->is_string) {
|
||||
pj_ko (pj, "string");
|
||||
pj_kn (pj, "length_raw", obj->length_raw);
|
||||
pj_kn (pj, "length", obj->length);
|
||||
pj_kn (pj, "payload_addr", obj->payload_addr);
|
||||
pj_kn (pj, "payload_size", obj->payload_size);
|
||||
pj_kb (pj, "two_byte", obj->is_two_byte);
|
||||
pj_kb (pj, "value_truncated", obj->value_truncated);
|
||||
if (obj->value) {
|
||||
pj_ks (pj, "value", obj->value);
|
||||
}
|
||||
pj_end (pj);
|
||||
}
|
||||
}
|
||||
|
||||
static void dart_object_json_value(PJ *pj, const DartValueInfo *value, bool decode_raw) {
|
||||
pj_ko (pj, "value");
|
||||
pj_kn (pj, "raw", value->raw);
|
||||
pj_kb (pj, "decoded", decode_raw);
|
||||
if (!decode_raw) {
|
||||
pj_ks (pj, "kind", "object_pool_ref");
|
||||
} else if (value->selected >= 0) {
|
||||
pj_ks (pj, "kind", "object");
|
||||
pj_ko (pj, "object");
|
||||
dart_object_json_object_fields (pj, &value->candidates[value->selected].object);
|
||||
pj_end (pj);
|
||||
} else if (value->is_smi) {
|
||||
pj_kb (pj, "smi", true);
|
||||
pj_kN (pj, "smi_value", value->smi);
|
||||
pj_ks (pj, "kind", "smi");
|
||||
} else {
|
||||
pj_kb (pj, "smi", false);
|
||||
pj_ks (pj, "kind", "unknown");
|
||||
}
|
||||
pj_ka (pj, "candidates");
|
||||
for (int i = 0; i < value->ncandidates; i++) {
|
||||
const DartObjectCandidate *candidate = &value->candidates[i];
|
||||
pj_o (pj);
|
||||
pj_ks (pj, "kind", candidate->kind);
|
||||
pj_kn (pj, "addr", candidate->addr);
|
||||
pj_kb (pj, "ok", candidate->ok);
|
||||
if (candidate->ok) {
|
||||
pj_ko (pj, "object");
|
||||
pj_kn (pj, "header", candidate->object.header);
|
||||
pj_ki (pj, "cid", candidate->object.cid);
|
||||
pj_ks (pj, "cid_name", candidate->object.cid_name? candidate->object.cid_name: "unknown");
|
||||
pj_end (pj);
|
||||
}
|
||||
pj_end (pj);
|
||||
}
|
||||
pj_end (pj);
|
||||
pj_end (pj);
|
||||
}
|
||||
|
||||
static void dart_object_json_pool_slot(PJ *pj, const DartPpSlotInfo *slot) {
|
||||
pj_ko (pj, "pp_slot");
|
||||
pj_kn (pj, "pp", slot->pp.base);
|
||||
pj_kn (pj, "offset", slot->offset);
|
||||
pj_kn (pj, "addr", slot->addr);
|
||||
pj_kn (pj, "raw", slot->raw);
|
||||
pj_kn (pj, "index", slot->index);
|
||||
pj_kb (pj, "bits_ok", slot->bits_ok);
|
||||
if (slot->bits_ok) {
|
||||
pj_ki (pj, "bits", slot->bits);
|
||||
}
|
||||
pj_kb (pj, "resolved", slot->slot_ok);
|
||||
if (slot->slot_ok) {
|
||||
const DartModernPoolSlotInfo *si = &slot->slot;
|
||||
pj_kn (pj, "cluster_index", si->cluster_index);
|
||||
pj_kn (pj, "pool_index", si->pool_index);
|
||||
pj_kn (pj, "pool_ref", si->pool_ref);
|
||||
pj_kn (pj, "length", si->length);
|
||||
pj_kn (pj, "entry_index", si->index);
|
||||
pj_kn (pj, "pool_offset", si->pool_offset);
|
||||
pj_kn (pj, "pp_offset", si->pp_offset);
|
||||
pj_kn (pj, "stream_offset", si->stream_offset);
|
||||
pj_kn (pj, "value_offset", si->value_offset);
|
||||
pj_ki (pj, "entry_bits", si->bits);
|
||||
pj_ks (pj, "type", si->type_name? si->type_name: "unknown");
|
||||
pj_ks (pj, "patch", si->patch_name? si->patch_name: "unknown");
|
||||
pj_ks (pj, "behavior", si->behavior_name? si->behavior_name: "unknown");
|
||||
if (si->behavior == 0 && si->type == 1) {
|
||||
pj_kn (pj, "ref", si->ref);
|
||||
} else if (si->behavior == 0 && si->type == 0) {
|
||||
pj_kn (pj, "raw_value", si->raw);
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_kind)) {
|
||||
pj_ks (pj, "resolved_kind", si->resolved_kind);
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_name)) {
|
||||
pj_ks (pj, "resolved_name", si->resolved_name);
|
||||
}
|
||||
if (si->resolved_cid >= 0) {
|
||||
pj_ki (pj, "resolved_cid", si->resolved_cid);
|
||||
}
|
||||
if (si->resolved_code_index != UT64_MAX) {
|
||||
pj_kn (pj, "resolved_code_index", si->resolved_code_index);
|
||||
}
|
||||
}
|
||||
pj_end (pj);
|
||||
}
|
||||
|
||||
static char *dart_object_dump_json(const char *spec, const DartValueInfo *value, const DartPpSlotInfo *slot, bool decode_raw) {
|
||||
PJ *pj = pj_new ();
|
||||
if (!pj) {
|
||||
return strdup ("{\"error\":\"Failed to create JSON\"}");
|
||||
}
|
||||
pj_o (pj);
|
||||
pj_ks (pj, "spec", spec? spec: "");
|
||||
if (slot && slot->present) {
|
||||
if (!slot->read_ok) {
|
||||
pj_ks (pj, "kind", "error");
|
||||
pj_ks (pj, "error", "PP slot not resolved");
|
||||
} else if (slot->slot_ok && slot->slot.behavior == 0 && slot->slot.type == 1) {
|
||||
pj_ks (pj, "kind", "object_pool_ref");
|
||||
} else {
|
||||
pj_ks (pj, "kind", "pp_slot");
|
||||
}
|
||||
dart_object_json_pool_slot (pj, slot);
|
||||
} else if (decode_raw && value->selected >= 0) {
|
||||
pj_ks (pj, "kind", "object");
|
||||
} else if (value->is_smi) {
|
||||
pj_ks (pj, "kind", "smi");
|
||||
} else {
|
||||
pj_ks (pj, "kind", "unknown");
|
||||
}
|
||||
dart_object_json_value (pj, value, decode_raw);
|
||||
pj_end (pj);
|
||||
return pj_drain (pj);
|
||||
}
|
||||
|
||||
static void dart_object_text_object(RStrBuf *sb, const DartObjectInfo *obj, const char *prefix) {
|
||||
r_strbuf_appendf (sb, "%saddr: 0x%" PFMT64x "\n", prefix, obj->addr);
|
||||
r_strbuf_appendf (sb, "%sheader: 0x%" PFMT64x "\n", prefix, obj->header);
|
||||
r_strbuf_appendf (sb, "%scid: %d %s\n", prefix, obj->cid, obj->cid_name? obj->cid_name: "unknown");
|
||||
if (obj->is_string) {
|
||||
r_strbuf_appendf (sb, "%sstring.len: %" PRIu64 "\n", prefix, (uint64_t)obj->length);
|
||||
r_strbuf_appendf (sb, "%sstring.raw: 0x%" PFMT64x "\n", prefix, obj->length_raw);
|
||||
r_strbuf_appendf (sb, "%spayload: 0x%" PFMT64x " size=0x%" PFMT64x "\n", prefix, obj->payload_addr, obj->payload_size);
|
||||
r_strbuf_appendf (sb, "%sencoding: %s\n", prefix, obj->is_two_byte? "utf16le": "one-byte");
|
||||
if (obj->value) {
|
||||
r_strbuf_appendf (sb, "%svalue: \"%s%s\"\n", prefix, obj->value, obj->value_truncated? "...": "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dart_object_text_value(RStrBuf *sb, const DartValueInfo *value, bool decode_raw) {
|
||||
r_strbuf_appendf (sb, "raw: 0x%" PFMT64x "\n", value->raw);
|
||||
if (!decode_raw) {
|
||||
r_strbuf_append (sb, "value: object_pool_ref\n");
|
||||
return;
|
||||
}
|
||||
if (value->is_smi) {
|
||||
r_strbuf_appendf (sb, "smi: %" PFMT64d "\n", value->smi);
|
||||
}
|
||||
if (decode_raw && value->selected >= 0) {
|
||||
const DartObjectCandidate *candidate = &value->candidates[value->selected];
|
||||
r_strbuf_appendf (sb, "object: %s\n", candidate->kind);
|
||||
dart_object_text_object (sb, &candidate->object, "");
|
||||
} else if (!value->is_smi) {
|
||||
r_strbuf_append (sb, "object: unresolved\n");
|
||||
}
|
||||
if (value->ncandidates > 0) {
|
||||
r_strbuf_append (sb, "candidates:\n");
|
||||
for (int i = 0; i < value->ncandidates; i++) {
|
||||
const DartObjectCandidate *candidate = &value->candidates[i];
|
||||
r_strbuf_appendf (sb, " - %-22s 0x%" PFMT64x " %s", candidate->kind, candidate->addr, candidate->ok? "ok": "no");
|
||||
if (candidate->ok) {
|
||||
r_strbuf_appendf (sb, " cid=%d %s", candidate->object.cid, candidate->object.cid_name? candidate->object.cid_name: "unknown");
|
||||
}
|
||||
r_strbuf_append (sb, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dart_object_text_pool_slot(RStrBuf *sb, const DartPpSlotInfo *slot) {
|
||||
if (!slot->read_ok) {
|
||||
r_strbuf_append (sb, "pp_slot: unresolved\n");
|
||||
return;
|
||||
}
|
||||
r_strbuf_appendf (sb, "pp: 0x%" PFMT64x "\n", slot->pp.base);
|
||||
r_strbuf_appendf (sb, "pp.offset: 0x%" PFMT64x "\n", slot->offset);
|
||||
r_strbuf_appendf (sb, "pp.addr: 0x%" PFMT64x "\n", slot->addr);
|
||||
r_strbuf_appendf (sb, "pp.raw: 0x%" PFMT64x "\n", slot->raw);
|
||||
if (slot->bits_ok) {
|
||||
r_strbuf_appendf (sb, "pp.index: %" PRIu64 "\n", (uint64_t)slot->index);
|
||||
r_strbuf_appendf (sb, "pp.bits: 0x%02x\n", (unsigned int)slot->bits);
|
||||
}
|
||||
if (slot->slot_ok) {
|
||||
const DartModernPoolSlotInfo *si = &slot->slot;
|
||||
r_strbuf_appendf (sb, "pool.ref: %" PRIu64 "\n", (uint64_t)si->pool_ref);
|
||||
r_strbuf_appendf (sb, "pool.index: %" PRIu64 "\n", (uint64_t)si->pool_index);
|
||||
r_strbuf_appendf (sb, "entry.index: %" PRIu64 "\n", (uint64_t)si->index);
|
||||
r_strbuf_appendf (sb, "entry.stream: 0x%" PFMT64x "\n", si->stream_offset);
|
||||
r_strbuf_appendf (sb, "entry.value: 0x%" PFMT64x "\n", si->value_offset);
|
||||
r_strbuf_appendf (sb, "entry.type: %s\n", si->type_name? si->type_name: "unknown");
|
||||
r_strbuf_appendf (sb, "entry.patch: %s\n", si->patch_name? si->patch_name: "unknown");
|
||||
r_strbuf_appendf (sb, "entry.behavior: %s\n", si->behavior_name? si->behavior_name: "unknown");
|
||||
if (si->behavior == 0 && si->type == 1) {
|
||||
r_strbuf_appendf (sb, "target.ref: %" PRIu64 "\n", (uint64_t)si->ref);
|
||||
} else if (si->behavior == 0 && si->type == 0) {
|
||||
r_strbuf_appendf (sb, "target.raw: 0x%" PFMT64x "\n", si->raw);
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_kind)) {
|
||||
r_strbuf_appendf (sb, "target.kind: %s\n", si->resolved_kind);
|
||||
}
|
||||
if (si->resolved_cid >= 0) {
|
||||
r_strbuf_appendf (sb, "target.cid: %d\n", si->resolved_cid);
|
||||
}
|
||||
if (si->resolved_code_index != UT64_MAX) {
|
||||
r_strbuf_appendf (sb, "target.code: %" PRIu64 "\n", (uint64_t)si->resolved_code_index);
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_name)) {
|
||||
r_strbuf_appendf (sb, "target.name: \"%s\"\n", si->resolved_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *dart_object_dump_text(const char *spec, const DartValueInfo *value, const DartPpSlotInfo *slot, bool decode_raw, bool quiet) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (quiet) {
|
||||
if (slot && slot->present && slot->slot_ok && R_STR_ISNOTEMPTY (slot->slot.resolved_name)) {
|
||||
r_strbuf_appendf (sb, "%s", slot->slot.resolved_name);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
if (decode_raw && value->selected >= 0) {
|
||||
const DartObjectInfo *obj = &value->candidates[value->selected].object;
|
||||
if (obj->is_string && obj->value) {
|
||||
r_strbuf_appendf (sb, "%s", obj->value);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
}
|
||||
r_strbuf_appendf (sb, "raw=0x%" PFMT64x, value->raw);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
r_strbuf_appendf (sb, "spec: %s\n", spec? spec: "");
|
||||
if (slot && slot->present) {
|
||||
dart_object_text_pool_slot (sb, slot);
|
||||
}
|
||||
dart_object_text_value (sb, value, decode_raw);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
static char *dart_object_dump_r2(const DartValueInfo *value, const DartPpSlotInfo *slot, bool decode_raw) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (slot && slot->present && slot->read_ok) {
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart PP slot offset=0x%" PFMT64x " raw=0x%" PFMT64x, slot->addr, slot->offset, slot->raw);
|
||||
if (slot->slot_ok) {
|
||||
const DartModernPoolSlotInfo *si = &slot->slot;
|
||||
r_strbuf_appendf (sb, " entry=%" PRIu64 " type=%s behavior=%s", (uint64_t)si->index, si->type_name? si->type_name: "unknown", si->behavior_name? si->behavior_name: "unknown");
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_kind)) {
|
||||
r_strbuf_appendf (sb, " resolved=%s", si->resolved_kind);
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_name)) {
|
||||
char *escaped = r_str_escape_utf8 (si->resolved_name, false, true);
|
||||
r_strbuf_appendf (sb, " name=\"%s\"", escaped);
|
||||
free (escaped);
|
||||
}
|
||||
}
|
||||
r_strbuf_append (sb, "\n");
|
||||
}
|
||||
if (decode_raw && value->selected >= 0) {
|
||||
const DartObjectInfo *obj = &value->candidates[value->selected].object;
|
||||
r_strbuf_appendf (sb, "f dart.object 1 @ 0x%" PFMT64x "\n", obj->addr);
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart object cid=%d %s", obj->addr, obj->cid, obj->cid_name? obj->cid_name: "unknown");
|
||||
if (obj->is_string && obj->value) {
|
||||
char *escaped = r_str_escape_utf8 (obj->value, false, true);
|
||||
r_strbuf_appendf (sb, " len=%" PRIu64 " value=\"%s%s\"", (uint64_t)obj->length, escaped, obj->value_truncated? "...": "");
|
||||
free (escaped);
|
||||
}
|
||||
r_strbuf_append (sb, "\n");
|
||||
} else if (decode_raw && value->is_smi) {
|
||||
r_strbuf_appendf (sb, "'CC Dart Smi raw=0x%" PFMT64x " value=%" PFMT64d "\n", value->raw, value->smi);
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
char *dart_pool_dump_object(DartCtx *ctx, const char *spec, int fmt) {
|
||||
if (!ctx || R_STR_ISEMPTY (spec)) {
|
||||
if (fmt == 'j') {
|
||||
return strdup ("{\"error\":\"missing object spec\"}");
|
||||
}
|
||||
return fmt == 'r'? strdup ("# Error: missing object spec\n"): strdup ("Error: missing object spec\n");
|
||||
}
|
||||
if (find_snapshots (ctx) != 0) {
|
||||
if (fmt == 'j') {
|
||||
return strdup ("{\"error\":\"Dart snapshots not found\"}");
|
||||
}
|
||||
return fmt == 'r'? strdup ("# Error: Dart snapshots not found\n"): strdup ("Error: Dart snapshots not found\n");
|
||||
}
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = ctx->layout? NULL: dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
ut64 raw = 0;
|
||||
ut64 pp_offset = 0;
|
||||
bool is_pp = dart_object_parse_pp_spec (spec, &pp_offset);
|
||||
if (!is_pp) {
|
||||
if (!dart_object_parse_u64 (spec, &raw)) {
|
||||
if (layout_owned) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
if (fmt == 'j') {
|
||||
return strdup ("{\"error\":\"invalid object spec\"}");
|
||||
}
|
||||
return fmt == 'r'? strdup ("# Error: invalid object spec\n"): strdup ("Error: invalid object spec\n");
|
||||
}
|
||||
if (raw >= DART_OBJECT_SYNTHETIC_PP_BASE) {
|
||||
pp_offset = raw - DART_OBJECT_SYNTHETIC_PP_BASE;
|
||||
is_pp = true;
|
||||
}
|
||||
}
|
||||
DartValueInfo value;
|
||||
memset (&value, 0, sizeof (value));
|
||||
value.selected = -1;
|
||||
DartPpSlotInfo slot;
|
||||
memset (&slot, 0, sizeof (slot));
|
||||
bool decode_raw = true;
|
||||
if (is_pp) {
|
||||
if (dart_pp_slot_decode (ctx, pp_offset, &slot)) {
|
||||
raw = slot.slot_ok && slot.slot.behavior == 0 && slot.slot.type == 0? slot.slot.raw: slot.raw;
|
||||
decode_raw = ! (slot.slot_ok && slot.slot.behavior == 0 && slot.slot.type == 1);
|
||||
}
|
||||
}
|
||||
dart_value_decode (ctx, raw, &value);
|
||||
char *out = NULL;
|
||||
if (fmt == 'j') {
|
||||
out = dart_object_dump_json (spec, &value, is_pp? &slot: NULL, decode_raw);
|
||||
} else if (fmt == 'r') {
|
||||
out = dart_object_dump_r2 (&value, is_pp? &slot: NULL, decode_raw);
|
||||
} else {
|
||||
out = dart_object_dump_text (spec, &value, is_pp? &slot: NULL, decode_raw, ctx->quiet);
|
||||
}
|
||||
dart_value_info_fini (&value);
|
||||
dart_pp_slot_info_fini (&slot);
|
||||
if (layout_owned) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -2364,6 +2364,112 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dart_modern_pool_slot_info_fini(DartModernPoolSlotInfo *info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
free (info->resolved_name);
|
||||
memset (info, 0, sizeof (*info));
|
||||
info->resolved_cid = -1;
|
||||
info->resolved_code_index = UT64_MAX;
|
||||
}
|
||||
|
||||
static void modern_pool_slot_info_set_resolved(DartModernPoolSlotInfo *out, const ModernResolvedRef *resolved) {
|
||||
if (!out || !resolved) {
|
||||
return;
|
||||
}
|
||||
out->resolved_kind = resolved->kind;
|
||||
out->resolved_cid = resolved->cid;
|
||||
out->resolved_code_index = resolved->code_index;
|
||||
if (R_STR_ISNOTEMPTY (resolved->name)) {
|
||||
out->resolved_name = strdup (resolved->name);
|
||||
}
|
||||
}
|
||||
|
||||
bool dart_modern_resolve_pp_slot(const DartModernClusterRequest *req, ut64 pp_offset, DartModernPoolSlotInfo *out) {
|
||||
DartCtx *ctx = req? req->ctx: NULL;
|
||||
if (!ctx || !out || !dart_modern_is_supported_snapshot (ctx)) {
|
||||
return false;
|
||||
}
|
||||
memset (out, 0, sizeof (*out));
|
||||
out->resolved_cid = -1;
|
||||
out->resolved_code_index = UT64_MAX;
|
||||
ModernClusterMeta *meta = modern_parse_cluster_meta (ctx, req->cluster_start, req->cluster_end, req->num_clusters, req->num_base_objects);
|
||||
if (!meta) {
|
||||
return false;
|
||||
}
|
||||
ModernRefResolver resolver = { 0 };
|
||||
bool have_resolver = modern_ref_resolver_init (&resolver, ctx, meta, req->num_clusters, req->num_base_objects);
|
||||
const ModernCidCache cids = modern_cid_cache_init (ctx->layout);
|
||||
bool done = false;
|
||||
bool ok = false;
|
||||
for (ut64 i = 0; i < req->num_clusters && !done; i++) {
|
||||
ModernClusterMeta *m = &meta[i];
|
||||
if (m->fill_kind != MODERN_FILL_OBJECT_POOL || !m->fill_parsed || !m->fill_ok || m->fill_offset >= m->fill_end || !m->count) {
|
||||
continue;
|
||||
}
|
||||
ClusterStream s = {
|
||||
.ctx = ctx,
|
||||
.cursor = m->fill_offset,
|
||||
.end = m->fill_end,
|
||||
};
|
||||
for (ut64 pool_index = 0; pool_index < m->count && !done; pool_index++) {
|
||||
ut64 length = 0;
|
||||
if (!cs_read_unsigned (&s, &length)) {
|
||||
break;
|
||||
}
|
||||
done = true;
|
||||
for (ut64 entry_index = 0; entry_index < length; entry_index++) {
|
||||
ModernPoolEntry entry = {
|
||||
.index = entry_index,
|
||||
.stream_offset = s.cursor,
|
||||
};
|
||||
if (!cs_read_u8 (&s, &entry.bits)) {
|
||||
break;
|
||||
}
|
||||
entry.type = entry.bits & 0x0f;
|
||||
entry.patch = (entry.bits >> 4) & 1;
|
||||
entry.behavior = entry.bits >> 5;
|
||||
entry.value_offset = s.cursor;
|
||||
if (!modern_decode_pool_entry (&s, ctx, entry.type, entry.behavior, &entry.ref, &entry.raw)) {
|
||||
break;
|
||||
}
|
||||
ut64 entry_pp = modern_pool_entry_pp_offset (ctx, entry_index);
|
||||
if (entry_pp != pp_offset) {
|
||||
continue;
|
||||
}
|
||||
modern_resolve_pool_entry (have_resolver? &resolver: NULL, &cids, entry.type, entry.behavior, entry.ref, &entry.resolved);
|
||||
out->valid = true;
|
||||
out->cluster_index = i;
|
||||
out->pool_index = pool_index;
|
||||
out->pool_ref = m->start_ref + pool_index;
|
||||
out->length = length;
|
||||
out->index = entry_index;
|
||||
out->pool_offset = modern_pool_entry_pool_offset (ctx, entry_index);
|
||||
out->pp_offset = entry_pp;
|
||||
out->stream_offset = entry.stream_offset;
|
||||
out->value_offset = entry.value_offset;
|
||||
out->ref = entry.ref;
|
||||
out->raw = entry.raw;
|
||||
out->bits = entry.bits;
|
||||
out->type = entry.type;
|
||||
out->patch = entry.patch;
|
||||
out->behavior = entry.behavior;
|
||||
out->type_name = modern_pool_entry_type_name (entry.type);
|
||||
out->patch_name = modern_pool_entry_patch_name (entry.patch);
|
||||
out->behavior_name = modern_pool_entry_behavior_name (entry.behavior);
|
||||
modern_pool_slot_info_set_resolved (out, &entry.resolved);
|
||||
modern_resolved_ref_fini (&entry.resolved);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
modern_ref_resolver_fini (&resolver);
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool modern_object_pool_string_ref_exists(RList *refs, ut64 pool_ref, ut64 entry_index) {
|
||||
if (!refs) {
|
||||
return false;
|
||||
|
||||
@@ -934,7 +934,7 @@ char *dart_pool_dump_header_deep(DartCtx *ctx, int fmt) {
|
||||
return dart_pool_dump_header_ext_level (ctx, fmt, 3);
|
||||
}
|
||||
|
||||
static void dart_pp_info_fini(DartPpInfo *info) {
|
||||
void dart_pp_info_fini(DartPpInfo *info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
@@ -956,7 +956,7 @@ static bool resolve_pp_from_snapshot(DartCtx *ctx, ut64 snapshot_base, const cha
|
||||
return dart_modern_build_synthetic_pp (&req, snapshot_base, label, data_image_base, info);
|
||||
}
|
||||
|
||||
static bool resolve_pp_info(DartCtx *ctx, DartPpInfo *info) {
|
||||
bool dart_resolve_pp_info(DartCtx *ctx, DartPpInfo *info) {
|
||||
if (!ctx || !ctx->core || !info) {
|
||||
return false;
|
||||
}
|
||||
@@ -965,12 +965,14 @@ static bool resolve_pp_info(DartCtx *ctx, DartPpInfo *info) {
|
||||
return false;
|
||||
}
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
DartVerLayout *layout_owned = ctx->layout? NULL: dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
bool ok = resolve_pp_from_snapshot (ctx, ctx->iso_data, "Isolate", info);
|
||||
if (!ok) {
|
||||
ok = resolve_pp_from_snapshot (ctx, ctx->vm_data, "VM", info);
|
||||
}
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
if (layout_owned) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -1061,7 +1063,7 @@ static char *dump_pp_text(const DartPpInfo *info, bool quiet) {
|
||||
|
||||
char *dart_pool_dump_pp(DartCtx *ctx, int fmt) {
|
||||
DartPpInfo info = { 0 };
|
||||
if (!resolve_pp_info (ctx, &info)) {
|
||||
if (!dart_resolve_pp_info (ctx, &info)) {
|
||||
if (fmt == 'j') {
|
||||
return strdup ("{\"error\":\"PP not resolved\",\"reason\":\"ObjectPool fill payload was not decoded\"}");
|
||||
}
|
||||
|
||||
@@ -150,6 +150,32 @@ typedef struct {
|
||||
ut8 *image;
|
||||
} DartPpInfo;
|
||||
|
||||
typedef struct {
|
||||
bool valid;
|
||||
ut64 cluster_index;
|
||||
ut64 pool_index;
|
||||
ut64 pool_ref;
|
||||
ut64 length;
|
||||
ut64 index;
|
||||
ut64 pool_offset;
|
||||
ut64 pp_offset;
|
||||
ut64 stream_offset;
|
||||
ut64 value_offset;
|
||||
ut64 ref;
|
||||
ut64 raw;
|
||||
ut8 bits;
|
||||
ut8 type;
|
||||
ut8 patch;
|
||||
ut8 behavior;
|
||||
const char *type_name;
|
||||
const char *patch_name;
|
||||
const char *behavior_name;
|
||||
const char *resolved_kind;
|
||||
char *resolved_name;
|
||||
int resolved_cid;
|
||||
ut64 resolved_code_index;
|
||||
} DartModernPoolSlotInfo;
|
||||
|
||||
typedef void(*DartInstructionTableEntryCallback)(const DartInstructionTableEntry *entry, void *user);
|
||||
|
||||
typedef struct {
|
||||
@@ -233,6 +259,8 @@ bool modern_skip_n_bytes(ClusterStream *s, ut64 len);
|
||||
bool dart_modern_is_supported_snapshot(DartCtx *ctx);
|
||||
bool dart_modern_emit_cluster_summary(const DartModernClusterSummaryRequest *req);
|
||||
bool dart_modern_build_synthetic_pp(const DartModernClusterRequest *req, ut64 snapshot_base, const char *snapshot_label, ut64 data_image_base, DartPpInfo *out);
|
||||
bool dart_modern_resolve_pp_slot(const DartModernClusterRequest *req, ut64 pp_offset, DartModernPoolSlotInfo *out);
|
||||
void dart_modern_pool_slot_info_fini(DartModernPoolSlotInfo *info);
|
||||
bool dart_modern_extract_object_pool_strings_from_clusters(const DartModernClusterRequest *req, RList *strings, HtUP *seen_refs, ut64 *ref_counter);
|
||||
bool dart_modern_scan_names_from_clusters(DartCtx *ctx, ut64 cluster_start, ut64 cluster_end, ut64 num_clusters, ut64 itlen);
|
||||
bool dart_modern_extract_classes_from_clusters(const DartModernClusterRequest *req, RList *class_list);
|
||||
@@ -249,4 +277,7 @@ int dart_it_emit_linear(const DartItEmitRequest *req);
|
||||
int dart_it_emit_fixed(const DartItEmitRequest *req);
|
||||
int dart_it_emit_varint(const DartItEmitRequest *req);
|
||||
|
||||
bool dart_resolve_pp_info(DartCtx *ctx, DartPpInfo *info);
|
||||
void dart_pp_info_fini(DartPpInfo *info);
|
||||
|
||||
#endif
|
||||
+21
-1
@@ -20,6 +20,7 @@ typedef struct {
|
||||
bool string_refs;
|
||||
bool help;
|
||||
char *obf_map_path;
|
||||
char *object_spec;
|
||||
} R2FlutterCmd;
|
||||
|
||||
static bool r2flutter_core_init(RCorePluginSession *cps) {
|
||||
@@ -69,6 +70,7 @@ static void r2flutter_help(RCore *core) {
|
||||
"| r2flutter -HHH decode selected cluster payloads for diagnostics\n"
|
||||
"| r2flutter -h show this help\n"
|
||||
"| r2flutter -i dump instruction table entries\n"
|
||||
"| r2flutter -O addr decode Dart tagged/object pointer or ObjectPool PP slot\n"
|
||||
"| r2flutter -p print reconstructed ObjectPool PP value\n"
|
||||
"| r2flutter -R dump full radare2 script (like standalone -R)\n"
|
||||
"| r2flutter -S dump best-effort recovered SBOM/components\n"
|
||||
@@ -161,6 +163,7 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
cmd->string_refs = false;
|
||||
cmd->help = false;
|
||||
cmd->obf_map_path = NULL;
|
||||
cmd->object_spec = NULL;
|
||||
char *dup = strdup (args);
|
||||
int nwords = r_str_word_set0 (dup);
|
||||
char **words = R_NEWS0 (char *, nwords);
|
||||
@@ -211,6 +214,19 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
dctx->dump_it = true;
|
||||
cmd->action = flag;
|
||||
break;
|
||||
case 'O':
|
||||
{
|
||||
const char *arg = r2flutter_opt_arg (tail, words, nwords, &i);
|
||||
if (!arg) {
|
||||
cmd->help = true;
|
||||
break;
|
||||
}
|
||||
free (cmd->object_spec);
|
||||
cmd->object_spec = strdup (arg);
|
||||
cmd->action = flag;
|
||||
j += strlen (tail);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
cmd->action = flag;
|
||||
break;
|
||||
@@ -284,7 +300,7 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
cmd->help = true;
|
||||
break;
|
||||
}
|
||||
if (cmd->help || (R_STR_ISNOTEMPTY (tail) && (flag == 'l' || flag == 'm'))) {
|
||||
if (cmd->help || (R_STR_ISNOTEMPTY (tail) && (flag == 'l' || flag == 'm' || flag == 'O'))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -335,6 +351,9 @@ static bool r2flutter_run_cmd(RCore *core, DartCtx *dctx, const R2FlutterCmd *cm
|
||||
case 'i':
|
||||
out = dart_pool_dump_it (dctx, cmd->fmt);
|
||||
break;
|
||||
case 'O':
|
||||
out = dart_pool_dump_object (dctx, cmd->object_spec, cmd->fmt);
|
||||
break;
|
||||
case 'p':
|
||||
out = dart_pool_dump_pp (dctx, cmd->fmt);
|
||||
break;
|
||||
@@ -401,6 +420,7 @@ static bool r_cmd_r2flutter_call(RCorePluginSession *cps, const char *input) {
|
||||
ret = r2flutter_run_cmd (core, &dctx, &cmd);
|
||||
}
|
||||
free (cmd.obf_map_path);
|
||||
free (cmd.object_spec);
|
||||
return ret;
|
||||
}
|
||||
r2flutter_help (core);
|
||||
|
||||
+10
-1
@@ -30,6 +30,7 @@ static const char usage_text[] =
|
||||
" -HH Print extended snapshot header and cluster layout\n"
|
||||
" -HHH Decode selected cluster payloads for diagnostics\n"
|
||||
" -i Print instruction table entries to stdout\n"
|
||||
" -O <addr|pp+off> Decode Dart tagged/object pointer or ObjectPool PP slot\n"
|
||||
" -p Print reconstructed ObjectPool PP value\n"
|
||||
" -R Print radare2 script for snapshot analysis\n"
|
||||
" -S Print best-effort recovered SBOM/components\n"
|
||||
@@ -111,11 +112,12 @@ int main(int argc, char **argv) {
|
||||
int analysis_depth = 0;
|
||||
int header_depth = 0;
|
||||
int string_depth = 0;
|
||||
const char *object_spec = NULL;
|
||||
DartCtx dctx = {
|
||||
.no_stubs = true
|
||||
};
|
||||
RGetopt opt;
|
||||
r_getopt_init (&opt, argc, (const char **)argv, "AcfhHijnm:pqrRSzTvVxl:");
|
||||
r_getopt_init (&opt, argc, (const char **)argv, "AcfhHijnm:O:pqrRSzTvVxl:");
|
||||
int c;
|
||||
while ((c = r_getopt_next (&opt)) != -1) {
|
||||
switch (c) {
|
||||
@@ -140,6 +142,10 @@ int main(int argc, char **argv) {
|
||||
action = c;
|
||||
dctx.dump_it = true;
|
||||
break;
|
||||
case 'O':
|
||||
action = c;
|
||||
object_spec = opt.arg;
|
||||
break;
|
||||
case 'p':
|
||||
action = c;
|
||||
break;
|
||||
@@ -305,6 +311,9 @@ int main(int argc, char **argv) {
|
||||
case 'i':
|
||||
output = dart_pool_dump_it (&app->dctx, fmt);
|
||||
break;
|
||||
case 'O':
|
||||
output = dart_pool_dump_object (&app->dctx, object_spec, fmt);
|
||||
break;
|
||||
case 'p':
|
||||
output = dart_pool_dump_pp (&app->dctx, fmt);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "sbom-synthetic",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-q", "-S", "bins/synthetic/field_snapshot.bin"],
|
||||
"timeout": 10,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"dart-package\ttest\t-\tlibrary_cluster\t90",
|
||||
"runtime\tdart-sdk\t-\tsnapshot_header\t70"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
NAME=object-inspect-tagged-android
|
||||
FILE=-
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -q -j -O 0x1651 bins/android/first 2>/dev/null | python3 -c 'import json, sys; j = json.load (sys.stdin); o = j["value"]["object"]; s = o["string"]; print ("%s addr=0x%x cid=%d len=%d value=%s" % (j["kind"], o["addr"], o["cid"], s["length"], s["value"]))'
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
object addr=0x1650 cid=93 len=11 value=_Uint16List
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=object-inspect-pp-mafia
|
||||
FILE=-
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -q -j -O pp+0x68 bins/android/mafia 2>/dev/null | python3 -c 'import json, sys; j = json.load (sys.stdin); p = j["pp_slot"]; v = j["value"]; print ("%s index=%d type=%s kind=%s cid=%d name=%s decoded=%s" % (j["kind"], p["entry_index"], p["type"], p["resolved_kind"], p["resolved_cid"], p["resolved_name"], v["decoded"]))'
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
object_pool_ref index=11 type=tagged_object kind=string cid=93 name=getCallbackHandle decoded=False
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=object-inspect-plugin-android
|
||||
FILE=-
|
||||
CMDS=<<EOF
|
||||
!!sh -c "p=../src/r2/core_flutter.dylib; [ -f \"\$p\" ] || p=../src/r2/core_flutter.so; r2 -NN -q -i \"\$p\" -qc \"r2flutter -q -O 0x1651\" bins/android/first/libapp.so 2>/dev/null"
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
_Uint16List
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,26 @@
|
||||
NAME=sbom-synthetic
|
||||
FILE=-
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -S bins/synthetic/field_snapshot.bin 2>/dev/null | sed -n '1,8p'
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
# Dart/Flutter Recovered Components
|
||||
# complete: false
|
||||
# versions: explicit metadata only; AOT snapshots usually do not serialize pub package versions
|
||||
snapshot_hash: 00000000000000000000000000000000
|
||||
dart_version: unknown
|
||||
components: 2
|
||||
dart-package test - confidence=90 source=library_cluster occurrences=3 evidence=package:test
|
||||
runtime dart-sdk - confidence=70 source=snapshot_header evidence=snapshot_hash=00000000000000000000000000000000
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=json-sbom-synthetic
|
||||
FILE=-
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -jS bins/synthetic/field_snapshot.bin 2>/dev/null
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
{"format":"r2flutter-recovered-sbom","complete":false,"note":"Best-effort component report. Dart package versions are null unless explicit packaged metadata is present.","input":"bins/synthetic/field_snapshot.bin","snapshot":{"hash":"00000000000000000000000000000000","dart_version":"unknown","vm_data":4,"iso_data":4},"count":2,"components":[{"type":"dart-package","name":"test","version":null,"confidence":90,"occurrences":3,"source":"library_cluster","evidence":"package:test"},{"type":"runtime","name":"dart-sdk","version":null,"confidence":70,"occurrences":1,"source":"snapshot_header","evidence":"snapshot_hash=00000000000000000000000000000000"}]}
|
||||
EOF
|
||||
RUN
|
||||
Reference in new issue
Block a user