Compare commits

..
6 Commits
Author SHA1 Message Date
pancake 9821c3f425 fi 2026-04-24 01:05:01 +02:00
pancake 32d1965288 f 2026-04-24 00:47:51 +02:00
pancake 56900a5064 Update tests 2026-04-24 00:45:38 +02:00
pancake d6e807bc1c sclean 2026-04-24 00:38:53 +02:00
pancake 76ec5ad0f8 test 2026-04-24 00:27:20 +02:00
pancake c08dcc3268 SourceLine exposure and strip details in rbininfo 2026-04-24 00:26:26 +02:00
10 changed files with 64 additions and 935 deletions

No files matched your search

-1
View File
@@ -81,7 +81,6 @@ test: $(BIN_FILE) $(TEST_BIN)
$(BIN_FILE) sl test/bins/hbc/bespoke_eval.hbc | grep -q '0x000000b4 f=0 +0x4 yes.js:1:5 stmt=0'
$(BIN_FILE) h test/bins/hbc/bespoke_eval.hbc | grep -q 'Source Locations: 13 bytes'
$(BIN_FILE) h test/bins/hbc/index.android.bundle | grep -q 'Source Locations: 0 bytes'
$(BIN_FILE) libs test/bins/hbc/index.android.bundle | grep -q 'react-native'
r2:
$(MAKE) clean
-23
View File
@@ -344,22 +344,6 @@ typedef struct {
u32 count;
} HBCBindings;
typedef struct {
char *name;
char *path;
char *version;
const char *kind; /* cjs, package, js, native, global */
u32 function_id;
u32 offset;
u32 string_id;
bool inferred;
} HBCModule;
typedef struct {
HBCModule *modules;
u32 count;
} HBCModules;
/**
* Free instruction array.
*/
@@ -373,13 +357,6 @@ void hbc_free_insns(HBCInsn *insns, u32 count);
Result hbc_scan_bindings(HBC *hbc, HBCBindings *out);
void hbc_free_bindings(HBCBindings *bindings);
/**
* List probable bundled modules/libraries. Hermes bytecode has no SBOM table,
* so versions are only populated when they are explicitly preserved in strings.
*/
Result hbc_list_modules(HBC *hbc, HBCModules *out);
void hbc_free_modules(HBCModules *modules);
/**
* Encoding buffer and functions
*/
+1 -1
View File
@@ -1,7 +1,7 @@
project(
'r2hermes',
'c',
version: '1.2.8',
version: '1.2.6',
license: 'MIT',
default_options: [
'c_std=c11',
+50 -244
View File
@@ -863,10 +863,12 @@ static const BindingHint g_hints[] = {
};
#define HINTS_N (sizeof (g_hints) / sizeof (g_hints[0]))
/* Per-sid dedup flags for hbc_scan_bindings; avoids O(N^2) strcmp dedup. */
#define SEEN_CJS 0x1
#define SEEN_IMP 0x2
#define SEEN_EXP 0x4
static bool str_eq(const char *a, const char *b) {
if (a == b) {
return true;
}
return a && b && !strcmp (a, b);
}
static bool is_export_marker(const char *s) {
return !strcmp (s, "exports") || !strcmp (s, "module");
@@ -878,6 +880,13 @@ static bool is_export_store(const char *name) {
static Result binding_add(HBCBindings *out, HBCBindingType type, const char *kind,
const char *name, const char *module, u32 function_id, u32 offset, u32 string_id) {
for (u32 i = 0; i < out->count; i++) {
HBCBinding *b = &out->bindings[i];
if (b->type == type && !strcmp (b->kind, kind)
&& !strcmp (b->name, name) && str_eq (b->module, module)) {
return SUCCESS_RESULT ();
}
}
HBCBinding *nb = realloc (out->bindings, (out->count + 1) * sizeof (HBCBinding));
if (!nb) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
@@ -940,11 +949,7 @@ static size_t decode_operands(const Instruction *inst, const u8 *bytes, size_t l
return pos;
}
static Result scan_hint_matches(HBCBindings *out, const char *s, u32 fid, u32 off, u32 sid, u8 *flag) {
/* Dedup identical (kind,module) across hints (pointers are stable literals).
* Set SEEN_EXP when adding an EXPORT so caller's put_by_id skips this sid. */
const void *added[HINTS_N * 2];
u32 added_n = 0;
static Result scan_hint_matches(HBCBindings *out, const char *s, u32 fid, u32 off, u32 sid) {
for (size_t h = 0; h < HINTS_N; h++) {
const BindingHint *hint = &g_hints[h];
bool match = hint->name? !strcmp (s, hint->name):
@@ -952,27 +957,37 @@ static Result scan_hint_matches(HBCBindings *out, const char *s, u32 fid, u32 of
if (!match) {
continue;
}
bool dup = false;
for (u32 a = 0; a < added_n; a += 2) {
if (added[a] == hint->kind && added[a + 1] == hint->module) {
dup = true;
break;
}
}
if (dup) {
continue;
}
added[added_n++] = hint->kind;
added[added_n++] = hint->module;
if (hint->type == HBC_BINDING_EXPORT) {
*flag |= SEEN_EXP;
}
RETURN_IF_ERROR (binding_add (out, hint->type, hint->kind, s,
hint->module, fid, off, sid));
}
return SUCCESS_RESULT ();
}
static bool function_has_export_marker(HBCReader *r, const u8 *code, u32 size, HBCISA isa) {
for (u32 pc = 0; pc < size; ) {
u8 op = code[pc];
const Instruction *inst = (op < isa.count)? &isa.instructions[op]: NULL;
if (!inst || !inst->name || !inst->binary_size) {
break;
}
u32 values[6];
if (!decode_operands (inst, code + pc, size - pc, values)) {
break;
}
for (int i = 0; i < 6 && inst->operands[i].operand_type != OPERAND_TYPE_NONE; i++) {
if (inst->operands[i].operand_meaning == OPERAND_MEANING_STRING_ID) {
u32 sid = values[i];
const char *s = (sid < r->header.stringCount && r->strings)? r->strings[sid]: NULL;
if (s && is_export_marker (s)) {
return true;
}
}
}
pc += inst->binary_size;
}
return false;
}
Result hbc_scan_bindings(HBC *hbc, HBCBindings *out) {
if (!hbc || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
@@ -983,29 +998,16 @@ Result hbc_scan_bindings(HBC *hbc, HBCBindings *out) {
if (!isa.instructions) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "no isa for version");
}
u32 sc = r->header.stringCount;
u8 *seen = sc? calloc (sc, 1): NULL;
if (sc && !seen) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
}
Result res = SUCCESS_RESULT ();
if (r->header.cjsModuleCount > 0 && r->header.version >= 77 && r->cjs_modules) {
for (u32 i = 0; i < r->header.cjsModuleCount; i++) {
u32 sid = r->cjs_modules[i].symbol_id;
const char *name = (sid < sc && r->strings)? r->strings[sid]: NULL;
if (!name || !*name || (seen[sid] & SEEN_CJS)) {
continue;
}
seen[sid] |= SEEN_CJS;
res = binding_add (out, HBC_BINDING_IMPORT, "cjs", name,
"commonjs", 0, r->cjs_modules[i].offset, sid);
if (res.code != RESULT_SUCCESS) {
goto done;
const char *name = (sid < r->header.stringCount && r->strings)? r->strings[sid]: NULL;
if (name && *name) {
RETURN_IF_ERROR (binding_add (out, HBC_BINDING_IMPORT,
"cjs", name, "commonjs", 0, r->cjs_modules[i].offset, sid));
}
}
}
struct { u32 sid, off; } *cand = NULL;
u32 cand_cap = 0;
for (u32 fid = 0; fid < r->header.functionCount; fid++) {
const u8 *code = NULL;
u32 size = 0;
@@ -1015,8 +1017,7 @@ Result hbc_scan_bindings(HBC *hbc, HBCBindings *out) {
}
HBCFunc fi = { 0 };
(void)hbc_get_function_info (hbc, fid, &fi);
u32 cand_n = 0;
bool has_marker = false;
bool has_export_marker = function_has_export_marker (r, code, size, isa);
for (u32 pc = 0; pc < size; ) {
u8 op = code[pc];
const Instruction *inst = (op < isa.count)? &isa.instructions[op]: NULL;
@@ -1027,72 +1028,25 @@ Result hbc_scan_bindings(HBC *hbc, HBCBindings *out) {
if (!decode_operands (inst, code + pc, size - pc, values)) {
break;
}
bool is_store = is_export_store (inst->name);
for (int i = 0; i < 6 && inst->operands[i].operand_type != OPERAND_TYPE_NONE; i++) {
if (inst->operands[i].operand_meaning != OPERAND_MEANING_STRING_ID) {
continue;
}
u32 sid = values[i];
if (sid >= sc || !r->strings) {
continue;
}
const char *s = r->strings[sid];
const char *s = (sid < r->header.stringCount && r->strings)? r->strings[sid]: NULL;
if (!s || !*s) {
continue;
}
bool marker = is_export_marker (s);
if (marker) {
has_marker = true;
}
if (!(seen[sid] & SEEN_IMP)) {
seen[sid] |= SEEN_IMP;
res = scan_hint_matches (out, s, fid, fi.offset + pc, sid, &seen[sid]);
if (res.code != RESULT_SUCCESS) {
goto done_scan;
}
}
if (is_store && !marker && !(seen[sid] & SEEN_EXP)) {
if (cand_n == cand_cap) {
u32 nc = cand_cap? cand_cap * 2: 16;
void *nb = realloc (cand, nc * sizeof (*cand));
if (!nb) {
res = ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
goto done_scan;
}
cand = nb;
cand_cap = nc;
}
cand[cand_n].sid = sid;
cand[cand_n].off = fi.offset + pc;
cand_n++;
RETURN_IF_ERROR (scan_hint_matches (out, s, fid, fi.offset + pc, sid));
if (has_export_marker && is_export_store (inst->name) && !is_export_marker (s)) {
RETURN_IF_ERROR (binding_add (out, HBC_BINDING_EXPORT,
"module", s, NULL, fid, fi.offset + pc, sid));
}
}
pc += inst->binary_size;
}
if (!has_marker) {
continue;
}
for (u32 k = 0; k < cand_n; k++) {
u32 sid = cand[k].sid;
if (seen[sid] & SEEN_EXP) {
continue;
}
seen[sid] |= SEEN_EXP;
res = binding_add (out, HBC_BINDING_EXPORT, "module",
r->strings[sid], NULL, fid, cand[k].off, sid);
if (res.code != RESULT_SUCCESS) {
goto done_scan;
}
}
}
done_scan:
free (cand);
done:
free (seen);
if (res.code != RESULT_SUCCESS) {
hbc_free_bindings (out);
}
return res;
return SUCCESS_RESULT ();
}
void hbc_free_bindings(HBCBindings *bindings) {
@@ -1108,154 +1062,6 @@ void hbc_free_bindings(HBCBindings *bindings) {
bindings->count = 0;
}
static char *dup_range(const char *s, size_t n) {
char *ret = (char *)malloc (n + 1);
if (!ret) {
return NULL;
}
memcpy (ret, s, n);
ret[n] = 0;
return ret;
}
static char *package_from_path(const char *s) {
const char *nm = strstr (s, "node_modules/");
if (nm) {
const char *p = nm + strlen ("node_modules/");
const char *end;
if (*p == '@') {
const char *scope_end = strchr (p, '/');
if (!scope_end) {
return strdup (p);
}
end = strchr (scope_end + 1, '/');
} else {
end = strchr (p, '/');
}
return end? dup_range (p, (size_t)(end - p)): strdup (p);
}
if (s[0] == '@') {
const char *scope_end = strchr (s, '/');
if (scope_end) {
const char *end = strchr (scope_end + 1, '/');
return end? dup_range (s, (size_t)(end - s)): strdup (s);
}
}
return NULL;
}
static bool module_eq(const HBCModule *m, const char *kind, const char *name) {
return m->kind && kind && !strcmp (m->kind, kind)
&& m->name && name && !strcmp (m->name, name);
}
static Result module_add(HBCModules *out, const char *kind, const char *name,
const char *path, const char *version, u32 function_id, u32 offset,
u32 string_id, bool inferred) {
if (!name || !*name) {
return SUCCESS_RESULT ();
}
for (u32 i = 0; i < out->count; i++) {
if (module_eq (&out->modules[i], kind, name)) {
return SUCCESS_RESULT ();
}
}
HBCModule *nm = realloc (out->modules, (out->count + 1) * sizeof (HBCModule));
if (!nm) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
}
out->modules = nm;
HBCModule *m = &nm[out->count++];
*m = (HBCModule){
.name = strdup (name),
.path = path? strdup (path): NULL,
.version = version? strdup (version): NULL,
.kind = kind,
.function_id = function_id,
.offset = offset,
.string_id = string_id,
.inferred = inferred,
};
if (!m->name || (path && !m->path) || (version && !m->version)) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
}
return SUCCESS_RESULT ();
}
Result hbc_list_modules(HBC *hbc, HBCModules *out) {
if (!hbc || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
}
memset (out, 0, sizeof (*out));
HBCReader *r = &hbc->reader;
if (r->header.cjsModuleCount > 0 && r->header.version >= 77 && r->cjs_modules) {
for (u32 i = 0; i < r->header.cjsModuleCount; i++) {
u32 sid = r->cjs_modules[i].symbol_id;
const char *s = (sid < r->header.stringCount && r->strings)? r->strings[sid]: NULL;
if (!s || !*s) {
continue;
}
char *pkg = package_from_path (s);
if (pkg) {
Result res = module_add (out, "package", pkg, s, NULL, 0,
r->cjs_modules[i].offset, sid, true);
free (pkg);
if (res.code != RESULT_SUCCESS) {
hbc_free_modules (out);
return res;
}
} else {
Result res = module_add (out, "cjs", s, s, NULL, 0,
r->cjs_modules[i].offset, sid, false);
if (res.code != RESULT_SUCCESS) {
hbc_free_modules (out);
return res;
}
}
}
}
HBCBindings bindings = { 0 };
Result res = hbc_scan_bindings (hbc, &bindings);
if (res.code != RESULT_SUCCESS) {
hbc_free_modules (out);
return res;
}
for (u32 i = 0; i < bindings.count; i++) {
HBCBinding *b = &bindings.bindings[i];
if (b->type != HBC_BINDING_IMPORT) {
continue;
}
const char *name = b->module? b->module: b->name;
if (!name || !*name || !strcmp (name, "commonjs")) {
name = b->name;
}
res = module_add (out, b->kind? b->kind: "js", name,
b->name, NULL, b->function_id, b->offset, b->string_id, true);
if (res.code != RESULT_SUCCESS) {
hbc_free_bindings (&bindings);
hbc_free_modules (out);
return res;
}
}
hbc_free_bindings (&bindings);
return SUCCESS_RESULT ();
}
void hbc_free_modules(HBCModules *modules) {
if (!modules || !modules->modules) {
return;
}
for (u32 i = 0; i < modules->count; i++) {
free (modules->modules[i].name);
free (modules->modules[i].path);
free (modules->modules[i].version);
}
free (modules->modules);
modules->modules = NULL;
modules->count = 0;
}
/* Encoding functions */
Result hbc_enc(
+12 -265
View File
@@ -220,9 +220,15 @@ static RList *sections(RBinFile *bf) {
* literals live, and lets users `s` / `pd @` straight into them. */
HBC *hbc = get_hbc (bf);
if (hbc) {
add_pool_section (sections, "slp.arrays", hbc_get_pool_paddr (hbc, HBC_LIT_ARRAY), hbc_get_pool_size (hbc, HBC_LIT_ARRAY));
add_pool_section (sections, "slp.object_keys", hbc_get_pool_paddr (hbc, HBC_LIT_OBJECT), hbc_get_pool_size (hbc, HBC_LIT_OBJECT));
add_pool_section (sections, "slp.object_values", hbc_get_object_values_paddr (hbc), hbc_get_object_values_size (hbc));
add_pool_section (sections, "slp.arrays",
hbc_get_pool_paddr (hbc, HBC_LIT_ARRAY),
hbc_get_pool_size (hbc, HBC_LIT_ARRAY));
add_pool_section (sections, "slp.object_keys",
hbc_get_pool_paddr (hbc, HBC_LIT_OBJECT),
hbc_get_pool_size (hbc, HBC_LIT_OBJECT));
add_pool_section (sections, "slp.object_values",
hbc_get_object_values_paddr (hbc),
hbc_get_object_values_size (hbc));
}
return sections;
@@ -338,7 +344,8 @@ static RList *symbols(RBinFile *bf) {
const char *prefix = k? "lit.obj.": "lit.arr.";
HBCPoolGroup *groups = NULL;
u32 n = 0;
if (hbc_literals_scan_pool (hbc, kind, &groups, &n).code != RESULT_SUCCESS || !groups) {
if (hbc_literals_scan_pool (hbc, kind, &groups, &n).code
!= RESULT_SUCCESS || !groups) {
continue;
}
for (u32 i = 0; i < n; i++) {
@@ -392,44 +399,13 @@ static RList *imports(RBinFile *bf) {
return imports;
}
static bool list_has_string(RList *list, const char *name) {
RListIter *iter;
const char *it;
r_list_foreach (list, iter, it) {
if (it && name && !strcmp (it, name)) {
return true;
}
}
return false;
}
static RList *libs(RBinFile *bf) {
RList *libs = r_list_newf (free);
HBC *hbc = get_hbc (bf);
if (!libs || !hbc) {
return libs;
}
HBCModules modules = { 0 };
if (hbc_list_modules (hbc, &modules).code != RESULT_SUCCESS) {
return libs;
}
for (u32 i = 0; i < modules.count; i++) {
HBCModule *m = &modules.modules[i];
if (m->name && *m->name && !list_has_string (libs, m->name)) {
r_list_append (libs, strdup (m->name));
}
}
hbc_free_modules (&modules);
return libs;
}
static R_UNOWNED RList *lines(RBinFile *bf) {
HBC *hbc = get_hbc (bf);
if (!hbc) {
return NULL;
}
/* Respect bin.dbginfo=false — return early so the lazy debug-info parse
*(triggered by hbc_get_source_lines) never runs for users who opted out. */
* (triggered by hbc_get_source_lines) never runs for users who opted out. */
if (bf->rbin && !bf->rbin->want_dbginfo) {
return NULL;
}
@@ -519,232 +495,6 @@ static RList *strings(RBinFile *bf) {
return ret;
}
static void hbc_source_hash_hex(const HBCHeader *hh, char *dst, size_t dstsz) {
if (!dst || dstsz < 41) {
return;
}
r_hex_bin2str (hh->sourceHash, 20, dst);
}
static void hbc_header_print_u32(RStrBuf *sb, ut64 off, const char *name, ut32 value) {
r_strbuf_appendf (sb, "0x%08" PFMT64x " %-29s 0x%08x\n", off, name, value);
}
static void hbc_header_print_bool(RStrBuf *sb, ut64 off, const char *name, bool value) {
r_strbuf_appendf (sb, "0x%08" PFMT64x " %-29s %s\n", off, name, value? "true": "false");
}
static char *header(RBinFile *bf, int mode) {
HBC *hbc = get_hbc (bf);
if (!hbc) {
return NULL;
}
HBCHeader hh;
if (hbc_get_header (hbc, &hh).code != RESULT_SUCCESS) {
return NULL;
}
char hash[41] = { 0 };
hbc_source_hash_hex (&hh, hash, sizeof (hash));
RStrBuf *sb = r_strbuf_new ("");
if (!sb) {
return NULL;
}
if (mode == R_MODE_EQUAL) {
r_strbuf_appendf (sb, "magic=0x%016" PFMT64x "\n", hh.magic);
r_strbuf_appendf (sb, "version=%u\n", hh.version);
r_strbuf_appendf (sb, "sourceHash=%s\n", hash);
r_strbuf_appendf (sb, "fileLength=%u\n", hh.fileLength);
r_strbuf_appendf (sb, "globalCodeIndex=%u\n", hh.globalCodeIndex);
r_strbuf_appendf (sb, "functionCount=%u\n", hh.functionCount);
r_strbuf_appendf (sb, "stringKindCount=%u\n", hh.stringKindCount);
r_strbuf_appendf (sb, "identifierCount=%u\n", hh.identifierCount);
r_strbuf_appendf (sb, "stringCount=%u\n", hh.stringCount);
r_strbuf_appendf (sb, "overflowStringCount=%u\n", hh.overflowStringCount);
r_strbuf_appendf (sb, "stringStorageSize=%u\n", hh.stringStorageSize);
r_strbuf_appendf (sb, "bigIntCount=%u\n", hh.bigIntCount);
r_strbuf_appendf (sb, "bigIntStorageSize=%u\n", hh.bigIntStorageSize);
r_strbuf_appendf (sb, "regExpCount=%u\n", hh.regExpCount);
r_strbuf_appendf (sb, "regExpStorageSize=%u\n", hh.regExpStorageSize);
r_strbuf_appendf (sb, "literalValueBufferSize=%u\n", hh.literalValueBufferSize);
r_strbuf_appendf (sb, "arrayBufferSize=%u\n", hh.arrayBufferSize);
r_strbuf_appendf (sb, "objKeyBufferSize=%u\n", hh.objKeyBufferSize);
r_strbuf_appendf (sb, "objValueBufferSize=%u\n", hh.objValueBufferSize);
r_strbuf_appendf (sb, "objShapeTableCount=%u\n", hh.objShapeTableCount);
r_strbuf_appendf (sb, "numStringSwitchImms=%u\n", hh.numStringSwitchImms);
r_strbuf_appendf (sb, "segmentID=%u\n", hh.segmentID);
r_strbuf_appendf (sb, "cjsModuleCount=%u\n", hh.cjsModuleCount);
r_strbuf_appendf (sb, "functionSourceCount=%u\n", hh.functionSourceCount);
r_strbuf_appendf (sb, "debugInfoOffset=%u\n", hh.debugInfoOffset);
r_strbuf_appendf (sb, "staticBuiltins=%s\n", hh.staticBuiltins? "true": "false");
r_strbuf_appendf (sb, "cjsModulesStaticallyResolved=%s\n", hh.cjsModulesStaticallyResolved? "true": "false");
r_strbuf_appendf (sb, "hasAsync=%s\n", hh.hasAsync? "true": "false");
return r_strbuf_drain (sb);
}
if (mode == R_MODE_JSON) {
PJ *pj = pj_new ();
if (!pj) {
r_strbuf_free (sb);
return NULL;
}
pj_o (pj);
pj_kn (pj, "magic", hh.magic);
pj_kn (pj, "version", hh.version);
pj_ks (pj, "sourceHash", hash);
pj_kn (pj, "fileLength", hh.fileLength);
pj_kn (pj, "globalCodeIndex", hh.globalCodeIndex);
pj_kn (pj, "functionCount", hh.functionCount);
pj_kn (pj, "stringKindCount", hh.stringKindCount);
pj_kn (pj, "identifierCount", hh.identifierCount);
pj_kn (pj, "stringCount", hh.stringCount);
pj_kn (pj, "overflowStringCount", hh.overflowStringCount);
pj_kn (pj, "stringStorageSize", hh.stringStorageSize);
pj_kn (pj, "bigIntCount", hh.bigIntCount);
pj_kn (pj, "bigIntStorageSize", hh.bigIntStorageSize);
pj_kn (pj, "regExpCount", hh.regExpCount);
pj_kn (pj, "regExpStorageSize", hh.regExpStorageSize);
pj_kn (pj, "literalValueBufferSize", hh.literalValueBufferSize);
pj_kn (pj, "arrayBufferSize", hh.arrayBufferSize);
pj_kn (pj, "objKeyBufferSize", hh.objKeyBufferSize);
pj_kn (pj, "objValueBufferSize", hh.objValueBufferSize);
pj_kn (pj, "objShapeTableCount", hh.objShapeTableCount);
pj_kn (pj, "numStringSwitchImms", hh.numStringSwitchImms);
pj_kn (pj, "segmentID", hh.segmentID);
pj_kn (pj, "cjsModuleCount", hh.cjsModuleCount);
pj_kn (pj, "functionSourceCount", hh.functionSourceCount);
pj_kn (pj, "debugInfoOffset", hh.debugInfoOffset);
pj_kb (pj, "staticBuiltins", hh.staticBuiltins);
pj_kb (pj, "cjsModulesStaticallyResolved", hh.cjsModulesStaticallyResolved);
pj_kb (pj, "hasAsync", hh.hasAsync);
pj_end (pj);
r_strbuf_free (sb);
return pj_drain (pj);
}
ut64 off = 0;
r_strbuf_append (sb, "Hermes bytecode header:\n");
r_strbuf_appendf (sb, "0x%08" PFMT64x " %-29s 0x%016" PFMT64x "\n", off, "magic", hh.magic);
off += 8;
hbc_header_print_u32 (sb, off, "version", hh.version);
off += 4;
r_strbuf_appendf (sb, "0x%08" PFMT64x " %-29s %s\n", off, "sourceHash", hash);
off += 20;
hbc_header_print_u32 (sb, off, "fileLength", hh.fileLength);
off += 4;
hbc_header_print_u32 (sb, off, "globalCodeIndex", hh.globalCodeIndex);
off += 4;
hbc_header_print_u32 (sb, off, "functionCount", hh.functionCount);
off += 4;
hbc_header_print_u32 (sb, off, "stringKindCount", hh.stringKindCount);
off += 4;
hbc_header_print_u32 (sb, off, "identifierCount", hh.identifierCount);
off += 4;
hbc_header_print_u32 (sb, off, "stringCount", hh.stringCount);
off += 4;
hbc_header_print_u32 (sb, off, "overflowStringCount", hh.overflowStringCount);
off += 4;
hbc_header_print_u32 (sb, off, "stringStorageSize", hh.stringStorageSize);
off += 4;
if (hh.version >= 87) {
hbc_header_print_u32 (sb, off, "bigIntCount", hh.bigIntCount);
off += 4;
hbc_header_print_u32 (sb, off, "bigIntStorageSize", hh.bigIntStorageSize);
off += 4;
}
hbc_header_print_u32 (sb, off, "regExpCount", hh.regExpCount);
off += 4;
hbc_header_print_u32 (sb, off, "regExpStorageSize", hh.regExpStorageSize);
off += 4;
hbc_header_print_u32 (sb, off, "literalValueBufferSize", hh.literalValueBufferSize);
off += 4;
hbc_header_print_u32 (sb, off, "objKeyBufferSize", hh.objKeyBufferSize);
off += 4;
if (hh.version >= 97) {
hbc_header_print_u32 (sb, off, "objShapeTableCount", hh.objShapeTableCount);
off += 4;
} else {
hbc_header_print_u32 (sb, off, "objValueBufferSize", hh.objValueBufferSize);
off += 4;
}
if (hh.version >= 99) {
hbc_header_print_u32 (sb, off, "numStringSwitchImms", hh.numStringSwitchImms);
off += 4;
}
hbc_header_print_u32 (sb, off, "segmentID", hh.segmentID);
off += 4;
hbc_header_print_u32 (sb, off, "cjsModuleCount", hh.cjsModuleCount);
off += 4;
if (hh.version >= 84) {
hbc_header_print_u32 (sb, off, "functionSourceCount", hh.functionSourceCount);
off += 4;
}
hbc_header_print_u32 (sb, off, "debugInfoOffset", hh.debugInfoOffset);
off += 4;
hbc_header_print_bool (sb, off, "staticBuiltins", hh.staticBuiltins);
hbc_header_print_bool (sb, off, "cjsModulesStaticallyResolved", hh.cjsModulesStaticallyResolved);
hbc_header_print_bool (sb, off, "hasAsync", hh.hasAsync);
return r_strbuf_drain (sb);
}
static void hbc_fields_append_u32(RList *ret, ut64 *off, const char *name, ut32 value) {
r_list_append (ret, r_bin_field_new (*off, *off, value, 4, name, NULL, "x", false));
*off += 4;
}
static RList *fields(RBinFile *bf) {
RList *ret = r_list_new ();
HBC *hbc = get_hbc (bf);
if (!ret || !hbc) {
return ret;
}
HBCHeader hh;
if (hbc_get_header (hbc, &hh).code != RESULT_SUCCESS) {
return ret;
}
char hash[41] = { 0 };
hbc_source_hash_hex (&hh, hash, sizeof (hash));
ut64 off = 0;
r_list_append (ret, r_bin_field_new (off, off, hh.magic, 8, "magic", NULL, "q", false));
off += 8;
hbc_fields_append_u32 (ret, &off, "version", hh.version);
r_list_append (ret, r_bin_field_new (off, off, 0, 20, "sourceHash", hash, "s", false));
off += 20;
hbc_fields_append_u32 (ret, &off, "fileLength", hh.fileLength);
hbc_fields_append_u32 (ret, &off, "globalCodeIndex", hh.globalCodeIndex);
hbc_fields_append_u32 (ret, &off, "functionCount", hh.functionCount);
hbc_fields_append_u32 (ret, &off, "stringKindCount", hh.stringKindCount);
hbc_fields_append_u32 (ret, &off, "identifierCount", hh.identifierCount);
hbc_fields_append_u32 (ret, &off, "stringCount", hh.stringCount);
hbc_fields_append_u32 (ret, &off, "overflowStringCount", hh.overflowStringCount);
hbc_fields_append_u32 (ret, &off, "stringStorageSize", hh.stringStorageSize);
if (hh.version >= 87) {
hbc_fields_append_u32 (ret, &off, "bigIntCount", hh.bigIntCount);
hbc_fields_append_u32 (ret, &off, "bigIntStorageSize", hh.bigIntStorageSize);
}
hbc_fields_append_u32 (ret, &off, "regExpCount", hh.regExpCount);
hbc_fields_append_u32 (ret, &off, "regExpStorageSize", hh.regExpStorageSize);
hbc_fields_append_u32 (ret, &off, "literalValueBufferSize", hh.literalValueBufferSize);
hbc_fields_append_u32 (ret, &off, "objKeyBufferSize", hh.objKeyBufferSize);
if (hh.version >= 97) {
hbc_fields_append_u32 (ret, &off, "objShapeTableCount", hh.objShapeTableCount);
} else {
hbc_fields_append_u32 (ret, &off, "objValueBufferSize", hh.objValueBufferSize);
}
if (hh.version >= 99) {
hbc_fields_append_u32 (ret, &off, "numStringSwitchImms", hh.numStringSwitchImms);
}
hbc_fields_append_u32 (ret, &off, "segmentID", hh.segmentID);
hbc_fields_append_u32 (ret, &off, "cjsModuleCount", hh.cjsModuleCount);
if (hh.version >= 84) {
hbc_fields_append_u32 (ret, &off, "functionSourceCount", hh.functionSourceCount);
}
hbc_fields_append_u32 (ret, &off, "debugInfoOffset", hh.debugInfoOffset);
ut32 flags = (hh.staticBuiltins? 1: 0) | (hh.cjsModulesStaticallyResolved? 2: 0) | (hh.hasAsync? 4: 0);
r_list_append (ret, r_bin_field_new (off, off, flags, 1, "flags", NULL, "b", false));
r_list_append (ret, r_bin_field_new (off, off, hh.staticBuiltins? 1: 0, 1, "staticBuiltins", NULL, "b", false));
r_list_append (ret, r_bin_field_new (off, off, hh.cjsModulesStaticallyResolved? 1: 0, 1, "cjsModulesStaticallyResolved", NULL, "b", false));
r_list_append (ret, r_bin_field_new (off, off, hh.hasAsync? 1: 0, 1, "hasAsync", NULL, "b", false));
return ret;
}
const RBinPlugin r_bin_plugin_r2hermes = {
.meta = {
.name = "hbc.bin",
@@ -761,11 +511,8 @@ const RBinPlugin r_bin_plugin_r2hermes = {
.baddr = &baddr,
.symbols = &symbols,
.imports = &imports,
.libs = &libs,
.lines = &lines,
.strings = &strings,
.header = &header,
.fields = &fields,
};
#ifndef R2_PLUGIN_INCORE
-54
View File
@@ -5,7 +5,6 @@
#include <r_core.h>
#include <hbc/hbc.h>
#include <hbc/literals.h>
#include <hbc/opcodes.h>
#include "utils.inc.c"
#define HBC_VADDR_BASE 0x10000000ULL
@@ -261,55 +260,6 @@ static void cmd_list_functions(HbcContext *ctx, RCore *core) {
}
}
/* List functions containing direct eval instructions */
static void cmd_list_eval_functions(HbcContext *ctx, RCore *core) {
Result res = hbc_load_current_binary (ctx, core);
if (res.code != RESULT_SUCCESS) {
R_LOG_ERROR ("%s", safe_errmsg (res.error_message));
return;
}
HBCHeader header;
res = hbc_get_header (ctx->hbc, &header);
if (res.code != RESULT_SUCCESS) {
R_LOG_ERROR ("reading header: %s", safe_errmsg (res.error_message));
return;
}
HBCISA isa = hbc_isa_getv (header.version);
if (!isa.instructions) {
R_LOG_ERROR ("No ISA for Hermes bytecode version %u", header.version);
return;
}
const u32 count = hbc_function_count (ctx->hbc);
for (u32 fid = 0; fid < count; fid++) {
HBCFunc info;
if (hbc_get_function_info (ctx->hbc, fid, &info).code != RESULT_SUCCESS) {
continue;
}
const u8 *code = NULL;
u32 size = 0;
if (hbc_get_function_bytecode (ctx->hbc, fid, &code, &size).code != RESULT_SUCCESS || !code) {
continue;
}
for (u32 pc = 0; pc < size; ) {
const u8 op = code[pc];
const Instruction *inst = (op < isa.count)? &isa.instructions[op]: NULL;
if (!inst || !inst->name || !inst->binary_size) {
break;
}
if (hbc_canonical_opcode_from_name (inst->name) == OP_DirectEval) {
r_cons_printf (core->cons,
"0x%08"PFMT64x" f=%u +0x%x func=0x%08"PFMT64x" name=%s\n",
(ut64)HBC_VADDR_BASE + info.offset + pc, fid, pc,
(ut64)HBC_VADDR_BASE + info.offset,
r_str_get_fail (info.name, "unknown"));
}
pc += inst->binary_size;
}
}
}
/* Show file information */
static void cmd_file_info(HbcContext *ctx, RCore *core) {
Result res = hbc_load_current_binary (ctx, core);
@@ -898,7 +848,6 @@ static void cmd_help(RCore *core) {
" pd:h - Decompile function at current offset (or all if not in function)\n"
" pd:hc [id] - Decompile function by id\n"
" pd:ha - Decompile all functions\n"
" pd:he - List functions containing eval instructions\n"
" pd:hf - List all functions\n"
" pd:hi - Show file information\n"
" pd:hj [id] - JSON output for function\n"
@@ -942,9 +891,6 @@ static bool cmd_handler(RCorePluginSession *s, const char *input) {
case 'c': /* pd:hc [id] */
cmd_decompile_function_ex (ctx, core, arg + 1, false);
break;
case 'e': /* pd:he */
cmd_list_eval_functions (ctx, core);
break;
case 'f': /* pd:hf */
cmd_list_functions (ctx, core);
break;
+1 -62
View File
@@ -879,14 +879,6 @@ static void json_print_string(const char *s) {
putchar ('"');
}
static void json_print_nullable_string(const char *s) {
if (s) {
json_print_string (s);
} else {
fputs ("null", stdout);
}
}
static const char *binding_type_name(HBCBindingType type) {
return type == HBC_BINDING_EXPORT? "export": "import";
}
@@ -935,58 +927,6 @@ static Result cmd_bind(const CliContext *ctx, int argc, char **argv) {
return SUCCESS_RESULT ();
}
static Result cmd_libs(const CliContext *ctx, int argc, char **argv) {
if (argc != 1) {
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Usage: libs <input>");
}
HBC *hbc;
RETURN_IF_ERROR (open_hbc (argv[0], &hbc));
HBCModules modules = { 0 };
Result r = hbc_list_modules (hbc, &modules);
if (r.code != RESULT_SUCCESS) {
hbc_close (hbc);
return r;
}
if (ctx->flags.json) {
putchar ('[');
for (u32 i = 0; i < modules.count; i++) {
HBCModule *m = &modules.modules[i];
printf ("%s{\"kind\":", i? ",": "");
json_print_string (m->kind);
printf (",\"name\":");
json_print_string (m->name);
printf (",\"path\":");
json_print_nullable_string (m->path);
printf (",\"version\":");
json_print_nullable_string (m->version);
printf (",\"function_id\":%u,\"offset\":%u,\"string_id\":%u,\"inferred\":%s}",
m->function_id, m->offset, m->string_id, m->inferred? "true": "false");
}
puts ("]");
} else {
printf ("libs: %u\n", modules.count);
for (u32 i = 0; i < modules.count; i++) {
HBCModule *m = &modules.modules[i];
printf ("%-8s sid=%-5u fn=%-5u off=0x%08x %-30s",
m->kind? m->kind: "", m->string_id, m->function_id,
m->offset, m->name? m->name: "");
if (m->version && *m->version) {
printf (" version=%s", m->version);
}
if (m->path && *m->path && (!m->name || strcmp (m->path, m->name))) {
printf (" path=%s", m->path);
}
if (m->inferred) {
printf (" inferred");
}
putchar ('\n');
}
}
hbc_free_modules (&modules);
hbc_close (hbc);
return SUCCESS_RESULT ();
}
static const Command commands[] = {
{ "d", "Disassemble a Hermes bytecode file", cmd_d },
{ "c", "Decompile a Hermes bytecode file", cmd_c },
@@ -1005,7 +945,6 @@ static const Command commands[] = {
{ "sm", "Show string entry metadata", cmd_sm },
{ "lit", "SLP buffer literals: list, get, pool, xrefs", cmd_lit },
{ "bind", "List probable imports/exports/native bindings", cmd_bind },
{ "libs", "List probable bundled modules/libraries", cmd_libs },
};
#define COMMANDS_N (sizeof (commands) / sizeof (commands[0]))
@@ -1015,7 +954,7 @@ static void print_usage(const char *program_name) {
printf (" %-5s %s\n", commands[i].name, commands[i].help);
}
printf ("\nOptions (may appear anywhere after the command):\n");
printf (" --json, -j JSON output (lit list, bind, libs)\n");
printf (" --json, -j JSON output (lit list)\n");
printf (" --asmsyntax CPU-like asm syntax (dis)\n");
printf (" --verbose, -v Verbose output\n");
printf (" --bytecode, -b Include bytecode bytes\n");
-30
View File
@@ -18,16 +18,6 @@ EXPECT=<<EOF
EOF
RUN
NAME=pd:h help eval command
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:h?~pd:he
EOF
EXPECT=<<EOF
pd:he - List functions containing eval instructions
EOF
RUN
NAME=pd:hi file info small
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
@@ -136,26 +126,6 @@ EXPECT=<<EOF
EOF
RUN
NAME=pd:he list eval sites
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:he
EOF
EXPECT=<<EOF
0x100000b4 f=0 +0x4 func=0x100000b0 name=global
EOF
RUN
NAME=pd:he list eval sites large
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
pd:he
EOF
EXPECT=<<EOF
0x100d166b f=4953 +0x28 func=0x100d1643 name=onPress
EOF
RUN
NAME=pd:hc decompile function return
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
-83
View File
@@ -1,83 +0,0 @@
NAME=hbc ih header fields
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
ih~version
ih~functionCount
ih~debugInfoOffset
EOF
EXPECT=<<EOF
0x00000008 0x00000008 0x00000060 version
0x00000028 0x00000028 0x00000001 functionCount
0x00000068 0x00000068 0x000000c8 debugInfoOffset
EOF
RUN
NAME=hbc ihq header fields
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
ihq~version
ihq~functionCount
ihq~debugInfoOffset
EOF
EXPECT=<<EOF
0x00000008 0x00000060 version
0x00000028 0x00000001 functionCount
0x00000068 0x000000c8 debugInfoOffset
EOF
RUN
NAME=hbc ihj header fields
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
ihj~{}~version
ihj~{}~functionCount
ihj~{}~debugInfoOffset
EOF
EXPECT=<<EOF
"name": "version",
"name": "functionCount",
"name": "debugInfoOffset",
EOF
RUN
NAME=hbc iH ascii header
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
iH~version
iH~functionCount
iH~debugInfoOffset
EOF
EXPECT=<<EOF
0x00000008 version 0x00000060
0x00000028 functionCount 0x00000001
0x00000068 debugInfoOffset 0x000000c8
EOF
RUN
NAME=hbc iH key values
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
iH=~version
iH=~functionCount
iH=~debugInfoOffset
EOF
EXPECT=<<EOF
version=96
functionCount=1
debugInfoOffset=200
EOF
RUN
NAME=hbc iH json
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
iHj~version
iHj~functionCount
iHj~debugInfoOffset
EOF
EXPECT=<<EOF
{"magic":2240826417119764422,"version":96,"sourceHash":"a97c8302dab90bec7184a9183b22b43badd57a65","fileLength":292,"globalCodeIndex":0,"functionCount":1,"stringKindCount":1,"identifierCount":0,"stringCount":2,"overflowStringCount":0,"stringStorageSize":17,"bigIntCount":0,"bigIntStorageSize":0,"regExpCount":0,"regExpStorageSize":0,"literalValueBufferSize":0,"arrayBufferSize":0,"objKeyBufferSize":0,"objValueBufferSize":0,"objShapeTableCount":0,"numStringSwitchImms":0,"segmentID":0,"cjsModuleCount":0,"functionSourceCount":0,"debugInfoOffset":200,"staticBuiltins":false,"cjsModulesStaticallyResolved":false,"hasAsync":false}
{"magic":2240826417119764422,"version":96,"sourceHash":"a97c8302dab90bec7184a9183b22b43badd57a65","fileLength":292,"globalCodeIndex":0,"functionCount":1,"stringKindCount":1,"identifierCount":0,"stringCount":2,"overflowStringCount":0,"stringStorageSize":17,"bigIntCount":0,"bigIntStorageSize":0,"regExpCount":0,"regExpStorageSize":0,"literalValueBufferSize":0,"arrayBufferSize":0,"objKeyBufferSize":0,"objValueBufferSize":0,"objShapeTableCount":0,"numStringSwitchImms":0,"segmentID":0,"cjsModuleCount":0,"functionSourceCount":0,"debugInfoOffset":200,"staticBuiltins":false,"cjsModulesStaticallyResolved":false,"hasAsync":false}
{"magic":2240826417119764422,"version":96,"sourceHash":"a97c8302dab90bec7184a9183b22b43badd57a65","fileLength":292,"globalCodeIndex":0,"functionCount":1,"stringKindCount":1,"identifierCount":0,"stringCount":2,"overflowStringCount":0,"stringStorageSize":17,"bigIntCount":0,"bigIntStorageSize":0,"regExpCount":0,"regExpStorageSize":0,"literalValueBufferSize":0,"arrayBufferSize":0,"objKeyBufferSize":0,"objValueBufferSize":0,"objShapeTableCount":0,"numStringSwitchImms":0,"segmentID":0,"cjsModuleCount":0,"functionSourceCount":0,"debugInfoOffset":200,"staticBuiltins":false,"cjsModulesStaticallyResolved":false,"hasAsync":false}
EOF
RUN
-172
View File
@@ -1,172 +0,0 @@
NAME=pd:hs help entry
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:h?~pd:hs
EOF
EXPECT=<<EOF
pd:hs [id] - List source-line information
EOF
RUN
NAME=pd:hs source lines small
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:hs
EOF
EXPECT=<<EOF
0x000000b0 f=0 +0x0 yes.js:1:1 stmt=0
0x000000b4 f=0 +0x4 yes.js:1:5 stmt=0
EOF
RUN
NAME=pd:hs source lines filter
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:hs 0
EOF
EXPECT=<<EOF
0x000000b0 f=0 +0x0 yes.js:1:1 stmt=0
0x000000b4 f=0 +0x4 yes.js:1:5 stmt=0
EOF
RUN
NAME=pd:hs source lines filter miss
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:hs 99
EOF
EXPECT=<<EOF
EOF
RUN
NAME=pd:hs empty source lines
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
pd:hs
EOF
EXPECT=<<EOF
EOF
RUN
NAME=pd:hi debug info present
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:hi~Debug Info Present
EOF
EXPECT=<<EOF
Debug Info Present: yes
EOF
RUN
NAME=pd:hi source locations size
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:hi~Source Locations
EOF
EXPECT=<<EOF
Source Locations: 13 bytes
EOF
RUN
NAME=pd:hi debug files count
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
pd:hi~Debug Files
EOF
EXPECT=<<EOF
Debug Files: 1 (6 bytes)
EOF
RUN
NAME=iI linenum exposed (debug info)
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
iI~linenum
EOF
EXPECT=<<EOF
linenum true
EOF
RUN
NAME=iI linenum stripped
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
iI~linenum
EOF
EXPECT=<<EOF
linenum false
EOF
RUN
NAME=iI linenum honours file truth with bin.dbginfo=false
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e bin.dbginfo=false
CMDS=<<EOF
iI~linenum,stripped
EOF
EXPECT=<<EOF
linenum true
stripped false
EOF
RUN
NAME=iI stripped file still stripped with bin.dbginfo=false
FILE=bins/hbc/index.android.bundle
ARGS=-e bin.dbginfo=false
CMDS=<<EOF
iI~linenum,stripped
EOF
EXPECT=<<EOF
linenum false
stripped true
EOF
RUN
NAME=iI stripped file stripped with bin.dbginfo=true
FILE=bins/hbc/index.android.bundle
ARGS=-e bin.dbginfo=true
CMDS=<<EOF
iI~linenum,stripped
EOF
EXPECT=<<EOF
linenum false
stripped true
EOF
RUN
NAME=CL populated with bin.dbginfo=true
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e bin.dbginfo=true
CMDS=<<EOF
CL
EOF
EXPECT=<<EOF
file: yes.js
line: 1
colu: 1
addr: 0x100000b0
file: yes.js
line: 1
colu: 5
addr: 0x100000b4
EOF
RUN
NAME=CL empty with bin.dbginfo=false
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e bin.dbginfo=false
CMDS=<<EOF
CL
EOF
EXPECT=<<EOF
EOF
RUN
NAME=CL empty on stripped file
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
CL
EOF
EXPECT=<<EOF
EOF
RUN