mirror of
https://github.com/radareorg/r2unity.git
synced 2026-11-07 06:36:00 -10:00
Add -R/-R to list imports/exports from managed code (PInvoke vs PInvokeCallback)
This commit is contained in:
1 parent
c942c21060
commit
d8c9ab4d88
3 files changed
+466
-6
No files matched your search
+372
@@ -668,6 +668,378 @@ R_API R2UnityInterop *r2unity_enumerate_pinvokes (R2UnityMetadata *meta, size_t
|
||||
return out;
|
||||
}
|
||||
|
||||
/* v29+ attribute data range: { uint32 token, uint32 startOffset }. The end of
|
||||
* an entry's BLOB is the next entry's startOffset, or attributeDataSize for
|
||||
* the last entry. See doc/pinvoke.md §4.1 and Il2CppDumper Metadata.cs:131. */
|
||||
typedef struct {
|
||||
uint32_t token;
|
||||
uint32_t start_offset;
|
||||
} R2UAttrRange;
|
||||
|
||||
static R2UAttrRange *load_attribute_ranges (R2UnityMetadata *meta, size_t *count,
|
||||
ut64 *out_data_off, ut64 *out_data_size) {
|
||||
*count = 0;
|
||||
*out_data_off = 0;
|
||||
*out_data_size = 0;
|
||||
if (meta->version < 29) {
|
||||
return NULL;
|
||||
}
|
||||
/* v29+ repurposes the v27-era fields at this position:
|
||||
* attributesInfoOffset/Size -> attributeDataOffset/Size
|
||||
* attributeTypesOffset/Size -> attributeDataRangeOffset/Size
|
||||
* The byte positions are identical; only the semantics changed. */
|
||||
ut64 data_off = meta->header.v29.attributesInfoOffset;
|
||||
ut64 data_size = meta->header.v29.attributesInfoSize;
|
||||
ut64 range_off = meta->header.v29.attributeTypesOffset;
|
||||
ut64 range_size = meta->header.v29.attributeTypesSize;
|
||||
if (!range_size || (range_size % 8) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
size_t n = range_size / 8;
|
||||
R2UAttrRange *ranges = R_NEWS (R2UAttrRange, n);
|
||||
if (!ranges) {
|
||||
return NULL;
|
||||
}
|
||||
ut8 *buf = R_NEWS (ut8, range_size);
|
||||
if (!buf) {
|
||||
R_FREE (ranges);
|
||||
return NULL;
|
||||
}
|
||||
if (r_buf_read_at (meta->buf, range_off, buf, range_size) != (st64) range_size) {
|
||||
R_FREE (buf);
|
||||
R_FREE (ranges);
|
||||
return NULL;
|
||||
}
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
ranges[i].token = r_read_le32 (buf + i * 8 + 0);
|
||||
ranges[i].start_offset = r_read_le32 (buf + i * 8 + 4);
|
||||
}
|
||||
R_FREE (buf);
|
||||
*count = n;
|
||||
*out_data_off = data_off;
|
||||
*out_data_size = data_size;
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/* ECMA-335 §II.23.2 compressed uint32. Returns 0 on malformed input. */
|
||||
static uint32_t read_compressed_uint (const ut8 *buf, size_t bufsize, size_t *pos) {
|
||||
if (*pos >= bufsize) {
|
||||
return 0;
|
||||
}
|
||||
ut8 b0 = buf[*pos];
|
||||
if ((b0 & 0x80) == 0) {
|
||||
*pos += 1;
|
||||
return b0;
|
||||
}
|
||||
if ((b0 & 0xC0) == 0x80) {
|
||||
if (*pos + 2 > bufsize) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t v = ((uint32_t) (b0 & 0x3F) << 8) | buf[*pos + 1];
|
||||
*pos += 2;
|
||||
return v;
|
||||
}
|
||||
if (*pos + 4 > bufsize) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t v = ((uint32_t) (b0 & 0x1F) << 24)
|
||||
| ((uint32_t) buf[*pos + 1] << 16)
|
||||
| ((uint32_t) buf[*pos + 2] << 8)
|
||||
| buf[*pos + 3];
|
||||
*pos += 4;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* For attribute entry at `range_idx` (v29+), read the ctor method indices
|
||||
* (first 4 bytes after count header) and check if any ctor's declaring type
|
||||
* matches the target attribute type name.
|
||||
*
|
||||
* Returns the matched attribute kind (R2U_INTEROP_REVERSE_PINVOKE or
|
||||
* R2U_INTEROP_UNMANAGED_ONLY), or 0 if no match. */
|
||||
static uint8_t attribute_range_interop_kind (
|
||||
R2UnityMetadata *meta,
|
||||
const R2UAttrRange *ranges, size_t nranges,
|
||||
size_t range_idx,
|
||||
ut64 attr_data_off, ut64 attr_data_size,
|
||||
const Il2CppMethodDefinition *methods, size_t nmethods,
|
||||
const Il2CppTypeDefinition *types, size_t ntypes) {
|
||||
if (range_idx >= nranges) {
|
||||
return 0;
|
||||
}
|
||||
ut64 blob_start = ranges[range_idx].start_offset;
|
||||
ut64 blob_end = (range_idx + 1 < nranges)
|
||||
? ranges[range_idx + 1].start_offset
|
||||
: attr_data_size;
|
||||
if (blob_end <= blob_start || blob_end > attr_data_size) {
|
||||
return 0;
|
||||
}
|
||||
ut64 blob_len = blob_end - blob_start;
|
||||
if (blob_len > 0x10000) {
|
||||
return 0;
|
||||
}
|
||||
ut8 *blob = R_NEWS (ut8, (size_t) blob_len);
|
||||
if (!blob) {
|
||||
return 0;
|
||||
}
|
||||
if (r_buf_read_at (meta->buf, attr_data_off + blob_start, blob, blob_len) != (st64) blob_len) {
|
||||
R_FREE (blob);
|
||||
return 0;
|
||||
}
|
||||
size_t pos = 0;
|
||||
uint32_t n_attrs = read_compressed_uint (blob, blob_len, &pos);
|
||||
if (n_attrs == 0 || (ut64) pos + (ut64) n_attrs * 4 > blob_len) {
|
||||
R_FREE (blob);
|
||||
return 0;
|
||||
}
|
||||
uint8_t kind = 0;
|
||||
for (uint32_t i = 0; i < n_attrs; i++) {
|
||||
uint32_t ctor_idx = r_read_le32 (blob + pos + i * 4);
|
||||
if (ctor_idx >= nmethods) {
|
||||
continue;
|
||||
}
|
||||
int32_t declaring = methods[ctor_idx].declaringType;
|
||||
if (declaring < 0 || (size_t) declaring >= ntypes) {
|
||||
continue;
|
||||
}
|
||||
const char *name = r2unity_get_string (meta, types[declaring].nameIndex);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp (name, "MonoPInvokeCallbackAttribute")) {
|
||||
kind = R2U_INTEROP_REVERSE_PINVOKE;
|
||||
free ((void *) name);
|
||||
break;
|
||||
}
|
||||
if (!strcmp (name, "UnmanagedCallersOnlyAttribute")) {
|
||||
kind = R2U_INTEROP_UNMANAGED_ONLY;
|
||||
free ((void *) name);
|
||||
break;
|
||||
}
|
||||
free ((void *) name);
|
||||
}
|
||||
R_FREE (blob);
|
||||
return kind;
|
||||
}
|
||||
|
||||
R_API R2UnityInterop *r2unity_enumerate_reverse_pinvokes (R2UnityMetadata *meta, size_t *count) {
|
||||
if (!meta || !count) {
|
||||
return NULL;
|
||||
}
|
||||
*count = 0;
|
||||
if (meta->version < 29) {
|
||||
/* Pre-v29: attribute args live in native generator stubs; see doc/pinvoke.md §4.2. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t method_count = 0;
|
||||
Il2CppMethodDefinition *methods = r2unity_get_method_definitions (meta, &method_count);
|
||||
if (!methods || !method_count) {
|
||||
R_FREE (methods);
|
||||
return NULL;
|
||||
}
|
||||
size_t type_count = 0;
|
||||
Il2CppTypeDefinition *types = r2unity_get_type_definitions (meta, &type_count);
|
||||
size_t image_count = 0;
|
||||
Il2CppImageDefinition *images = r2unity_get_images (meta, &image_count);
|
||||
|
||||
ut64 attr_data_off = 0, attr_data_size = 0;
|
||||
size_t nranges = 0;
|
||||
R2UAttrRange *ranges = load_attribute_ranges (meta, &nranges, &attr_data_off, &attr_data_size);
|
||||
if (!ranges || !nranges) {
|
||||
R_FREE (methods);
|
||||
R_FREE (types);
|
||||
R_FREE (images);
|
||||
R_FREE (ranges);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* typeIndex -> imageIndex map for naming and scoping. */
|
||||
int *type2img = NULL;
|
||||
if (images && type_count) {
|
||||
type2img = R_NEWS (int, type_count);
|
||||
if (type2img) {
|
||||
for (size_t ti = 0; ti < type_count; ti++) {
|
||||
type2img[ti] = -1;
|
||||
}
|
||||
for (size_t ii = 0; ii < image_count; ii++) {
|
||||
int start = images[ii].typeStart;
|
||||
int tcount = (int) images[ii].typeCount;
|
||||
if (start < 0 || tcount <= 0) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < tcount && (size_t) (start + k) < type_count; k++) {
|
||||
type2img[start + k] = (int) ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Build per-image sorted (token, method_idx) index. Method tokens are
|
||||
* only unique within their owning image, so lookups must be scoped. */
|
||||
typedef struct { uint32_t token; int32_t idx; } TokIdx;
|
||||
int32_t *img_tok_start = NULL; /* offset into tmap for each image */
|
||||
int32_t *img_tok_count = NULL;
|
||||
TokIdx *tmap = NULL;
|
||||
if (type2img && image_count) {
|
||||
img_tok_start = R_NEWS0 (int32_t, image_count);
|
||||
img_tok_count = R_NEWS0 (int32_t, image_count);
|
||||
tmap = R_NEWS (TokIdx, method_count);
|
||||
if (img_tok_start && img_tok_count && tmap) {
|
||||
/* Count methods per image. */
|
||||
for (size_t j = 0; j < method_count; j++) {
|
||||
int32_t decl = methods[j].declaringType;
|
||||
if (decl < 0 || (size_t) decl >= type_count) {
|
||||
continue;
|
||||
}
|
||||
int img_idx = type2img[decl];
|
||||
if (img_idx < 0 || (size_t) img_idx >= image_count) {
|
||||
continue;
|
||||
}
|
||||
if (methods[j].token) {
|
||||
img_tok_count[img_idx]++;
|
||||
}
|
||||
}
|
||||
/* Assign start offsets. */
|
||||
int32_t acc = 0;
|
||||
for (size_t ii = 0; ii < image_count; ii++) {
|
||||
img_tok_start[ii] = acc;
|
||||
acc += img_tok_count[ii];
|
||||
img_tok_count[ii] = 0; /* reset for use as append cursor */
|
||||
}
|
||||
/* Fill. */
|
||||
for (size_t j = 0; j < method_count; j++) {
|
||||
int32_t decl = methods[j].declaringType;
|
||||
if (decl < 0 || (size_t) decl >= type_count) {
|
||||
continue;
|
||||
}
|
||||
int img_idx = type2img[decl];
|
||||
if (img_idx < 0 || (size_t) img_idx >= image_count) {
|
||||
continue;
|
||||
}
|
||||
if (!methods[j].token) {
|
||||
continue;
|
||||
}
|
||||
int32_t pos = img_tok_start[img_idx] + img_tok_count[img_idx]++;
|
||||
tmap[pos].token = methods[j].token;
|
||||
tmap[pos].idx = (int32_t) j;
|
||||
}
|
||||
(void) acc;
|
||||
/* Sort each image's slice by token. */
|
||||
extern int r2u_cmp_tokidx (const void *a, const void *b);
|
||||
for (size_t ii = 0; ii < image_count; ii++) {
|
||||
qsort (tmap + img_tok_start[ii], (size_t) img_tok_count[ii],
|
||||
sizeof (TokIdx), r2u_cmp_tokidx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate attribute ranges per-image, so method-token lookups stay scoped. */
|
||||
size_t cap = 32;
|
||||
R2UnityInterop *out = R_NEWS0 (R2UnityInterop, cap);
|
||||
size_t n = 0;
|
||||
if (!out) {
|
||||
goto done;
|
||||
}
|
||||
for (size_t ii = 0; ii < image_count; ii++) {
|
||||
int32_t r_start = images[ii].customAttributeStart;
|
||||
int32_t r_end = r_start + (int32_t) images[ii].customAttributeCount;
|
||||
if (r_start < 0 || r_end < 0 || (size_t) r_end > nranges) {
|
||||
continue;
|
||||
}
|
||||
for (int32_t ri = r_start; ri < r_end; ri++) {
|
||||
uint32_t tok = ranges[ri].token;
|
||||
if ((tok >> 24) != 0x06) {
|
||||
continue;
|
||||
}
|
||||
uint8_t kind = attribute_range_interop_kind (meta, ranges, nranges, (size_t) ri,
|
||||
attr_data_off, attr_data_size,
|
||||
methods, method_count, types, type_count);
|
||||
if (!kind) {
|
||||
continue;
|
||||
}
|
||||
/* Binary-search this image's token slice. */
|
||||
int32_t mi = -1;
|
||||
if (tmap) {
|
||||
size_t lo = (size_t) img_tok_start[ii];
|
||||
size_t hi = lo + (size_t) img_tok_count[ii];
|
||||
while (lo < hi) {
|
||||
size_t mid = (lo + hi) >> 1;
|
||||
if (tmap[mid].token < tok) {
|
||||
lo = mid + 1;
|
||||
} else if (tmap[mid].token > tok) {
|
||||
hi = mid;
|
||||
} else {
|
||||
mi = tmap[mid].idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mi < 0) {
|
||||
continue;
|
||||
}
|
||||
if (n + 1 > cap) {
|
||||
size_t ncap = cap * 2;
|
||||
R2UnityInterop *noo = realloc (out, ncap * sizeof (R2UnityInterop));
|
||||
if (!noo) {
|
||||
break;
|
||||
}
|
||||
memset (noo + cap, 0, (ncap - cap) * sizeof (R2UnityInterop));
|
||||
out = noo;
|
||||
cap = ncap;
|
||||
}
|
||||
R2UnityInterop *it = &out[n++];
|
||||
const Il2CppMethodDefinition *m = &methods[mi];
|
||||
it->kind = kind;
|
||||
it->method_index = mi;
|
||||
it->token = m->token;
|
||||
it->flags = m->flags;
|
||||
it->iflags = m->iflags;
|
||||
it->wrapper_va = 0;
|
||||
it->wrapper_index = UINT32_MAX;
|
||||
it->confidence = 100;
|
||||
|
||||
const Il2CppTypeDefinition *td = NULL;
|
||||
if (types && m->declaringType >= 0 && (size_t) m->declaringType < type_count) {
|
||||
td = &types[m->declaringType];
|
||||
}
|
||||
it->name = build_qualified_name (meta, td, m->nameIndex);
|
||||
|
||||
int img_idx = -1;
|
||||
if (type2img && m->declaringType >= 0 && (size_t) m->declaringType < type_count) {
|
||||
img_idx = type2img[m->declaringType];
|
||||
}
|
||||
it->image_index = img_idx;
|
||||
if (img_idx >= 0 && images && (size_t) img_idx < image_count) {
|
||||
it->image_name = (char *) r2unity_get_string (meta, images[img_idx].nameIndex);
|
||||
}
|
||||
} /* close for ri */
|
||||
} /* close for ii */
|
||||
if (n == 0) {
|
||||
R_FREE (out);
|
||||
out = NULL;
|
||||
}
|
||||
done:
|
||||
R_FREE (ranges);
|
||||
R_FREE (methods);
|
||||
R_FREE (types);
|
||||
R_FREE (images);
|
||||
R_FREE (type2img);
|
||||
R_FREE (tmap);
|
||||
*count = n;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* qsort comparator used by r2unity_enumerate_reverse_pinvokes; kept at file
|
||||
* scope because nested functions are not portable in ISO C. */
|
||||
int r2u_cmp_tokidx (const void *a, const void *b) {
|
||||
typedef struct { uint32_t token; int32_t idx; } TokIdx;
|
||||
const TokIdx *x = a;
|
||||
const TokIdx *y = b;
|
||||
if (x->token < y->token) return -1;
|
||||
if (x->token > y->token) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
R_API void r2unity_free_interop (R2UnityInterop *items, size_t count) {
|
||||
if (!items) {
|
||||
return;
|
||||
|
||||
@@ -359,6 +359,10 @@ R_API Il2CppImageDefinition *r2unity_get_images (R2UnityMetadata *meta, size_t *
|
||||
R_API Il2CppAssemblyDefinition *r2unity_get_assemblies (R2UnityMetadata *meta, size_t *count);
|
||||
R_API int32_t *r2unity_get_referenced_assemblies (R2UnityMetadata *meta, size_t *count);
|
||||
R_API R2UnityInterop *r2unity_enumerate_pinvokes (R2UnityMetadata *meta, size_t *count);
|
||||
/* Reverse-P/Invoke (native -> managed) enumeration via v29+ attribute BLOB.
|
||||
* Returns methods tagged with [MonoPInvokeCallback] or [UnmanagedCallersOnly].
|
||||
* Pre-v29 metadata returns NULL (attribute ctor args live in generator stubs). */
|
||||
R_API R2UnityInterop *r2unity_enumerate_reverse_pinvokes (R2UnityMetadata *meta, size_t *count);
|
||||
R_API void r2unity_free_interop (R2UnityInterop *items, size_t count);
|
||||
/* Simplified API: use format-specific finders, or manual read stub. */
|
||||
R_API bool r2unity_read_method_pointers_at (R2UnityMetadata *meta, const char *exe_path, ut64 addr, size_t count, ut64 **out_ptrs);
|
||||
|
||||
+90
-6
@@ -76,6 +76,7 @@ static void print_usage (FILE *out, const char *prog_name) {
|
||||
" -c N Read N pointer entries (pair with -a)\n"
|
||||
" -S Emit a CycloneDX SBOM (JSON) of the managed assemblies\n"
|
||||
" -P Enumerate P/Invoke (managed -> native) methods\n"
|
||||
" -R Enumerate reverse-P/Invoke (native -> managed) methods (v29+)\n"
|
||||
" -z Enumerate managed string literals (`ldstr`) from metadata\n"
|
||||
" -v Verbose debug tracing on stderr\n"
|
||||
" -h Show this help and exit\n"
|
||||
@@ -440,7 +441,82 @@ static int emit_pinvokes (R2UnityMetadata *meta, const char *exe_path,
|
||||
(unsigned) it->confidence);
|
||||
}
|
||||
if (!quiet) {
|
||||
printf ("# summary: pinvokes=%zu reverse_pinvokes=0 (wrapper resolution not yet implemented)\n", n);
|
||||
printf ("# summary: pinvokes=%zu\n", n);
|
||||
}
|
||||
}
|
||||
|
||||
r2unity_free_interop (items, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *interop_kind_label (uint8_t kind) {
|
||||
switch (kind) {
|
||||
case R2U_INTEROP_REVERSE_PINVOKE: return "MonoPInvokeCallback";
|
||||
case R2U_INTEROP_UNMANAGED_ONLY: return "UnmanagedCallersOnly";
|
||||
default: return "reverse";
|
||||
}
|
||||
}
|
||||
|
||||
static int emit_reverse_pinvokes (R2UnityMetadata *meta, const char *exe_path,
|
||||
bool is_json, bool is_r2, bool quiet) {
|
||||
(void) exe_path;
|
||||
size_t n = 0;
|
||||
R2UnityInterop *items = r2unity_enumerate_reverse_pinvokes (meta, &n);
|
||||
if (items && n > 1) {
|
||||
qsort (items, n, sizeof (R2UnityInterop), interop_cmp);
|
||||
}
|
||||
|
||||
if (is_json) {
|
||||
FILE *f = stdout;
|
||||
fprintf (f, "{\"ok\":true,\"version\":%d,\"reverse_pinvokes\":[", meta->version);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
R2UnityInterop *it = &items[i];
|
||||
if (i) fputc (',', f);
|
||||
fprintf (f, "{\"image\":\"");
|
||||
json_escape (f, it->image_name ? it->image_name : "");
|
||||
fprintf (f, "\",\"method\":\"");
|
||||
json_escape (f, it->name ? it->name : "");
|
||||
fprintf (f, "\",\"token\":\"0x%08x\",\"flags\":\"0x%04x\",\"iflags\":\"0x%04x\",\"attribute\":\"%s\",\"confidence\":%u",
|
||||
it->token, it->flags, it->iflags,
|
||||
interop_kind_label (it->kind),
|
||||
(unsigned) it->confidence);
|
||||
fputc ('}', f);
|
||||
}
|
||||
fprintf (f, "]}\n");
|
||||
} else if (is_r2) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
R2UnityInterop *it = &items[i];
|
||||
if (!it->name) {
|
||||
continue;
|
||||
}
|
||||
char buf[1024];
|
||||
if (it->image_name && *it->image_name) {
|
||||
snprintf (buf, sizeof (buf), "sym.unity.reverse.%s.%s", it->image_name, it->name);
|
||||
} else {
|
||||
snprintf (buf, sizeof (buf), "sym.unity.reverse.%s", it->name);
|
||||
}
|
||||
r_name_filter (buf, -1);
|
||||
printf ("# ReversePInvoke %s [%s]\n", buf, interop_kind_label (it->kind));
|
||||
}
|
||||
} else {
|
||||
if (!quiet) {
|
||||
printf ("# Reverse-P/Invoke methods (native -> managed), metadata wire version %d\n", meta->version);
|
||||
printf ("# IMAGE\tMETHOD\tATTRIBUTE\tFLAGS\tCONFIDENCE\n");
|
||||
}
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
R2UnityInterop *it = &items[i];
|
||||
char attrs[128];
|
||||
attrs[0] = 0;
|
||||
build_method_attrs_string (attrs, sizeof (attrs), it->flags);
|
||||
printf ("%s\t%s\t%s\t%s\t%u\n",
|
||||
it->image_name ? it->image_name : "",
|
||||
it->name ? it->name : "",
|
||||
interop_kind_label (it->kind),
|
||||
*attrs ? attrs : "-",
|
||||
(unsigned) it->confidence);
|
||||
}
|
||||
if (!quiet) {
|
||||
printf ("# summary: reverse_pinvokes=%zu\n", n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,9 +535,10 @@ int main (int argc, char *argv[]) {
|
||||
bool fast = false;
|
||||
bool sbom = false;
|
||||
bool pinvokes = false;
|
||||
bool reverse_pinvokes = false;
|
||||
bool string_literals = false;
|
||||
int opt;
|
||||
while ((opt = getopt (argc, argv, "hjrqfvSPzl:a:c:")) != -1) {
|
||||
while ((opt = getopt (argc, argv, "hjrqfvSPRzl:a:c:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'j': json_one_line = true; break;
|
||||
case 'r': r2_script = true; break;
|
||||
@@ -470,6 +547,7 @@ int main (int argc, char *argv[]) {
|
||||
case 'v': debug = true; break;
|
||||
case 'S': sbom = true; break;
|
||||
case 'P': pinvokes = true; break;
|
||||
case 'R': reverse_pinvokes = true; break;
|
||||
case 'z': string_literals = true; break;
|
||||
case 'l': limit = strtol (optarg, NULL, 0); break;
|
||||
case 'a': gmp_addr = (ut64) strtoull (optarg, NULL, 0); break;
|
||||
@@ -482,9 +560,13 @@ int main (int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (pinvokes && reverse_pinvokes) {
|
||||
R_LOG_ERROR ("-P and -R are mutually exclusive");
|
||||
return 1;
|
||||
}
|
||||
if (string_literals) {
|
||||
if (json_one_line || fast || gmp_addr || sbom || pinvokes) {
|
||||
R_LOG_ERROR ("-z cannot be combined with -j, -f, -a/-c, -S or -P");
|
||||
if (json_one_line || fast || gmp_addr || sbom || pinvokes || reverse_pinvokes) {
|
||||
R_LOG_ERROR ("-z cannot be combined with -j, -f, -a/-c, -S, -P or -R");
|
||||
return 1;
|
||||
}
|
||||
if (argc - optind != 1 && argc - optind != 2) {
|
||||
@@ -528,14 +610,16 @@ int main (int argc, char *argv[]) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (pinvokes) {
|
||||
if (pinvokes || reverse_pinvokes) {
|
||||
if (json_one_line && r2_script) {
|
||||
R_LOG_ERROR ("-j and -r are mutually exclusive");
|
||||
r2unity_free_metadata (meta);
|
||||
r_unref (buf);
|
||||
return 1;
|
||||
}
|
||||
int rc = emit_pinvokes (meta, exe_path, json_one_line, r2_script, quiet);
|
||||
int rc = reverse_pinvokes
|
||||
? emit_reverse_pinvokes (meta, exe_path, json_one_line, r2_script, quiet)
|
||||
: emit_pinvokes (meta, exe_path, json_one_line, r2_script, quiet);
|
||||
r2unity_free_metadata (meta);
|
||||
r_unref (buf);
|
||||
return rc;
|
||||
|
||||
Reference in new issue
Block a user