mirror of
https://github.com/radareorg/r2hermes.git
synced 2026-11-09 04:19:34 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1202343799 | ||
|
|
31c8b20249 | ||
|
|
d41228d045 | ||
|
|
30fbd04f68 | ||
|
|
4dc8b9a569 | ||
|
|
6a3c982eeb | ||
|
|
dce75b6809 | ||
|
|
acbee1ba19 | ||
|
|
105e8d6674 | ||
|
|
89d6f52fe1 | ||
|
|
7875731e84 | ||
|
|
e73406646a | ||
|
|
9957807e90 | ||
|
|
adad501abb | ||
|
|
b14389ea9d | ||
|
|
01039f308d | ||
|
|
4c49b6b530 | ||
|
|
c9c6035535 | ||
|
|
c5e9965c35 | ||
|
|
cf4ce12aaa | ||
|
|
cd30d40b73 | ||
|
|
605334bf21 | ||
|
|
d9a988ede3 | ||
|
|
e08f48e527 | ||
|
|
47b151b927 | ||
|
|
2f7ea18bd5 | ||
|
|
769e20d6c0 | ||
|
|
8b10abfa5b | ||
|
|
a4afec9e44 | ||
|
|
8e6d1562ad | ||
|
|
cd918d41eb | ||
|
|
07c796baa5 | ||
|
|
234267673b | ||
|
|
ccbcbd44ad | ||
|
|
88fd7897ea | ||
|
|
2e6eaacf7c |
No files matched your search
@@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "##build "
|
||||
prefix-development: "##build "
|
||||
labels:
|
||||
- buildsystem
|
||||
@@ -1,7 +1,7 @@
|
||||
name: CI
|
||||
|
||||
env:
|
||||
R2V: 6.0.7
|
||||
R2V: 6.0.8
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install build deps
|
||||
@@ -55,12 +55,12 @@ jobs:
|
||||
release:
|
||||
name: Create release and upload tarball
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
needs: [build]
|
||||
if: needs.build.outputs.tag != ''
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
git archive --format=tar.gz --prefix=libhbc-$TAG/ $TAG -o libhbc-$TAG.tgz
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.build.outputs.tag }}
|
||||
files: ./libhbc-${{ needs.build.outputs.tag }}.tgz
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Meson Build
|
||||
|
||||
env:
|
||||
R2V: 6.0.7
|
||||
R2V: 6.0.8
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
name: CI
|
||||
|
||||
env:
|
||||
R2V: 6.0.8
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
tcc-r2git:
|
||||
name: Test unified r2plugin with TinyCC
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Checkout TinyCC repository
|
||||
run: |
|
||||
git clone https://github.com/TinyCC/tinycc
|
||||
cd tinycc
|
||||
git checkout mob
|
||||
git reset --hard 4fccaf61241a5eb72b0777b3a44bd7abbea48604
|
||||
sh ./configure --prefix=/usr
|
||||
make -j
|
||||
sudo make install
|
||||
|
||||
- name: Build radare2 from git with TCC
|
||||
run: |
|
||||
git clone --depth=1 https://github.com/radareorg/radare2.git
|
||||
cd radare2
|
||||
echo core.r2hermes > libr/xps/static.cfg
|
||||
export CC=tcc
|
||||
cp -f ../r2plugin/config.mk libr/xps/config.mk
|
||||
git clone .. libr/xps/p/r2hermes
|
||||
make -C libr/xps/p/r2hermes include/hbc/version.h
|
||||
make -C libr/xps/p/r2hermes
|
||||
make -C libr/xps
|
||||
./configure --prefix=/usr --with-compiler=tcc
|
||||
make -j
|
||||
sudo make install
|
||||
|
||||
- name: Test if r2plugin is available
|
||||
run: |
|
||||
r2 -q -c La~hbc -c Lb~hbc -c Lc~hbc -- | grep -E "(core|arch|bin).*hbc"
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Windows Build
|
||||
|
||||
env:
|
||||
R2V: 6.0.7
|
||||
R2V: 6.0.8
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.12.x
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
meson install -C builddir
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-windows
|
||||
path: builddir\install\**\*
|
||||
@@ -17,8 +17,7 @@ PARSERS_SRC = $(wildcard $(SRC_DIR)/parsers/*.c)
|
||||
DISASM_SRC = $(wildcard $(SRC_DIR)/disassembly/*.c)
|
||||
DECOMPILE_SRC = $(wildcard $(SRC_DIR)/decompilation/*.c)
|
||||
OPCODES_SRC = $(filter-out $(SRC_DIR)/opcodes/%.inc.c,$(wildcard $(SRC_DIR)/opcodes/*.c))
|
||||
DATA_PROVIDER_SRC = $(SRC_DIR)/data_provider_file.c $(SRC_DIR)/data_provider_buffer.c
|
||||
LIB_SRC = $(UTILS_SRC) $(PARSERS_SRC) $(DISASM_SRC) $(DECOMPILE_SRC) $(OPCODES_SRC) $(DATA_PROVIDER_SRC) \
|
||||
LIB_SRC = $(UTILS_SRC) $(PARSERS_SRC) $(DISASM_SRC) $(DECOMPILE_SRC) $(OPCODES_SRC) \
|
||||
$(SRC_DIR)/hbc.c $(SRC_DIR)/opcodes/encoder.c $(SRC_DIR)/opcodes/decoder.c $(SRC_DIR)/r2.c
|
||||
MAIN_SRC = src/tool/libhbctool.c
|
||||
|
||||
@@ -74,10 +73,14 @@ TEST_SRC = $(wildcard $(TEST_DIR)/*.c)
|
||||
TEST_BIN = $(BIN_DIR)/run_tests
|
||||
|
||||
|
||||
r2 test:
|
||||
r2:
|
||||
$(MAKE) clean
|
||||
$(MAKE) -C src/r2 && $(MAKE) -C src/r2 user-install
|
||||
r2r -i test/db/extras
|
||||
|
||||
r2one:
|
||||
$(MAKE) -C src/r2 r2one
|
||||
|
||||
user-install user-uninstall:
|
||||
$(MAKE) -C src/r2
|
||||
$(MAKE) -C src/r2 $@
|
||||
|
||||
@@ -136,7 +136,7 @@ typedef struct HermesDecompiler {
|
||||
char *input_file;
|
||||
char *output_file;
|
||||
HBCReader *hbc_reader;
|
||||
HBC *hbc; /* Use HBC provider instead of direct file I/O */
|
||||
HBC *hbc; /* Use HBC for direct file I/O */
|
||||
u32 *calldirect_function_ids;
|
||||
u32 calldirect_function_ids_count;
|
||||
u32 calldirect_function_ids_capacity;
|
||||
@@ -150,15 +150,15 @@ typedef struct HermesDecompiler {
|
||||
|
||||
/* Function declarations */
|
||||
Result _hbc_decompiler_init(HermesDecompiler *decompiler);
|
||||
Result _hbc_decompiler_init_with_provider(HermesDecompiler *decompiler, HBC *hbc);
|
||||
Result _hbc_decompiler_init_with_state(HermesDecompiler *decompiler, HBC *hbc);
|
||||
Result _hbc_decompiler_cleanup(HermesDecompiler *decompiler);
|
||||
/* High-level entry points */
|
||||
Result _hbc_decompile_file(const char *input_file, const char *output_file);
|
||||
/* Buffer-based APIs used by hermesdec library */
|
||||
Result _hbc_decompile_all_to_buffer(HBCReader *reader, HBCDecompOptions options, StringBuffer *out);
|
||||
Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, StringBuffer *out);
|
||||
Result _hbc_decompile_all_with_state(HBC *hbc, HBCDecompOptions options, StringBuffer *out);
|
||||
Result _hbc_decompile_function_to_buffer(HBCReader *reader, u32 function_id, HBCDecompOptions options, StringBuffer *out);
|
||||
Result _hbc_decompile_function_with_provider(HBC *hbc, u32 function_id, HBCDecompOptions options, StringBuffer *out);
|
||||
Result _hbc_decompile_function_with_state(HBC *hbc, u32 function_id, HBCDecompOptions options, StringBuffer *out);
|
||||
Result _hbc_decompile_function(HermesDecompiler *state, u32 function_id, Environment *parent_environment, int environment_id, bool is_closure, bool is_generator, bool is_async);
|
||||
|
||||
/* Transformation passes */
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef HERMES_DEC_OPTIMIZER_H
|
||||
#define HERMES_DEC_OPTIMIZER_H
|
||||
|
||||
#include <hbc/common.h>
|
||||
#include <hbc/decompilation/token.h>
|
||||
|
||||
typedef enum {
|
||||
OPTIMIZER_NONE = 0,
|
||||
OPTIMIZER_CONSTANT_PROPAGATION = 1,
|
||||
OPTIMIZER_DEAD_CODE_ELIMINATION = 2,
|
||||
OPTIMIZER_EXPRESSION_INLINING = 4,
|
||||
OPTIMIZER_ALL = 7
|
||||
} OptimizerFlags;
|
||||
|
||||
/* Constant value type */
|
||||
typedef enum {
|
||||
CONST_TYPE_NONE,
|
||||
CONST_TYPE_NUMBER,
|
||||
CONST_TYPE_BOOL,
|
||||
CONST_TYPE_NULL,
|
||||
CONST_TYPE_UNDEFINED,
|
||||
CONST_TYPE_STRING
|
||||
} ConstType;
|
||||
|
||||
typedef struct {
|
||||
ConstType type;
|
||||
union {
|
||||
double num_val;
|
||||
bool bool_val;
|
||||
const char *str_val;
|
||||
} val;
|
||||
} ConstValue;
|
||||
|
||||
/* Token optimization pass - processes and transforms token sequences */
|
||||
Result optimizer_run_constant_propagation(TokenString *ts);
|
||||
Result optimizer_run_dead_code_elimination(TokenString *ts);
|
||||
Result optimizer_run_expression_inlining(TokenString *ts);
|
||||
|
||||
/* Main optimizer entry point */
|
||||
Result optimizer_run(TokenString *ts, OptimizerFlags flags);
|
||||
|
||||
#endif /* HERMES_DEC_OPTIMIZER_H */
|
||||
+61
-114
@@ -52,6 +52,22 @@ typedef struct HBCStringMeta {
|
||||
HBCStringKind kind;
|
||||
} HBCStringMeta;
|
||||
|
||||
/* Function metadata structure */
|
||||
typedef struct {
|
||||
const char *name; // Valid while provider is alive; caller must not free
|
||||
u32 offset; // Bytecode offset in file
|
||||
u32 size; // Size in bytes
|
||||
u32 param_count; // Number of parameters
|
||||
} HBCFunc;
|
||||
|
||||
/* String table data */
|
||||
typedef struct {
|
||||
u32 string_count;
|
||||
const void *small_string_table;
|
||||
const void *overflow_string_table;
|
||||
u64 string_storage_offset;
|
||||
} HBCStrs;
|
||||
|
||||
/* Disassembly options */
|
||||
typedef struct {
|
||||
bool verbose; /* Show detailed metadata */
|
||||
@@ -128,137 +144,79 @@ typedef struct {
|
||||
* ============================================================================ */
|
||||
|
||||
/* Opaque data provider handle */
|
||||
/* ============================================================================
|
||||
* HBC - Direct File Access API
|
||||
*
|
||||
* Low-level API for direct file access without abstraction layer.
|
||||
* These functions work directly with HBC files and provide the most
|
||||
* efficient access to the underlying data structures.
|
||||
* ============================================================================ */
|
||||
|
||||
/* Opaque state handle for direct file access */
|
||||
typedef struct HBC HBC;
|
||||
|
||||
/* ============================================================================
|
||||
* HBC Factories - Create a provider from different sources
|
||||
* ============================================================================ */
|
||||
/**
|
||||
* Open an HBC file from disk.
|
||||
* Creates an HBC for direct access to file data.
|
||||
*/
|
||||
Result hbc_open(const char *path, HBC **out);
|
||||
|
||||
/**
|
||||
* Create a provider from a file on disk.
|
||||
* Internally opens and manages the file. Returns NULL on failure.
|
||||
* Open an HBC file from memory buffer.
|
||||
* Creates an HBC for direct access to in-memory data.
|
||||
*/
|
||||
HBC *hbc_new_file(const char *path);
|
||||
Result hbc_open_from_memory(const u8 *data, size_t size, HBC **out);
|
||||
|
||||
/**
|
||||
* Create a provider from a memory buffer.
|
||||
* Data must remain valid for the lifetime of the provider.
|
||||
* Returns NULL on failure.
|
||||
* Close an HBC and free all resources.
|
||||
* After this call, all pointers returned from the state are invalid.
|
||||
*/
|
||||
HBC *hbc_new_buf(const u8 *data, size_t size);
|
||||
void hbc_close(HBC *hbc);
|
||||
|
||||
/**
|
||||
* Create a provider from an r2 RBinFile.
|
||||
* Reads data via r2's RBuffer API (no separate file opens).
|
||||
* Returns NULL if bf is NULL or invalid.
|
||||
* Get the total number of functions in the HBC file.
|
||||
*/
|
||||
typedef struct r_bin_file_t RBinFile;
|
||||
HBC *hbc_new_r2(RBinFile *bf);
|
||||
u32 hbc_function_count(HBC *hbc);
|
||||
|
||||
/**
|
||||
* Free a provider and all associated resources.
|
||||
* After this call, all pointers returned by provider queries are invalid.
|
||||
* Get the total number of strings in the HBC file.
|
||||
*/
|
||||
void hbc_free(HBC *provider);
|
||||
|
||||
/* ============================================================================
|
||||
* Query Methods - Access parsed binary data
|
||||
* ============================================================================ */
|
||||
u32 hbc_string_count(HBC *hbc);
|
||||
|
||||
/**
|
||||
* Get the HBC file header.
|
||||
* Get the HBC file header information.
|
||||
*/
|
||||
Result hbc_hdr(
|
||||
HBC *provider,
|
||||
HBCHeader *out);
|
||||
|
||||
/**
|
||||
* Get the total number of functions in the binary.
|
||||
*/
|
||||
Result hbc_func_count(
|
||||
HBC *provider,
|
||||
u32 *out_count);
|
||||
|
||||
/**
|
||||
* Function metadata structure
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name; // Valid while provider is alive; caller must not free
|
||||
u32 offset; // Bytecode offset in file
|
||||
u32 size; // Size in bytes
|
||||
u32 param_count; // Number of parameters
|
||||
} HBCFunc;
|
||||
Result hbc_get_header(HBC *hbc, HBCHeader *out);
|
||||
|
||||
/**
|
||||
* Get metadata for a specific function.
|
||||
*/
|
||||
Result hbc_func_info(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
HBCFunc *out);
|
||||
|
||||
/**
|
||||
* Get the total number of strings in the binary.
|
||||
*/
|
||||
Result hbc_str_count(
|
||||
HBC *provider,
|
||||
u32 *out_count);
|
||||
Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out);
|
||||
|
||||
/**
|
||||
* Get a string by index.
|
||||
*/
|
||||
Result hbc_str(
|
||||
HBC *provider,
|
||||
u32 string_id,
|
||||
const char **out_str);
|
||||
Result hbc_get_string(HBC *hbc, u32 index, const char **out_str);
|
||||
|
||||
/**
|
||||
* Get metadata for a string (offset, length, kind).
|
||||
*/
|
||||
Result hbc_str_meta(
|
||||
HBC *provider,
|
||||
u32 string_id,
|
||||
HBCStringMeta *out);
|
||||
Result hbc_get_string_meta(HBC *hbc, u32 index, HBCStringMeta *out);
|
||||
|
||||
/**
|
||||
* Get the raw bytecode bytes for a function.
|
||||
* Get string table data for decoding purposes.
|
||||
*/
|
||||
Result hbc_bytecode(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
const u8 **out_ptr,
|
||||
u32 *out_size);
|
||||
|
||||
/**
|
||||
* String table data.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 string_count;
|
||||
const void *small_string_table;
|
||||
const void *overflow_string_table;
|
||||
u64 string_storage_offset;
|
||||
} HBCStrs;
|
||||
|
||||
Result hbc_str_tbl(
|
||||
HBC *provider,
|
||||
HBCStrs *out);
|
||||
Result hbc_get_string_tables(HBC *hbc, HBCStrs *out);
|
||||
|
||||
/**
|
||||
* Get source/module name associated with a function.
|
||||
*/
|
||||
Result hbc_src(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
const char **out_src);
|
||||
Result hbc_get_function_source(HBC *hbc, u32 function_id, const char **out_src);
|
||||
|
||||
/**
|
||||
* Read raw bytes from the binary at a specific offset.
|
||||
* Get the raw bytecode bytes for a function.
|
||||
*/
|
||||
Result hbc_read(
|
||||
HBC *provider,
|
||||
u64 offset,
|
||||
u32 size,
|
||||
const u8 **out_ptr);
|
||||
Result hbc_get_function_bytecode(HBC *hbc, u32 function_id, const u8 **out_ptr, u32 *out_size);
|
||||
|
||||
/* ============================================================================
|
||||
* Decompilation API
|
||||
@@ -268,7 +226,7 @@ Result hbc_read(
|
||||
* Decompile a specific function.
|
||||
*/
|
||||
Result hbc_decomp_fn(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
u32 function_id,
|
||||
HBCDecompOptions options,
|
||||
char **out_str);
|
||||
@@ -277,7 +235,7 @@ Result hbc_decomp_fn(
|
||||
* Decompile all functions.
|
||||
*/
|
||||
Result hbc_decomp_all(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
HBCDecompOptions options,
|
||||
char **out_str);
|
||||
|
||||
@@ -285,7 +243,7 @@ Result hbc_decomp_all(
|
||||
* Disassemble a specific function.
|
||||
*/
|
||||
Result hbc_disasm_fn(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
u32 function_id,
|
||||
HBCDisOptions options,
|
||||
char **out_str);
|
||||
@@ -294,7 +252,7 @@ Result hbc_disasm_fn(
|
||||
* Disassemble all functions.
|
||||
*/
|
||||
Result hbc_disasm_all(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
HBCDisOptions options,
|
||||
char **out_str);
|
||||
|
||||
@@ -322,7 +280,7 @@ typedef struct {
|
||||
* Get all functions at once.
|
||||
*/
|
||||
Result hbc_all_funcs(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
HBCFuncArray *out);
|
||||
|
||||
/**
|
||||
@@ -342,7 +300,7 @@ typedef struct {
|
||||
* Decode a function into an array of instructions.
|
||||
*/
|
||||
Result hbc_decode_fn(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
u32 function_id,
|
||||
HBCInsns *out);
|
||||
|
||||
@@ -378,6 +336,9 @@ typedef struct {
|
||||
|
||||
/* String tables (optional, for string resolution) */
|
||||
const HBCStrs *string_tables;
|
||||
|
||||
/* HBC context (optional, for object/array buffer resolution) */
|
||||
HBC *hbc;
|
||||
} HBCDecodeCtx;
|
||||
|
||||
/**
|
||||
@@ -387,6 +348,7 @@ Result hbc_dec(const HBCDecodeCtx *ctx, HBCInsnInfo *out);
|
||||
|
||||
/**
|
||||
* Decode a single instruction from raw bytes.
|
||||
* @param hbc Optional HBC context for object/array buffer resolution (can be NULL)
|
||||
*/
|
||||
Result hbc_dec_insn(
|
||||
const u8 *bytes,
|
||||
@@ -396,6 +358,7 @@ Result hbc_dec_insn(
|
||||
bool asm_syntax,
|
||||
bool resolve_string_ids,
|
||||
const HBCStrs *string_ctx,
|
||||
HBC *hbc,
|
||||
HBCInsnInfo *out);
|
||||
|
||||
/* ============================================================================
|
||||
@@ -418,20 +381,4 @@ Result hbc_enc_multi(
|
||||
u32 bytecode_version,
|
||||
HBCEncBuf *out);
|
||||
|
||||
/* ============================================================================
|
||||
* CLI Convenience Functions
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* Decompile a file and write output to another file.
|
||||
* Convenience wrapper for CLI usage.
|
||||
*/
|
||||
Result hbc_decompile_file(const char *input_file, const char *output_file);
|
||||
|
||||
/**
|
||||
* Generate an r2 script from a bytecode file.
|
||||
* Convenience wrapper for CLI usage.
|
||||
*/
|
||||
Result hbc_generate_r2_script(const char *input_file, const char *output_file);
|
||||
|
||||
#endif /* LIBHBC_API_H */
|
||||
+38
-36
@@ -67,9 +67,7 @@ lib_opcodes_src = files(
|
||||
|
||||
lib_core_src = files(
|
||||
'src/lib/hbc.c',
|
||||
'src/lib/r2.c',
|
||||
'src/lib/data_provider_file.c',
|
||||
'src/lib/data_provider_buffer.c',
|
||||
'src/lib/r2.c'
|
||||
)
|
||||
|
||||
lib_src = lib_core_src + lib_utils_src + lib_parsers_src + lib_disasm_src + lib_decompile_src + lib_opcodes_src
|
||||
@@ -120,40 +118,44 @@ if r2_core.found()
|
||||
r2_prefix = get_option('r2_prefix')
|
||||
endif
|
||||
|
||||
# arch_hbc plugin
|
||||
shared_library(
|
||||
'arch_hbc',
|
||||
'src/r2/arch_hbc.c',
|
||||
link_with: libhbc,
|
||||
include_directories: include_dirs,
|
||||
dependencies: r2_core,
|
||||
override_options: ['werror=false'],
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
# Check if only core_hbc_one should be built
|
||||
r2_oneplug = get_option('r2_oneplug')
|
||||
|
||||
# bin_hbc plugin
|
||||
shared_library(
|
||||
'bin_hbc',
|
||||
'src/r2/bin_hbc.c',
|
||||
link_with: libhbc,
|
||||
include_directories: include_dirs,
|
||||
dependencies: r2_core,
|
||||
override_options: ['werror=false'],
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
if r2_oneplug
|
||||
# NOTE: arch_hbc, bin_hbc, and asm_hbc plugins are built into
|
||||
# core_hbc_one to avoid duplicate symbols and simplify the build process
|
||||
|
||||
# asm_hbc plugin (pseudo decompiler)
|
||||
shared_library(
|
||||
'asm_hbc',
|
||||
'src/r2/asm_hbc.c',
|
||||
include_directories: include_dirs,
|
||||
dependencies: r2_core,
|
||||
override_options: ['werror=false'],
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
# core_hbc plugin with plugin registration enabled (r2one)
|
||||
shared_library(
|
||||
'core_hbc',
|
||||
'src/r2/core_hbc_one.c',
|
||||
link_with: libhbc,
|
||||
include_directories: include_dirs,
|
||||
dependencies: r2_core,
|
||||
override_options: ['werror=false'],
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
|
||||
message('Building only core_hbc_one plugin (r2_oneplug enabled)')
|
||||
else
|
||||
# NOTE: arch_hbc, bin_hbc, and asm_hbc plugins are now built into
|
||||
# core_hbc_one to avoid duplicate symbols and simplify the build process
|
||||
|
||||
# core_hbc plugin with plugin registration enabled (r2one)
|
||||
shared_library(
|
||||
'core_hbc',
|
||||
'src/r2/core_hbc_one.c',
|
||||
link_with: libhbc,
|
||||
include_directories: include_dirs,
|
||||
dependencies: r2_core,
|
||||
override_options: ['werror=false'],
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
|
||||
message('Building core_hbc_one plugin (individual plugins disabled)')
|
||||
endif
|
||||
|
||||
message('radare2 plugins enabled')
|
||||
message('Plugin directory: ' + r2_plugdir)
|
||||
@@ -171,7 +173,7 @@ message('r2hermes build summary:')
|
||||
message(' Library: libhbc')
|
||||
message(' CLI: libhbctool')
|
||||
if r2_core.found()
|
||||
message(' radare2 plugins: arch_hbc, bin_hbc, asm_hbc')
|
||||
message(' radare2 plugins: core_hbc (includes arch, bin, asm)')
|
||||
message(' Plugin install path: ' + r2_plugdir)
|
||||
else
|
||||
message(' radare2 plugins: disabled (radare2 not found)')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
option('tests', type: 'boolean', value: false, description: 'Build tests')
|
||||
option('r2_oneplug', type: 'boolean', value: false, description: 'Build only core_hbc_one plugin (no individual plugins)')
|
||||
option('r2_plugdir', type: 'string', value: '', description: 'radare2 plugin directory (auto-detected if empty)')
|
||||
option('r2_prefix', type: 'string', value: '', description: 'radare2 installation prefix')
|
||||
@@ -0,0 +1,9 @@
|
||||
# r2plugin
|
||||
|
||||
This directory contains the make and meson files required
|
||||
to link the plugins inside radare2.
|
||||
|
||||
```console
|
||||
cd radare2
|
||||
make -C libr/plugs EXTERNAL_PLUGINS=r2hermes
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
EXTERNAL_PLUGINS+=r2hermes
|
||||
# EXTERNAL_PLUGINS+=hi
|
||||
|
||||
.PHONY: r2hermes
|
||||
|
||||
r2hermes: p/r2hermes
|
||||
|
||||
p/r2hermes:
|
||||
cd p && git clone https://github.com/radareorg/r2hermes
|
||||
cd p/r2hermes && git checkout r2plugone
|
||||
@@ -0,0 +1,3 @@
|
||||
#ifdef R2_CORE_H
|
||||
extern RCorePlugin r_core_plugin_r2hermes;
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
HBC_CORE_WD=$(LIBR)/xps/p/r2hermes
|
||||
CFLAGS+=-I$(HBC_CORE_WD)/include
|
||||
HBC_CORE_OBJ=$(HBC_CORE_WD)/src/r2/core_hbc_one.o
|
||||
LDFLAGS+=$(HBC_CORE_WD)/build/libhbc.a
|
||||
EXTERNAL_STATIC_OBJS+=$(HBC_CORE_OBJ)
|
||||
@@ -0,0 +1,10 @@
|
||||
shared_library(
|
||||
'core_hbc',
|
||||
'../src/r2/core_hbc_one.c',
|
||||
link_with: libhbc,
|
||||
include_directories: ['../include', include_dirs],
|
||||
dependencies: r2_core,
|
||||
override_options: ['werror=false'],
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
@@ -0,0 +1,64 @@
|
||||
# Version
|
||||
# XXX reuse from parent meson.build or wtf
|
||||
version = '1.0.0'
|
||||
|
||||
# Version extraction
|
||||
version_parts = version.split('.')
|
||||
version_major = version_parts[0]
|
||||
version_minor = version_parts.length() > 1 ? version_parts[1] : '0'
|
||||
version_patch = version_parts.length() > 2 ? version_parts[2] : '0'
|
||||
|
||||
# Generate version.h
|
||||
configure_file(
|
||||
input: '../include/hbc/version.h.in',
|
||||
output: 'version.h',
|
||||
configuration: {
|
||||
'VERSION': version,
|
||||
'VERSION_MAJOR': version_major,
|
||||
'VERSION_MINOR': version_minor,
|
||||
'VERSION_PATCH': version_patch,
|
||||
},
|
||||
install_dir: 'libr/hbc',
|
||||
install: true,
|
||||
)
|
||||
|
||||
# Include directories
|
||||
include_dirs = include_directories(join_paths('..', 'include'))
|
||||
r_core_additional_inc += include_dirs
|
||||
r_core_additional_inc += ['../xps/p/r2hermes/include']
|
||||
|
||||
# Library source files
|
||||
lib_utils_src = files(
|
||||
'../src/lib/utils/string_buffer.c',
|
||||
'../src/lib/utils/buffer_reader.c',
|
||||
)
|
||||
|
||||
lib_parsers_src = files(
|
||||
'../src/lib/parsers/hbc_file_parser.c',
|
||||
'../src/lib/parsers/hbc_bytecode_parser.c',
|
||||
)
|
||||
|
||||
lib_disasm_src = [] # Disassembly sources (if any)
|
||||
|
||||
lib_decompile_src = files(
|
||||
'../src/lib/decompilation/translator.c',
|
||||
'../src/lib/decompilation/token.c',
|
||||
'../src/lib/decompilation/literals.c',
|
||||
'../src/lib/decompilation/decompiler.c',
|
||||
)
|
||||
|
||||
lib_opcodes_src = files(
|
||||
'../src/lib/opcodes/isa.c',
|
||||
'../src/lib/opcodes/encoder.c',
|
||||
'../src/lib/opcodes/decoder.c',
|
||||
)
|
||||
|
||||
lib_core_src = files(
|
||||
'../src/lib/hbc.c',
|
||||
'../src/lib/r2.c',
|
||||
'../src/r2/core_hbc.c',
|
||||
)
|
||||
|
||||
lib_src = lib_utils_src + lib_parsers_src + lib_disasm_src + lib_decompile_src + lib_opcodes_src + lib_core_src
|
||||
|
||||
r_core_additional_sources += lib_src
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifdef R2_ARCH_H
|
||||
#include "arch/deps.h"
|
||||
#endif
|
||||
#ifdef R2_BIN_H
|
||||
#include "bin/deps.h"
|
||||
#endif
|
||||
#ifdef R2_ASM_H
|
||||
#include "asm/deps.h"
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
core.r2hermes
|
||||
@@ -1,177 +0,0 @@
|
||||
#include <hbc/hbc.h>
|
||||
#include "hbc_internal_legacy.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* BufferDataProvider wraps a memory buffer containing HBC binary data.
|
||||
* Used for testing and embedded systems without file I/O.
|
||||
*/
|
||||
struct BufferDataProvider {
|
||||
HBCState *hbc;
|
||||
};
|
||||
|
||||
HBC *hbc_new_buf(const u8 *data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)malloc (sizeof (*bp));
|
||||
if (!bp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Use existing hbc_open_from_memory API */
|
||||
Result res = hbc_open_from_memory (data, size, &bp->hbc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
free (bp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (HBC *)bp;
|
||||
}
|
||||
|
||||
Result hbc_hdr(
|
||||
HBC *provider,
|
||||
struct HBCHeader *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_header (bp->hbc, out);
|
||||
}
|
||||
|
||||
Result hbc_func_count(
|
||||
HBC *provider,
|
||||
u32 *out_count) {
|
||||
|
||||
if (!provider || !out_count) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
*out_count = hbc_function_count (bp->hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_func_info(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
HBCFunc *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_function_info (bp->hbc, function_id, out);
|
||||
}
|
||||
|
||||
Result hbc_str_count(
|
||||
HBC *provider,
|
||||
u32 *out_count) {
|
||||
|
||||
if (!provider || !out_count) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
*out_count = hbc_string_count (bp->hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_str(
|
||||
HBC *provider,
|
||||
u32 string_id,
|
||||
const char **out_str) {
|
||||
|
||||
if (!provider || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_string (bp->hbc, string_id, out_str);
|
||||
}
|
||||
|
||||
Result hbc_str_meta(
|
||||
HBC *provider,
|
||||
u32 string_id,
|
||||
HBCStringMeta *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_string_meta (bp->hbc, string_id, out);
|
||||
}
|
||||
|
||||
Result hbc_bytecode(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
const u8 **out_ptr,
|
||||
u32 *out_size) {
|
||||
|
||||
if (!provider || !out_ptr || !out_size) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_function_bytecode (bp->hbc, function_id, out_ptr, out_size);
|
||||
}
|
||||
|
||||
Result hbc_str_tbl(
|
||||
HBC *provider,
|
||||
HBCStrs *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_string_tables (bp->hbc, out);
|
||||
}
|
||||
|
||||
Result hbc_src(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
const char **out_src) {
|
||||
|
||||
if (!provider || !out_src) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
return hbc_get_function_source (bp->hbc, function_id, out_src);
|
||||
}
|
||||
|
||||
Result hbc_read(
|
||||
HBC *provider,
|
||||
u64 offset,
|
||||
u32 size,
|
||||
const u8 **out_ptr) {
|
||||
|
||||
/* Buffer provider doesn't need raw read since HBCState handles it */
|
||||
(void)provider;
|
||||
(void)offset;
|
||||
(void)size;
|
||||
(void)out_ptr;
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED,
|
||||
"Raw read not available for buffer provider");
|
||||
}
|
||||
|
||||
void hbc_free(HBC *provider) {
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct BufferDataProvider *bp = (struct BufferDataProvider *)provider;
|
||||
if (bp->hbc) {
|
||||
hbc_close (bp->hbc);
|
||||
bp->hbc = NULL;
|
||||
}
|
||||
free (bp);
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
#include <hbc/hbc.h>
|
||||
#include "hbc_internal_legacy.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* FileDataProvider wraps HBCState and HBCReader (current implementation).
|
||||
* This maintains backward compatibility while enabling other providers.
|
||||
*/
|
||||
struct FileDataProvider {
|
||||
HBCState *hbc;
|
||||
};
|
||||
|
||||
HBC *hbc_new_file(const char *path) {
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)malloc (sizeof (*fp));
|
||||
if (!fp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Result res = hbc_open (path, &fp->hbc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
free (fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (HBC *)fp;
|
||||
}
|
||||
|
||||
Result hbc_hdr(
|
||||
HBC *provider,
|
||||
struct HBCHeader *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_header (fp->hbc, out);
|
||||
}
|
||||
|
||||
Result hbc_func_count(
|
||||
HBC *provider,
|
||||
u32 *out_count) {
|
||||
|
||||
if (!provider || !out_count) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
*out_count = hbc_function_count (fp->hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_func_info(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
HBCFunc *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_function_info (fp->hbc, function_id, out);
|
||||
}
|
||||
|
||||
Result hbc_str_count(
|
||||
HBC *provider,
|
||||
u32 *out_count) {
|
||||
|
||||
if (!provider || !out_count) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
*out_count = hbc_string_count (fp->hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_str(
|
||||
HBC *provider,
|
||||
u32 string_id,
|
||||
const char **out_str) {
|
||||
|
||||
if (!provider || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_string (fp->hbc, string_id, out_str);
|
||||
}
|
||||
|
||||
Result hbc_str_meta(
|
||||
HBC *provider,
|
||||
u32 string_id,
|
||||
HBCStringMeta *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_string_meta (fp->hbc, string_id, out);
|
||||
}
|
||||
|
||||
Result hbc_bytecode(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
const u8 **out_ptr,
|
||||
u32 *out_size) {
|
||||
|
||||
if (!provider || !out_ptr || !out_size) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_function_bytecode (fp->hbc, function_id, out_ptr, out_size);
|
||||
}
|
||||
|
||||
Result hbc_str_tbl(
|
||||
HBC *provider,
|
||||
HBCStrs *out) {
|
||||
|
||||
if (!provider || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_string_tables (fp->hbc, out);
|
||||
}
|
||||
|
||||
Result hbc_src(
|
||||
HBC *provider,
|
||||
u32 function_id,
|
||||
const char **out_src) {
|
||||
|
||||
if (!provider || !out_src) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
return hbc_get_function_source (fp->hbc, function_id, out_src);
|
||||
}
|
||||
|
||||
Result hbc_read(
|
||||
HBC *provider,
|
||||
u64 offset,
|
||||
u32 size,
|
||||
const u8 **out_ptr) {
|
||||
|
||||
/* File provider doesn't need this since HBCState handles it internally */
|
||||
(void)provider;
|
||||
(void)offset;
|
||||
(void)size;
|
||||
(void)out_ptr;
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED,
|
||||
"Raw read not available for file provider");
|
||||
}
|
||||
|
||||
void hbc_free(HBC *provider) {
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct FileDataProvider *fp = (struct FileDataProvider *)provider;
|
||||
if (fp->hbc) {
|
||||
hbc_close (fp->hbc);
|
||||
fp->hbc = NULL;
|
||||
}
|
||||
free (fp);
|
||||
}
|
||||
@@ -9,24 +9,24 @@
|
||||
#include <hbc/decompilation/translator.h>
|
||||
#include <hbc/opcodes.h>
|
||||
#include <hbc/decompilation/literals.h>
|
||||
#include "../hbc_internal.h"
|
||||
|
||||
/* Ensure that the function's bytecode buffer is loaded into memory. */
|
||||
/**
|
||||
* Load bytecode for a function using the data provider.
|
||||
* Used when decompiling with provider-based API.
|
||||
* Load bytecode for a function using the state.
|
||||
*/
|
||||
static Result ensure_function_bytecode_loaded_from_provider(HBC *hbc, FunctionHeader *function_header, u32 function_id) {
|
||||
static Result ensure_function_bytecode_loaded_from_state(HBC *hbc, FunctionHeader *function_header, u32 function_id) {
|
||||
if (!hbc || !function_header) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "ensure: provider or header NULL");
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "ensure: state or header NULL");
|
||||
}
|
||||
if (function_header->bytecode) {
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/* Get bytecode from provider */
|
||||
/* Get bytecode from state */
|
||||
const u8 *bytecode_ptr = NULL;
|
||||
u32 bytecode_size = 0;
|
||||
Result res = hbc_bytecode (hbc, function_id, &bytecode_ptr, &bytecode_size);
|
||||
Result res = hbc_get_function_bytecode (hbc, function_id, &bytecode_ptr, &bytecode_size);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
@@ -374,6 +374,9 @@ typedef struct {
|
||||
u32 *frame_ends;
|
||||
u32 *bb_starts;
|
||||
u32 *bb_ends;
|
||||
u32 *if_block_stack;
|
||||
u32 if_block_stack_count;
|
||||
u32 if_block_stack_cap;
|
||||
} OutputBuffers;
|
||||
|
||||
static inline void output_buffers_fini(OutputBuffers *ob) {
|
||||
@@ -381,6 +384,21 @@ static inline void output_buffers_fini(OutputBuffers *ob) {
|
||||
free (ob->frame_ends);
|
||||
free (ob->bb_starts);
|
||||
free (ob->bb_ends);
|
||||
free (ob->if_block_stack);
|
||||
}
|
||||
|
||||
static inline Result if_block_stack_push(OutputBuffers *ob, u32 target_addr) {
|
||||
if (ob->if_block_stack_count >= ob->if_block_stack_cap) {
|
||||
u32 new_cap = ob->if_block_stack_cap? ob->if_block_stack_cap * 2: 16;
|
||||
u32 *new_stack = (u32 *)realloc (ob->if_block_stack, new_cap * sizeof (u32));
|
||||
if (!new_stack) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "oom if_block_stack");
|
||||
}
|
||||
ob->if_block_stack = new_stack;
|
||||
ob->if_block_stack_cap = new_cap;
|
||||
}
|
||||
ob->if_block_stack[ob->if_block_stack_count++] = target_addr;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/* ============= CFG construction ============= */
|
||||
@@ -692,7 +710,7 @@ Result _hbc_decompiler_init(HermesDecompiler *decompiler) {
|
||||
decompiler->function_in_progress = NULL;
|
||||
decompiler->indent_level = 0;
|
||||
decompiler->inlining_function = false;
|
||||
decompiler->hbc = NULL; /* Will be set if using provider-based API */
|
||||
decompiler->hbc = NULL; /* Will be set if using state-based API */
|
||||
decompiler->options.pretty_literals = true;
|
||||
decompiler->options.suppress_comments = false;
|
||||
|
||||
@@ -702,9 +720,9 @@ Result _hbc_decompiler_init(HermesDecompiler *decompiler) {
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result _hbc_decompiler_init_with_provider(HermesDecompiler *decompiler, HBC *hbc) {
|
||||
Result _hbc_decompiler_init_with_state(HermesDecompiler *decompiler, HBC *hbc) {
|
||||
if (!decompiler || !hbc) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Null decompiler or provider pointer");
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Null decompiler or state pointer");
|
||||
}
|
||||
|
||||
/* Initialize common fields */
|
||||
@@ -713,7 +731,7 @@ Result _hbc_decompiler_init_with_provider(HermesDecompiler *decompiler, HBC *hbc
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Store provider reference */
|
||||
/* Store state reference */
|
||||
decompiler->hbc = hbc;
|
||||
|
||||
return SUCCESS_RESULT ();
|
||||
@@ -740,9 +758,9 @@ Result _hbc_decompiler_cleanup(HermesDecompiler *decompiler) {
|
||||
decompiler->function_in_progress = NULL;
|
||||
}
|
||||
|
||||
// Free data provider if owned by decompiler
|
||||
/* Note: We don't free the provider here because it may be owned by caller.
|
||||
* The provider lifecycle is managed externally, not by decompiler. */
|
||||
// Free data state if owned by decompiler
|
||||
/* Note: We don't free the state here because it may be owned by caller.
|
||||
* The state lifecycle is managed externally, not by decompiler. */
|
||||
decompiler->hbc = NULL;
|
||||
|
||||
// Cleanup string buffer
|
||||
@@ -773,16 +791,7 @@ Result _hbc_decompile_file(const char *input_file, const char *output_file) {
|
||||
decompiler.output_file = (char *)output_file;
|
||||
decompiler.hbc_reader = &reader;
|
||||
|
||||
// Read and parse the file
|
||||
result = _hbc_reader_read_file (&reader, input_file);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
_hbc_reader_cleanup (&reader);
|
||||
_hbc_decompiler_cleanup (&decompiler);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read header
|
||||
result = _hbc_reader_read_header (&reader);
|
||||
result = _hbc_reader_read_whole_file (&reader, input_file);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
_hbc_reader_cleanup (&reader);
|
||||
_hbc_decompiler_cleanup (&decompiler);
|
||||
@@ -887,30 +896,30 @@ Result _hbc_decompile_all_to_buffer(HBCReader *reader, HBCDecompOptions options,
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result _hbc_decompile_function_with_provider(HBC *hbc, u32 function_id, HBCDecompOptions options, StringBuffer *out) {
|
||||
Result _hbc_decompile_function_with_state(HBC *hbc, u32 function_id, HBCDecompOptions options, StringBuffer *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_decompile_function_with_provider args");
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_decompile_function_with_state args");
|
||||
}
|
||||
|
||||
/* For now, leverage existing _hbc_decompile_function_to_buffer by extracting reader.
|
||||
* In Phase 2, we delegate to the provider API. This is a bridge.
|
||||
* TODO: Refactor _hbc_decompile_function to work directly with provider data */
|
||||
* In Phase 2, we delegate to the state API. This is a bridge.
|
||||
* TODO: Refactor _hbc_decompile_function to work directly with state data */
|
||||
|
||||
/* Try to use existing _hbc_decompile_function_to_buffer if we can extract HBCReader */
|
||||
HermesDecompiler dec;
|
||||
RETURN_IF_ERROR (_hbc_decompiler_init_with_provider (&dec, hbc));
|
||||
RETURN_IF_ERROR (_hbc_decompiler_init_with_state (&dec, hbc));
|
||||
dec.options = options;
|
||||
dec.indent_level = 0;
|
||||
|
||||
/* Create a stub HBCReader for internal use.
|
||||
* The provider is responsible for all actual data access. */
|
||||
* The state is responsible for all actual data access. */
|
||||
HBCReader stub_reader;
|
||||
memset (&stub_reader, 0, sizeof (stub_reader));
|
||||
dec.hbc_reader = &stub_reader;
|
||||
|
||||
/* Get header from provider and populate stub */
|
||||
/* Get header from state and populate stub */
|
||||
HBCHeader header;
|
||||
Result hres = hbc_hdr (hbc, &header);
|
||||
Result hres = hbc_get_header (hbc, &header);
|
||||
if (hres.code != RESULT_SUCCESS) {
|
||||
_hbc_decompiler_cleanup (&dec);
|
||||
return hres;
|
||||
@@ -932,7 +941,7 @@ Result _hbc_decompile_function_with_provider(HBC *hbc, u32 function_id, HBCDecom
|
||||
|
||||
for (u32 i = 0; i < header.functionCount; i++) {
|
||||
HBCFunc fi;
|
||||
Result fres = hbc_func_info (hbc, i, &fi);
|
||||
Result fres = hbc_get_function_info (hbc, i, &fi);
|
||||
if (fres.code != RESULT_SUCCESS) {
|
||||
free (stub_reader.function_headers);
|
||||
_hbc_decompiler_cleanup (&dec);
|
||||
@@ -943,7 +952,7 @@ Result _hbc_decompile_function_with_provider(HBC *hbc, u32 function_id, HBCDecom
|
||||
stub_reader.function_headers[i].bytecode = NULL;
|
||||
}
|
||||
|
||||
/* Populate strings array from provider */
|
||||
/* Populate strings array from state */
|
||||
if (header.stringCount > 0) {
|
||||
stub_reader.strings = (char **)calloc (header.stringCount, sizeof (char *));
|
||||
if (!stub_reader.strings) {
|
||||
@@ -953,20 +962,24 @@ Result _hbc_decompile_function_with_provider(HBC *hbc, u32 function_id, HBCDecom
|
||||
}
|
||||
for (u32 i = 0; i < header.stringCount; i++) {
|
||||
const char *str = NULL;
|
||||
Result sres = hbc_str (hbc, i, &str);
|
||||
Result sres = hbc_get_string (hbc, i, &str);
|
||||
if (sres.code == RESULT_SUCCESS && str) {
|
||||
stub_reader.strings[i] = (char *)str; /* Provider owns the string, we just reference it */
|
||||
stub_reader.strings[i] = (char *)str; /* State owns the string, we just reference it */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stub_reader.object_keys = hbc->reader.object_keys;
|
||||
stub_reader.object_values = hbc->reader.object_values;
|
||||
stub_reader.arrays = hbc->reader.arrays;
|
||||
|
||||
Result r = _hbc_decompile_function (&dec, function_id, NULL, -1, false, false, false);
|
||||
if (r.code == RESULT_SUCCESS) {
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (&dec.output, "\n\n"));
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, dec.output.data? dec.output.data: ""));
|
||||
}
|
||||
|
||||
/* Free allocated arrays (strings are owned by provider, don't free individual strings) */
|
||||
/* Free allocated arrays (strings are owned by state, don't free individual strings) */
|
||||
free (stub_reader.strings);
|
||||
free (stub_reader.function_headers);
|
||||
|
||||
@@ -974,12 +987,12 @@ Result _hbc_decompile_function_with_provider(HBC *hbc, u32 function_id, HBCDecom
|
||||
return r;
|
||||
}
|
||||
|
||||
Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, StringBuffer *out) {
|
||||
Result _hbc_decompile_all_with_state(HBC *hbc, HBCDecompOptions options, StringBuffer *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for _hbc_decompile_all_with_provider");
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for _hbc_decompile_all_with_state");
|
||||
}
|
||||
HermesDecompiler dec;
|
||||
RETURN_IF_ERROR (_hbc_decompiler_init_with_provider (&dec, hbc));
|
||||
RETURN_IF_ERROR (_hbc_decompiler_init_with_state (&dec, hbc));
|
||||
dec.options = options;
|
||||
dec.indent_level = 0;
|
||||
|
||||
@@ -988,16 +1001,16 @@ Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, Stri
|
||||
memset (&stub_reader, 0, sizeof (stub_reader));
|
||||
dec.hbc_reader = &stub_reader;
|
||||
|
||||
/* Get header from provider and populate stub */
|
||||
/* Get header from state and populate stub */
|
||||
HBCHeader header;
|
||||
Result res = hbc_hdr (hbc, &header);
|
||||
Result res = hbc_get_header (hbc, &header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
_hbc_decompiler_cleanup (&dec);
|
||||
return res;
|
||||
}
|
||||
stub_reader.header = header;
|
||||
|
||||
/* Get function count from provider */
|
||||
/* Get function count from state */
|
||||
u32 func_count = header.functionCount;
|
||||
if (func_count == 0) {
|
||||
/* No functions to decompile */
|
||||
@@ -1020,10 +1033,10 @@ Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, Stri
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate function_headers");
|
||||
}
|
||||
|
||||
/* Populate function_headers from provider */
|
||||
/* Populate function_headers from state */
|
||||
for (u32 i = 0; i < func_count; i++) {
|
||||
HBCFunc fi;
|
||||
Result fres = hbc_func_info (hbc, i, &fi);
|
||||
Result fres = hbc_get_function_info (hbc, i, &fi);
|
||||
if (fres.code != RESULT_SUCCESS) {
|
||||
free (stub_reader.function_headers);
|
||||
_hbc_decompiler_cleanup (&dec);
|
||||
@@ -1032,10 +1045,10 @@ Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, Stri
|
||||
stub_reader.function_headers[i].offset = fi.offset;
|
||||
stub_reader.function_headers[i].bytecodeSizeInBytes = fi.size;
|
||||
stub_reader.function_headers[i].bytecode = NULL; /* Will be loaded on demand */
|
||||
/* Other fields are not needed for provider-based decompilation */
|
||||
/* Other fields are not needed for state-based decompilation */
|
||||
}
|
||||
|
||||
/* Populate strings array from provider */
|
||||
/* Populate strings array from state */
|
||||
if (header.stringCount > 0) {
|
||||
stub_reader.strings = (char **)calloc (header.stringCount, sizeof (char *));
|
||||
if (!stub_reader.strings) {
|
||||
@@ -1045,17 +1058,21 @@ Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, Stri
|
||||
}
|
||||
for (u32 i = 0; i < header.stringCount; i++) {
|
||||
const char *str = NULL;
|
||||
Result sres = hbc_str (hbc, i, &str);
|
||||
Result sres = hbc_get_string (hbc, i, &str);
|
||||
if (sres.code == RESULT_SUCCESS && str) {
|
||||
stub_reader.strings[i] = (char *)str; /* Provider owns the string, we just reference it */
|
||||
stub_reader.strings[i] = (char *)str; /* State owns the string, we just reference it */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stub_reader.object_keys = hbc->reader.object_keys;
|
||||
stub_reader.object_values = hbc->reader.object_values;
|
||||
stub_reader.arrays = hbc->reader.arrays;
|
||||
|
||||
/* File preamble */
|
||||
if (!options.suppress_comments) {
|
||||
HBCHeader header;
|
||||
res = hbc_hdr (hbc, &header);
|
||||
res = hbc_get_header (hbc, &header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
_hbc_decompiler_cleanup (&dec);
|
||||
return res;
|
||||
@@ -1083,7 +1100,7 @@ Result _hbc_decompile_all_with_provider(HBC *hbc, HBCDecompOptions options, Stri
|
||||
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, dec.output.data? dec.output.data: ""));
|
||||
|
||||
/* Free allocated arrays (strings are owned by provider, don't free individual strings) */
|
||||
/* Free allocated arrays (strings are owned by state, don't free individual strings) */
|
||||
free (stub_reader.strings);
|
||||
free (stub_reader.function_headers);
|
||||
|
||||
@@ -1141,8 +1158,10 @@ static bool bbvec_contains(BasicBlock **arr, u32 count, BasicBlock *bb) {
|
||||
}
|
||||
|
||||
static bool token_needs_space(TokenType prev, TokenType cur) {
|
||||
/* Space before '(' for readability */
|
||||
if (cur == TOKEN_TYPE_LEFT_PARENTHESIS) {
|
||||
if (prev == TOKEN_TYPE_RIGHT_HAND_REG || prev == TOKEN_TYPE_LEFT_HAND_REG) {
|
||||
return false; /* r1 (args) not r1 (args) */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/* No space before other punctuation */
|
||||
@@ -1659,13 +1678,31 @@ static Result identify_dead_assignments(DecompiledFunctionBody *function_body, b
|
||||
bool is_assignment = (st->head->type == TOKEN_TYPE_LEFT_HAND_REG);
|
||||
int written_reg_num = -1;
|
||||
|
||||
bool has_call = false;
|
||||
bool has_jump = false;
|
||||
for (Token *tok = st->head; tok; tok = tok->next) {
|
||||
if (tok->type == TOKEN_TYPE_LEFT_PARENTHESIS) {
|
||||
has_call = true;
|
||||
}
|
||||
if (tok->type == TOKEN_TYPE_JUMP_CONDITION || tok->type == TOKEN_TYPE_JUMP_NOT_CONDITION) {
|
||||
has_jump = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_jump) {
|
||||
for (int r = 0; r < 256; r++) {
|
||||
register_read_after[r] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_assignment) {
|
||||
LeftHandRegToken *lhr = (LeftHandRegToken *)st->head;
|
||||
written_reg_num = lhr->reg_num;
|
||||
|
||||
/* Mark as dead if it writes a register never read after this point */
|
||||
/* BUT never eliminate function calls - they may have side effects */
|
||||
if (written_reg_num >= 0 && written_reg_num < 256) {
|
||||
if (!register_read_after[written_reg_num]) {
|
||||
if (!register_read_after[written_reg_num] && !has_call) {
|
||||
dce[si] = true;
|
||||
}
|
||||
/* Now mark this register as written (no longer readable after) */
|
||||
@@ -1858,6 +1895,13 @@ Result _hbc_output_code(HermesDecompiler *state, DecompiledFunctionBody *functio
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, "}\n"));
|
||||
}
|
||||
|
||||
while (ob.if_block_stack_count > 0 && ob.if_block_stack[ob.if_block_stack_count - 1] == pos) {
|
||||
ob.if_block_stack_count--;
|
||||
state->indent_level--;
|
||||
RETURN_IF_ERROR (append_indent (out, state->indent_level));
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, "}\n"));
|
||||
}
|
||||
|
||||
/* Basic block case label */
|
||||
if (use_dispatch) {
|
||||
bool is_bb_start = (bsearch (&pos, ob.bb_starts, bbcount, sizeof (u32), cmp_u32) != NULL);
|
||||
@@ -1945,8 +1989,54 @@ Result _hbc_output_code(HermesDecompiler *state, DecompiledFunctionBody *functio
|
||||
|
||||
bool emitted_continue = false;
|
||||
Token *head = st->head;
|
||||
/* Skip jump statements entirely - dispatch is disabled */
|
||||
if (head->type == TOKEN_TYPE_JUMP_NOT_CONDITION || head->type == TOKEN_TYPE_JUMP_CONDITION) {
|
||||
u32 target_addr = 0;
|
||||
if (head->type == TOKEN_TYPE_JUMP_CONDITION) {
|
||||
target_addr = ((JumpConditionToken *)head)->target_address;
|
||||
} else {
|
||||
target_addr = ((JumpNotConditionToken *)head)->target_address;
|
||||
}
|
||||
u32 current_pos = asm_ref? asm_ref->original_pos: 0;
|
||||
|
||||
bool is_unconditional = false;
|
||||
if (head->next && head->next->type == TOKEN_TYPE_RAW && !head->next->next) {
|
||||
RawToken *rt = (RawToken *)head->next;
|
||||
if (rt->text && (strcmp (rt->text, "true") == 0 || strcmp (rt->text, "false") == 0)) {
|
||||
is_unconditional = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_unconditional) {
|
||||
if (target_addr > current_pos) {
|
||||
if (!state->options.suppress_comments) {
|
||||
char goto_buf[64];
|
||||
snprintf (goto_buf, sizeof (goto_buf), "/* goto 0x%08x */\n", target_addr);
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, goto_buf));
|
||||
}
|
||||
} else {
|
||||
if (!state->options.suppress_comments) {
|
||||
char goto_buf[64];
|
||||
snprintf (goto_buf, sizeof (goto_buf), "/* loop to 0x%08x */\n", target_addr);
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, goto_buf));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, "if ("));
|
||||
for (Token *t = head->next; t; t = t->next) {
|
||||
RETURN_IF_ERROR (_hbc_token_to_string (t, out));
|
||||
}
|
||||
|
||||
if (target_addr > current_pos) {
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, ") {\n"));
|
||||
RETURN_IF_ERROR (if_block_stack_push (&ob, target_addr));
|
||||
state->indent_level++;
|
||||
} else {
|
||||
char goto_buf[64];
|
||||
snprintf (goto_buf, sizeof (goto_buf), ") { /* loop to 0x%08x */ }\n", target_addr);
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, goto_buf));
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
/* Emit all tokens, handling nested function expressions */
|
||||
@@ -2010,7 +2100,17 @@ Result _hbc_output_code(HermesDecompiler *state, DecompiledFunctionBody *functio
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!first_tok && token_needs_space (prev_type, t->type)) {
|
||||
bool needs_space = false;
|
||||
if (!first_tok) {
|
||||
needs_space = token_needs_space (prev_type, t->type);
|
||||
if (needs_space && t->type == TOKEN_TYPE_RAW) {
|
||||
RawToken *rt = (RawToken *)t;
|
||||
if (rt->text && (rt->text[0] == ',' || rt->text[0] == ';' || rt->text[0] == ')')) {
|
||||
needs_space = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needs_space) {
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append_char (out, ' '));
|
||||
}
|
||||
RETURN_IF_ERROR (_hbc_token_to_string (t, out));
|
||||
@@ -2059,6 +2159,13 @@ Result _hbc_output_code(HermesDecompiler *state, DecompiledFunctionBody *functio
|
||||
}
|
||||
}
|
||||
|
||||
while (ob.if_block_stack_count > 0) {
|
||||
ob.if_block_stack_count--;
|
||||
state->indent_level--;
|
||||
RETURN_IF_ERROR (append_indent (out, state->indent_level));
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, "}\n"));
|
||||
}
|
||||
|
||||
/* Close switch loop */
|
||||
if (use_dispatch) {
|
||||
state->indent_level--;
|
||||
@@ -2120,9 +2227,9 @@ Result _hbc_decompile_function(HermesDecompiler *state, u32 function_id, Environ
|
||||
state->decompiled_functions[function_id] = true;
|
||||
}
|
||||
|
||||
/* Load bytecode either from provider or from file buffer */
|
||||
/* Load bytecode either from state or from file buffer */
|
||||
if (state->hbc) {
|
||||
r = ensure_function_bytecode_loaded_from_provider (state->hbc, &reader->function_headers[function_id], function_id);
|
||||
r = ensure_function_bytecode_loaded_from_state (state->hbc, &reader->function_headers[function_id], function_id);
|
||||
} else {
|
||||
r = ensure_function_bytecode_loaded (reader, function_id);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@ Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, StringBu
|
||||
s = r->strings[string_id];
|
||||
}
|
||||
if (_hbc_is_js_identifier (s)) {
|
||||
return _hbc_string_buffer_append (out, s? (const char *)".": ".");
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, "."));
|
||||
return _hbc_string_buffer_append (out, s);
|
||||
}
|
||||
RETURN_IF_ERROR (_hbc_string_buffer_append (out, "["));
|
||||
if (s) {
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#include <hbc/decompilation/optimizer.h>
|
||||
#include <hbc/decompilation/token.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Constant Propagation Pass - analyzes constant assignments */
|
||||
Result optimizer_run_constant_propagation(TokenString *ts) {
|
||||
if (!ts) {
|
||||
return ERROR_RESULT(RESULT_ERROR_INVALID_ARGUMENT, "null tokenstring");
|
||||
}
|
||||
|
||||
/* Track register values during forward scan */
|
||||
typedef struct {
|
||||
int reg_num;
|
||||
double value;
|
||||
bool is_constant;
|
||||
} RegValue;
|
||||
|
||||
RegValue *regs = (RegValue *)malloc(sizeof(RegValue) * 256);
|
||||
if (!regs) {
|
||||
return ERROR_RESULT(RESULT_ERROR_MEMORY_ALLOCATION, "oom");
|
||||
}
|
||||
memset(regs, 0, sizeof(RegValue) * 256);
|
||||
|
||||
/* Scan tokens and detect patterns:
|
||||
1. r_N = constant (mark reg as constant)
|
||||
2. r_N = r_A op r_B where r_A, r_B are constants (fold and compute)
|
||||
*/
|
||||
|
||||
Token *t = ts->head;
|
||||
while (t) {
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
free(regs);
|
||||
return SUCCESS_RESULT();
|
||||
}
|
||||
|
||||
/* Dead Code Elimination - marks unused register definitions for potential removal */
|
||||
Result optimizer_run_dead_code_elimination(TokenString *ts) {
|
||||
if (!ts) {
|
||||
return ERROR_RESULT(RESULT_ERROR_INVALID_ARGUMENT, "null tokenstring");
|
||||
}
|
||||
|
||||
/* Track which registers are actually used after being assigned */
|
||||
typedef struct {
|
||||
int reg_num;
|
||||
bool is_used;
|
||||
int use_count;
|
||||
} RegUse;
|
||||
|
||||
RegUse *reg_uses = (RegUse *)malloc(sizeof(RegUse) * 256);
|
||||
if (!reg_uses) {
|
||||
return ERROR_RESULT(RESULT_ERROR_MEMORY_ALLOCATION, "oom");
|
||||
}
|
||||
memset(reg_uses, 0, sizeof(RegUse) * 256);
|
||||
|
||||
/* Forward pass: track register usage */
|
||||
Token *t = ts->head;
|
||||
while (t) {
|
||||
if (t->type == TOKEN_TYPE_RIGHT_HAND_REG) {
|
||||
RightHandRegToken *rhs = (RightHandRegToken *)t;
|
||||
if (rhs->reg_num < 256) {
|
||||
reg_uses[rhs->reg_num].is_used = true;
|
||||
reg_uses[rhs->reg_num].use_count++;
|
||||
}
|
||||
}
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
/* Identify unused registers:
|
||||
- Defined (LHS) but never used (RHS)
|
||||
- These can potentially be eliminated (future optimization)
|
||||
*/
|
||||
|
||||
free(reg_uses);
|
||||
return SUCCESS_RESULT();
|
||||
}
|
||||
|
||||
/* Expression Inlining - inline simple single-use expressions */
|
||||
Result optimizer_run_expression_inlining(TokenString *ts) {
|
||||
if (!ts) {
|
||||
return ERROR_RESULT(RESULT_ERROR_INVALID_ARGUMENT, "null tokenstring");
|
||||
}
|
||||
|
||||
/* Identify inlining opportunities:
|
||||
- Single register use
|
||||
- Simple assignment (literal or single operation)
|
||||
- Not critical for control flow
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int reg_num;
|
||||
int def_count;
|
||||
int use_count;
|
||||
Token *def_token;
|
||||
} InlineCandidate;
|
||||
|
||||
InlineCandidate *candidates = (InlineCandidate *)malloc(sizeof(InlineCandidate) * 256);
|
||||
if (!candidates) {
|
||||
return ERROR_RESULT(RESULT_ERROR_MEMORY_ALLOCATION, "oom");
|
||||
}
|
||||
memset(candidates, 0, sizeof(InlineCandidate) * 256);
|
||||
|
||||
/* Count definitions and uses */
|
||||
Token *t = ts->head;
|
||||
while (t) {
|
||||
if (t->type == TOKEN_TYPE_LEFT_HAND_REG) {
|
||||
LeftHandRegToken *lhs = (LeftHandRegToken *)t;
|
||||
if (lhs->reg_num < 256) {
|
||||
candidates[lhs->reg_num].def_count++;
|
||||
if (candidates[lhs->reg_num].def_count == 1) {
|
||||
candidates[lhs->reg_num].def_token = t;
|
||||
}
|
||||
}
|
||||
} else if (t->type == TOKEN_TYPE_RIGHT_HAND_REG) {
|
||||
RightHandRegToken *rhs = (RightHandRegToken *)t;
|
||||
if (rhs->reg_num < 256) {
|
||||
candidates[rhs->reg_num].use_count++;
|
||||
}
|
||||
}
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
/* Candidates for inlining: defined once, used once or twice (threshold) */
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (candidates[i].def_count == 1 && candidates[i].use_count <= 2) {
|
||||
/* This register is a candidate for inlining */
|
||||
/* Future: perform actual inlining here */
|
||||
}
|
||||
}
|
||||
|
||||
free(candidates);
|
||||
return SUCCESS_RESULT();
|
||||
}
|
||||
|
||||
/* Main optimizer dispatcher */
|
||||
Result optimizer_run(TokenString *ts, OptimizerFlags flags) {
|
||||
if (!ts) {
|
||||
return ERROR_RESULT(RESULT_ERROR_INVALID_ARGUMENT, "null tokenstring");
|
||||
}
|
||||
|
||||
Result r;
|
||||
|
||||
if (flags & OPTIMIZER_CONSTANT_PROPAGATION) {
|
||||
r = optimizer_run_constant_propagation(ts);
|
||||
if (r.code != RESULT_SUCCESS) return r;
|
||||
}
|
||||
|
||||
if (flags & OPTIMIZER_DEAD_CODE_ELIMINATION) {
|
||||
r = optimizer_run_dead_code_elimination(ts);
|
||||
if (r.code != RESULT_SUCCESS) return r;
|
||||
}
|
||||
|
||||
if (flags & OPTIMIZER_EXPRESSION_INLINING) {
|
||||
r = optimizer_run_expression_inlining(ts);
|
||||
if (r.code != RESULT_SUCCESS) return r;
|
||||
}
|
||||
|
||||
return SUCCESS_RESULT();
|
||||
}
|
||||
@@ -216,14 +216,14 @@ static Result emit_incdec(TokenString *out, const ParsedInstruction *insn, const
|
||||
return add (out, create_raw_token (suffix));
|
||||
}
|
||||
|
||||
static Result emit_call_fixed(TokenString *out, const ParsedInstruction *insn, int argc) {
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn, 0)));
|
||||
static Result emit_call_fixed(TokenString *out, const ParsedInstruction *insn, int operand_count) {
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn, 0))); /* dest */
|
||||
RETURN_IF_ERROR (add (out, create_assignment_token ()));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn, 1))); /* callee */
|
||||
RETURN_IF_ERROR (add (out, create_left_parenthesis_token ()));
|
||||
for (int i = 1; i <= argc; i++) {
|
||||
if (i > 1) {
|
||||
RETURN_IF_ERROR (add (out, create_raw_token (", ")));
|
||||
for (int i = 2; i < operand_count; i++) { /* args start at operand 2 */
|
||||
if (i > 2) {
|
||||
RETURN_IF_ERROR (add (out, create_raw_token (",")));
|
||||
}
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn, i)));
|
||||
}
|
||||
@@ -536,9 +536,8 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
{
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, create_assignment_token ()));
|
||||
/* a<idx> */
|
||||
char buf[32];
|
||||
snprintf (buf, sizeof (buf), "a%u", insn->arg2);
|
||||
snprintf (buf, sizeof (buf), "args[%u]", insn->arg2 > 0? insn->arg2 - 1: 0);
|
||||
RETURN_IF_ERROR (add (out, create_raw_token (buf)));
|
||||
break;
|
||||
}
|
||||
@@ -706,7 +705,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
case OP_PutNewOwnByIdLong:
|
||||
{
|
||||
/* obj.name = value (prefer dot accessor) */
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
u32 sid = (op == OP_PutNewOwnByIdShort)? insn->arg3: (op == OP_PutNewOwnById)? insn->arg3
|
||||
: insn->arg4;
|
||||
const char *s = (insn->hbc_reader && insn->hbc_reader->strings && sid < insn->hbc_reader->header.stringCount)? insn->hbc_reader->strings[sid]: NULL;
|
||||
@@ -726,7 +725,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
case OP_PutOwnByIndexL:
|
||||
{
|
||||
/* obj[idx] = value */
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("[")));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 1)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("]")));
|
||||
@@ -737,7 +736,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
case OP_PutByVal:
|
||||
{
|
||||
/* rObj[rKey] = rVal */
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0))); /* use LHS token for consistency */
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0))); /* object is read, not written */
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("[")));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 1)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("]")));
|
||||
@@ -749,7 +748,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
case OP_PutByIdLong:
|
||||
{
|
||||
/* rObj["name"] = rVal */
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("[")));
|
||||
u32 sid = (op == OP_PutById)? insn->arg4: insn->arg4;
|
||||
RETURN_IF_ERROR (add (out, quoted_string (insn->hbc_reader, sid)));
|
||||
@@ -908,13 +907,13 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
break;
|
||||
}
|
||||
case OP_Call1:
|
||||
return emit_call_fixed (out, insn_c, 2);
|
||||
return emit_call_fixed (out, insn_c, 3); /* dest, callee, arg1 */
|
||||
case OP_Call2:
|
||||
return emit_call_fixed (out, insn_c, 3);
|
||||
return emit_call_fixed (out, insn_c, 4); /* dest, callee, arg1, arg2 */
|
||||
case OP_Call3:
|
||||
return emit_call_fixed (out, insn_c, 4);
|
||||
return emit_call_fixed (out, insn_c, 5); /* dest, callee, arg1, arg2, arg3 */
|
||||
case OP_Call4:
|
||||
return emit_call_fixed (out, insn_c, 5);
|
||||
return emit_call_fixed (out, insn_c, 6); /* dest, callee, arg1, arg2, arg3, arg4 */
|
||||
case OP_Call:
|
||||
case OP_CallLong:
|
||||
case OP_Construct:
|
||||
@@ -1160,7 +1159,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
case OP_TryPutById:
|
||||
case OP_TryPutByIdLong:
|
||||
{
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
u32 sid = insn->arg3;
|
||||
const char *s = NULL;
|
||||
if (insn->hbc_reader && insn->hbc_reader->strings && sid < insn->hbc_reader->header.stringCount) {
|
||||
@@ -1181,7 +1180,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
case OP_PutNewOwnNEById:
|
||||
case OP_PutNewOwnNEByIdLong:
|
||||
{
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
u32 sid = (op == OP_PutNewOwnNEById)? insn->arg3: insn->arg4;
|
||||
const char *s = NULL;
|
||||
if (insn->hbc_reader && insn->hbc_reader->strings && sid < insn->hbc_reader->header.stringCount) {
|
||||
@@ -1201,7 +1200,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
}
|
||||
case OP_PutOwnByVal:
|
||||
{
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("[")));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 1)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("]")));
|
||||
@@ -1211,7 +1210,7 @@ Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn_c, Tok
|
||||
}
|
||||
case OP_PutOwnGetterSetterByVal:
|
||||
{
|
||||
RETURN_IF_ERROR (add (out, reg_l_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 0)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("[")));
|
||||
RETURN_IF_ERROR (add (out, reg_r_safe (insn_c, 1)));
|
||||
RETURN_IF_ERROR (add (out, create_raw_token ("]")));
|
||||
|
||||
+152
-124
@@ -2,154 +2,154 @@
|
||||
#include <hbc/common.h>
|
||||
#include <hbc/disasm.h>
|
||||
#include <hbc/decompilation/decompiler.h>
|
||||
#include <hbc/decompilation/literals.h>
|
||||
#include <hbc/hermes_encoder.h>
|
||||
#include <hbc/parser.h>
|
||||
#include <hbc/bytecode.h>
|
||||
#include "hbc_internal.h"
|
||||
#include "hbc_internal_legacy.h"
|
||||
#include <hbc/opcodes.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ============================================================================
|
||||
* INTERNAL LEGACY API - HBCState Implementation (For Data Providers Only)
|
||||
* HBC - Direct File Access API Implementation
|
||||
* ============================================================================ */
|
||||
|
||||
Result hbc_open(const char *path, HBCState **out) {
|
||||
Result hbc_open(const char *path, HBC **out) {
|
||||
if (!path || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for hbc_open");
|
||||
}
|
||||
|
||||
HBCState *hd = (HBCState *)calloc (1, sizeof (HBCState));
|
||||
if (!hd) {
|
||||
HBC *hbc = (HBC *)calloc (1, sizeof (HBC));
|
||||
if (!hbc) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate HBC");
|
||||
}
|
||||
|
||||
Result res = _hbc_reader_init (&hd->reader);
|
||||
Result res = _hbc_reader_init (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
free (hd);
|
||||
free (hbc);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = _hbc_reader_read_whole_file (&hd->reader, path);
|
||||
res = _hbc_reader_read_whole_file (&hbc->reader, path);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
_hbc_reader_cleanup (&hd->reader);
|
||||
free (hd);
|
||||
_hbc_reader_cleanup (&hbc->reader);
|
||||
free (hbc);
|
||||
return res;
|
||||
}
|
||||
|
||||
*out = hd;
|
||||
*out = hbc;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_open_from_memory(const u8 *data, size_t size, HBCState **out) {
|
||||
Result hbc_open_from_memory(const u8 *data, size_t size, HBC **out) {
|
||||
if (!data || size == 0 || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for hbc_open_from_memory");
|
||||
}
|
||||
|
||||
HBCState *hd = (HBCState *)calloc (1, sizeof (HBCState));
|
||||
if (!hd) {
|
||||
HBC *hbc = (HBC *)calloc (1, sizeof (HBC));
|
||||
if (!hbc) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate HBC");
|
||||
}
|
||||
|
||||
Result res = _hbc_reader_init (&hd->reader);
|
||||
Result res = _hbc_reader_init (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
free (hd);
|
||||
free (hbc);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = _hbc_buffer_reader_init_from_memory (&hd->reader.file_buffer, data, size);
|
||||
res = _hbc_buffer_reader_init_from_memory (&hbc->reader.file_buffer, data, size);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
_hbc_reader_cleanup (&hd->reader);
|
||||
free (hd);
|
||||
_hbc_reader_cleanup (&hbc->reader);
|
||||
free (hbc);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Parse header and all sections */
|
||||
res = _hbc_reader_read_header (&hd->reader);
|
||||
res = _hbc_reader_read_header (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_functions_robust (&hd->reader);
|
||||
res = _hbc_reader_read_functions_robust (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_string_kinds (&hd->reader);
|
||||
res = _hbc_reader_read_string_kinds (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_identifier_hashes (&hd->reader);
|
||||
res = _hbc_reader_read_identifier_hashes (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_string_tables (&hd->reader);
|
||||
res = _hbc_reader_read_string_tables (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_arrays (&hd->reader);
|
||||
res = _hbc_reader_read_arrays (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_bigints (&hd->reader);
|
||||
res = _hbc_reader_read_bigints (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_regexp (&hd->reader);
|
||||
res = _hbc_reader_read_regexp (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_cjs_modules (&hd->reader);
|
||||
res = _hbc_reader_read_cjs_modules (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
res = _hbc_reader_read_function_sources (&hd->reader);
|
||||
res = _hbc_reader_read_function_sources (&hbc->reader);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_close (hd);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
(void)_hbc_reader_read_debug_info;
|
||||
|
||||
*out = hd;
|
||||
*out = hbc;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
void hbc_close(HBCState *hd) {
|
||||
if (!hd) {
|
||||
void hbc_close(HBC *hbc) {
|
||||
if (!hbc) {
|
||||
return;
|
||||
}
|
||||
_hbc_reader_cleanup (&hd->reader);
|
||||
free (hd);
|
||||
_hbc_reader_cleanup (&hbc->reader);
|
||||
free (hbc);
|
||||
}
|
||||
|
||||
u32 hbc_function_count(HBCState *hd) {
|
||||
if (!hd) {
|
||||
u32 hbc_function_count(HBC *hbc) {
|
||||
if (!hbc) {
|
||||
return 0;
|
||||
}
|
||||
return hd->reader.header.functionCount;
|
||||
return hbc->reader.header.functionCount;
|
||||
}
|
||||
|
||||
u32 hbc_string_count(HBCState *hd) {
|
||||
if (!hd) {
|
||||
u32 hbc_string_count(HBC *hbc) {
|
||||
if (!hbc) {
|
||||
return 0;
|
||||
}
|
||||
return hd->reader.header.stringCount;
|
||||
return hbc->reader.header.stringCount;
|
||||
}
|
||||
|
||||
Result hbc_get_header(HBCState *hd, HBCHeader *out) {
|
||||
if (!hd || !out) {
|
||||
Result hbc_get_header(HBC *hbc, HBCHeader *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for hbc_get_header");
|
||||
}
|
||||
HBCHeader *h = &hd->reader.header;
|
||||
HBCHeader *h = &hbc->reader.header;
|
||||
out->magic = h->magic;
|
||||
out->version = h->version;
|
||||
memcpy (out->sourceHash, h->sourceHash, sizeof (out->sourceHash));
|
||||
@@ -178,18 +178,18 @@ Result hbc_get_header(HBCState *hd, HBCHeader *out) {
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_get_function_info(HBCState *hd, u32 function_id, HBCFunc *out) {
|
||||
if (!hd || !out) {
|
||||
Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
if (function_id >= hd->reader.header.functionCount) {
|
||||
if (function_id >= hbc->reader.header.functionCount) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Function ID out of range");
|
||||
}
|
||||
const FunctionHeader *fh = &hd->reader.function_headers[function_id];
|
||||
const FunctionHeader *fh = &hbc->reader.function_headers[function_id];
|
||||
/* Get function name from string table */
|
||||
const char *name = NULL;
|
||||
if (fh->functionName < hd->reader.header.stringCount) {
|
||||
name = hd->reader.strings[fh->functionName];
|
||||
if (fh->functionName < hbc->reader.header.stringCount) {
|
||||
name = hbc->reader.strings[fh->functionName];
|
||||
}
|
||||
out->name = name;
|
||||
out->offset = fh->offset;
|
||||
@@ -198,22 +198,22 @@ Result hbc_get_function_info(HBCState *hd, u32 function_id, HBCFunc *out) {
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_get_string(HBCState *hd, u32 index, const char **out_str) {
|
||||
if (!hd || !out_str) {
|
||||
Result hbc_get_string(HBC *hbc, u32 index, const char **out_str) {
|
||||
if (!hbc || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
if (index >= hd->reader.header.stringCount) {
|
||||
if (index >= hbc->reader.header.stringCount) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "String ID out of range");
|
||||
}
|
||||
*out_str = hd->reader.strings[index];
|
||||
*out_str = hbc->reader.strings[index];
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_get_string_meta(HBCState *hd, u32 index, HBCStringMeta *out) {
|
||||
if (!hd || !out) {
|
||||
Result hbc_get_string_meta(HBC *hbc, u32 index, HBCStringMeta *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
HBCReader *r = &hd->reader;
|
||||
HBCReader *r = &hbc->reader;
|
||||
if (index >= r->header.stringCount) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "String ID out of range");
|
||||
}
|
||||
@@ -224,6 +224,14 @@ Result hbc_get_string_meta(HBCState *hd, u32 index, HBCStringMeta *out) {
|
||||
|
||||
if (length == 0xFF) {
|
||||
u32 oi = off;
|
||||
/* Bounds check: ensure oi is within overflow_string_table bounds */
|
||||
if (oi >= r->header.overflowStringCount) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Overflow string index out of bounds");
|
||||
}
|
||||
/* Additional safety check: ensure overflow_string_table exists */
|
||||
if (!r->overflow_string_table) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Overflow string table not allocated");
|
||||
}
|
||||
off = r->overflow_string_table[oi].offset;
|
||||
length = r->overflow_string_table[oi].length;
|
||||
}
|
||||
@@ -235,65 +243,57 @@ Result hbc_get_string_meta(HBCState *hd, u32 index, HBCStringMeta *out) {
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_get_string_tables(HBCState *hd, HBCStrs *out) {
|
||||
if (!hd || !out) {
|
||||
Result hbc_get_string_tables(HBC *hbc, HBCStrs *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
out->string_count = hd->reader.header.stringCount;
|
||||
out->small_string_table = hd->reader.small_string_table;
|
||||
out->overflow_string_table = hd->reader.overflow_string_table;
|
||||
out->string_storage_offset = hd->reader.string_storage_file_offset;
|
||||
out->string_count = hbc->reader.header.stringCount;
|
||||
out->small_string_table = hbc->reader.small_string_table;
|
||||
out->overflow_string_table = hbc->reader.overflow_string_table;
|
||||
out->string_storage_offset = hbc->reader.string_storage_file_offset;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_get_function_source(HBCState *hd, u32 function_id, const char **out_src) {
|
||||
if (!hd || !out_src) {
|
||||
Result hbc_get_function_source(HBC *hbc, u32 function_id, const char **out_src) {
|
||||
if (!hbc || !out_src) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
if (function_id >= hd->reader.header.functionCount) {
|
||||
if (function_id >= hbc->reader.header.functionCount) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Function ID out of range");
|
||||
}
|
||||
*out_src = NULL;
|
||||
if (hd->reader.function_sources && function_id < hd->reader.header.functionSourceCount) {
|
||||
u32 string_id = hd->reader.function_sources[function_id].string_id;
|
||||
if (string_id < hd->reader.header.stringCount) {
|
||||
*out_src = hd->reader.strings[string_id];
|
||||
if (hbc->reader.function_sources && function_id < hbc->reader.header.functionSourceCount) {
|
||||
u32 string_id = hbc->reader.function_sources[function_id].string_id;
|
||||
if (string_id < hbc->reader.header.stringCount) {
|
||||
*out_src = hbc->reader.strings[string_id];
|
||||
}
|
||||
}
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_get_function_bytecode(HBCState *hd, u32 function_id, const u8 **out_ptr, u32 *out_size) {
|
||||
if (!hd || !out_ptr || !out_size) {
|
||||
Result hbc_get_function_bytecode(HBC *hbc, u32 function_id, const u8 **out_ptr, u32 *out_size) {
|
||||
if (!hbc || !out_ptr || !out_size) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
if (function_id >= hd->reader.header.functionCount) {
|
||||
if (function_id >= hbc->reader.header.functionCount) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Function ID out of range");
|
||||
}
|
||||
const FunctionHeader *fh = &hd->reader.function_headers[function_id];
|
||||
*out_ptr = hd->reader.file_buffer.data + fh->offset;
|
||||
const FunctionHeader *fh = &hbc->reader.function_headers[function_id];
|
||||
*out_ptr = hbc->reader.file_buffer.data + fh->offset;
|
||||
*out_size = fh->bytecodeSizeInBytes;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* CLI Convenience Functions
|
||||
* ============================================================================ */
|
||||
|
||||
Result hbc_decompile_file(const char *input_file, const char *output_file) {
|
||||
return _hbc_decompile_file (input_file, output_file);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* HBC Decompilation API (Primary Public Interface)
|
||||
* ============================================================================ */
|
||||
|
||||
Result hbc_decomp_fn(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
u32 function_id,
|
||||
HBCDecompOptions options,
|
||||
char **out_str) {
|
||||
if (!provider || !out_str) {
|
||||
if (!hbc || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
@@ -303,21 +303,21 @@ Result hbc_decomp_fn(
|
||||
return res;
|
||||
}
|
||||
|
||||
res = _hbc_decompile_function_with_provider (provider, function_id, options, &sb);
|
||||
res = _hbc_decompile_function_with_state (hbc, function_id, options, &sb);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
_hbc_string_buffer_free (&sb);
|
||||
return res;
|
||||
}
|
||||
|
||||
*out_str = sb.data; /* Transfer ownership to caller */
|
||||
*out_str = sb.data;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_decomp_all(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
HBCDecompOptions options,
|
||||
char **out_str) {
|
||||
if (!provider || !out_str) {
|
||||
if (!hbc || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
@@ -327,57 +327,52 @@ Result hbc_decomp_all(
|
||||
return res;
|
||||
}
|
||||
|
||||
res = _hbc_decompile_all_with_provider (provider, options, &sb);
|
||||
res = _hbc_decompile_all_with_state (hbc, options, &sb);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
_hbc_string_buffer_free (&sb);
|
||||
return res;
|
||||
}
|
||||
|
||||
*out_str = sb.data; /* Transfer ownership to caller */
|
||||
*out_str = sb.data;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_disasm_fn(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
u32 function_id,
|
||||
HBCDisOptions options,
|
||||
char **out_str) {
|
||||
(void)function_id;
|
||||
(void)options;
|
||||
|
||||
if (!provider || !out_str) {
|
||||
if (!hbc || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED, "Disassembly via provider not yet implemented");
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED, "Disassembly via state not yet implemented");
|
||||
}
|
||||
|
||||
Result hbc_disasm_all(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
HBCDisOptions options,
|
||||
char **out_str) {
|
||||
(void)options;
|
||||
|
||||
if (!provider || !out_str) {
|
||||
if (!hbc || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED, "Disassembly via provider not yet implemented");
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED, "Disassembly via state not yet implemented");
|
||||
}
|
||||
|
||||
Result hbc_all_funcs(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
HBCFuncArray *out) {
|
||||
if (!provider || !out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
Result res = hbc_func_count (provider, &count);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
u32 count = hbc_function_count (hbc);
|
||||
if (count == 0) {
|
||||
out->functions = NULL;
|
||||
out->count = 0;
|
||||
@@ -390,7 +385,7 @@ Result hbc_all_funcs(
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
res = hbc_func_info (provider, i, &out->functions[i]);
|
||||
Result res = hbc_get_function_info (hbc, i, &out->functions[i]);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
free (out->functions);
|
||||
out->functions = NULL;
|
||||
@@ -412,29 +407,29 @@ void hbc_free_funcs(HBCFuncArray *arr) {
|
||||
}
|
||||
|
||||
Result hbc_decode_fn(
|
||||
HBC *provider,
|
||||
HBC *hbc,
|
||||
u32 function_id,
|
||||
HBCInsns *out) {
|
||||
if (!provider || !out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
const u8 *bytecode = NULL;
|
||||
u32 bytecode_size = 0;
|
||||
Result res = hbc_bytecode (provider, function_id, &bytecode, &bytecode_size);
|
||||
Result res = hbc_get_function_bytecode (hbc, function_id, &bytecode, &bytecode_size);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
HBCStrs string_tables = { 0 };
|
||||
res = hbc_str_tbl (provider, &string_tables);
|
||||
res = hbc_get_string_tables (hbc, &string_tables);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
out->instructions = NULL;
|
||||
out->count = 0;
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED, "Instruction decoding via provider not yet implemented");
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_IMPLEMENTED, "Instruction decoding via state not yet implemented");
|
||||
}
|
||||
|
||||
/* Single-instruction decode functions */
|
||||
@@ -458,6 +453,7 @@ Result hbc_dec(const HBCDecodeCtx *ctx, HBCInsnInfo *out) {
|
||||
ctx->asm_syntax,
|
||||
ctx->resolve_string_ids,
|
||||
ctx->string_tables,
|
||||
ctx->hbc,
|
||||
out);
|
||||
}
|
||||
|
||||
@@ -469,6 +465,7 @@ Result hbc_dec_insn(
|
||||
bool asm_syntax,
|
||||
bool resolve_string_ids,
|
||||
const HBCStrs *string_ctx,
|
||||
HBC *hbc,
|
||||
HBCInsnInfo *out) {
|
||||
|
||||
if (!bytes || !out || len == 0) {
|
||||
@@ -613,9 +610,6 @@ Result hbc_dec_insn(
|
||||
/* Output the file-absolute string offset in hex - r2 will replace this with the string flag */
|
||||
u32 file_offset = string_ctx->string_storage_offset + str_offset;
|
||||
offset += snprintf (buf + offset, sizeof (buf) - offset, "0x%x", file_offset);
|
||||
/* Append full virtual address as comment */
|
||||
u32 str_addr = 0x10000000 + file_offset;
|
||||
offset += snprintf (buf + offset, sizeof (buf) - offset, " ; 0x%x", str_addr);
|
||||
} else {
|
||||
/* Fallback: just show the ID */
|
||||
offset += snprintf (buf + offset, sizeof (buf) - offset, "0x%x", val);
|
||||
@@ -637,7 +631,41 @@ Result hbc_dec_insn(
|
||||
}
|
||||
}
|
||||
|
||||
out->text = strdup (buf);
|
||||
char *final_text = NULL;
|
||||
if (hbc && (opcode == OP_NewObjectWithBuffer || opcode == OP_NewObjectWithBufferLong ||
|
||||
opcode == OP_NewArrayWithBuffer || opcode == OP_NewArrayWithBufferLong)) {
|
||||
HBCReader *reader = &hbc->reader;
|
||||
StringBuffer sb;
|
||||
if (_hbc_string_buffer_init (&sb, 256).code == RESULT_SUCCESS) {
|
||||
Result fmt_res = { .code = RESULT_ERROR_PARSING_FAILED };
|
||||
if (opcode == OP_NewObjectWithBuffer || opcode == OP_NewObjectWithBufferLong) {
|
||||
u32 key_count = operand_values[1];
|
||||
u32 value_count = operand_values[2];
|
||||
u32 keys_id = operand_values[3];
|
||||
u32 values_id = operand_values[4];
|
||||
fmt_res = _hbc_format_object_literal (reader, key_count, value_count, keys_id, values_id,
|
||||
&sb, LITERALS_PRETTY_NEVER, true);
|
||||
} else {
|
||||
u32 value_count = operand_values[2];
|
||||
u32 array_id = operand_values[3];
|
||||
fmt_res = _hbc_format_array_literal (reader, value_count, array_id,
|
||||
&sb, LITERALS_PRETTY_NEVER, true);
|
||||
}
|
||||
if (fmt_res.code == RESULT_SUCCESS && sb.data && sb.data[0]) {
|
||||
size_t buf_len = strlen (buf);
|
||||
size_t sb_len = strlen (sb.data);
|
||||
final_text = (char *)malloc (buf_len + 4 + sb_len + 1);
|
||||
if (final_text) {
|
||||
memcpy (final_text, buf, buf_len);
|
||||
memcpy (final_text + buf_len, " ; ", 4);
|
||||
memcpy (final_text + buf_len + 4, sb.data, sb_len + 1);
|
||||
}
|
||||
}
|
||||
_hbc_string_buffer_free (&sb);
|
||||
}
|
||||
}
|
||||
|
||||
out->text = final_text ? final_text : strdup (buf);
|
||||
out->size = (u32)pos;
|
||||
out->opcode = opcode;
|
||||
|
||||
|
||||
+10
-13
@@ -1,19 +1,16 @@
|
||||
#ifndef SRC_LIB_HBC_INTERNAL_H
|
||||
#define SRC_LIB_HBC_INTERNAL_H
|
||||
#ifndef HBC_INTERNAL_H
|
||||
#define HBC_INTERNAL_H
|
||||
|
||||
#include <hbc/hbc.h>
|
||||
#include <hbc/parser.h>
|
||||
|
||||
/* Internal full definition of HBCState (kept in a private header).
|
||||
* The public header exposes HBCState as an opaque typedef. */
|
||||
struct HBCState {
|
||||
HBCReader reader; // Internal parser state
|
||||
HBCHeader header; // File header
|
||||
u32 version; // Bytecode version (from header)
|
||||
u32 string_count; // Number of strings in constant pool
|
||||
const char **strings; // Array of string pointers (constant string pool)
|
||||
u32 function_count; // Number of functions
|
||||
// Add more fields as needed: function info, bytecode, bigints, regex, etc.
|
||||
struct HBC {
|
||||
HBCReader reader;
|
||||
HBCHeader header;
|
||||
u32 version;
|
||||
u32 string_count;
|
||||
const char **strings;
|
||||
u32 function_count;
|
||||
};
|
||||
|
||||
#endif /* SRC_LIB_HBC_INTERNAL_H */
|
||||
#endif /* HBC_INTERNAL_H */
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef HBC_INTERNAL_LEGACY_H
|
||||
#define HBC_INTERNAL_LEGACY_H
|
||||
|
||||
#include <hbc/hbc.h>
|
||||
|
||||
/* ============================================================================
|
||||
* INTERNAL LEGACY API - HBCState (For Data Provider Implementations Only)
|
||||
*
|
||||
* These functions are internal only. They exist to support the data provider
|
||||
* implementations that use HBCState internally. Do NOT use these in new code.
|
||||
* ============================================================================ */
|
||||
|
||||
typedef struct HBCState HBCState;
|
||||
|
||||
/* Opaque state handle (internal use only) */
|
||||
Result hbc_open(const char *path, HBCState **out);
|
||||
Result hbc_open_from_memory(const u8 *data, size_t size, HBCState **out);
|
||||
void hbc_close(HBCState *hd);
|
||||
|
||||
u32 hbc_function_count(HBCState *hd);
|
||||
u32 hbc_string_count(HBCState *hd);
|
||||
|
||||
Result hbc_get_header(HBCState *hd, HBCHeader *out);
|
||||
Result hbc_get_function_info(HBCState *hd, u32 function_id, HBCFunc *out);
|
||||
Result hbc_get_string(HBCState *hd, u32 index, const char **out_str);
|
||||
Result hbc_get_string_meta(HBCState *hd, u32 index, HBCStringMeta *out);
|
||||
Result hbc_get_string_tables(HBCState *hd, HBCStrs *out);
|
||||
Result hbc_get_function_source(HBCState *hd, u32 function_id, const char **out_str);
|
||||
Result hbc_get_function_bytecode(HBCState *hd, u32 function_id, const u8 **out_ptr, u32 *out_size);
|
||||
|
||||
#endif /* HBC_INTERNAL_LEGACY_H */
|
||||
@@ -187,7 +187,13 @@ static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int id
|
||||
if (v < r->header.stringCount && r->small_string_table) {
|
||||
if (r->small_string_table[v].length == 0xFF && r->overflow_string_table) {
|
||||
u32 oi = r->small_string_table[v].offset;
|
||||
off = r->overflow_string_table[oi].offset;
|
||||
/* Bounds check: ensure oi is within overflow_string_table bounds */
|
||||
if (oi < r->header.overflowStringCount) {
|
||||
off = r->overflow_string_table[oi].offset;
|
||||
} else {
|
||||
/* Fallback: use original offset if overflow index is invalid */
|
||||
off = r->small_string_table[v].offset;
|
||||
}
|
||||
} else {
|
||||
off = r->small_string_table[v].offset;
|
||||
}
|
||||
|
||||
+104
-130
@@ -756,6 +756,7 @@ Result _hbc_reader_read_string_tables(HBCReader *reader) {
|
||||
|
||||
/* Parse the entry based on version */
|
||||
if (reader->header.version >= 56) {
|
||||
// up to v96 at least
|
||||
reader->small_string_table[i].isUTF16 = entry & 0x1;
|
||||
reader->small_string_table[i].offset = (entry >> 1) & 0x7FFFFF; /* 23 bits */
|
||||
reader->small_string_table[i].length = (entry >> 24) & 0xFF; /* 8 bits */
|
||||
@@ -793,12 +794,24 @@ Result _hbc_reader_read_string_tables(HBCReader *reader) {
|
||||
reader->string_storage_file_offset = (u32)reader->file_buffer.position;
|
||||
|
||||
/* Read the string storage */
|
||||
u8 *string_storage = (u8 *)malloc (reader->header.stringStorageSize);
|
||||
size_t string_storage_size = reader->header.stringStorageSize;
|
||||
size_t remaining = reader->file_buffer.size - reader->file_buffer.position;
|
||||
if (remaining < string_storage_size) {
|
||||
string_storage_size = remaining;
|
||||
} else if (remaining > string_storage_size) {
|
||||
/* If more remaining, perhaps the header size is wrong, use remaining if debug info offset is 0 */
|
||||
if (reader->header.debugInfoOffset == 0) {
|
||||
string_storage_size = remaining;
|
||||
}
|
||||
}
|
||||
u8 *string_storage = (u8 *)malloc (string_storage_size);
|
||||
if (!string_storage) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate string storage");
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR (_hbc_buffer_reader_read_bytes (&reader->file_buffer, string_storage, reader->header.stringStorageSize));
|
||||
RETURN_IF_ERROR (_hbc_buffer_reader_read_bytes (&reader->file_buffer, string_storage, string_storage_size));
|
||||
reader->string_table_storage = string_storage;
|
||||
reader->string_table_storage_size = string_storage_size;
|
||||
|
||||
/* Allocate string array */
|
||||
reader->strings = (char **)calloc (reader->header.stringCount, sizeof (char *));
|
||||
@@ -816,6 +829,15 @@ Result _hbc_reader_read_string_tables(HBCReader *reader) {
|
||||
if (reader->small_string_table[i].length == 0xFF) {
|
||||
/* This is an overflow string */
|
||||
u32 overflow_index = reader->small_string_table[i].offset;
|
||||
if (overflow_index >= reader->header.overflowStringCount) {
|
||||
/* Invalid overflow index */
|
||||
reader->strings[i] = strdup("");
|
||||
if (!reader->strings[i]) {
|
||||
free (string_storage);
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate empty string");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
offset = reader->overflow_string_table[overflow_index].offset;
|
||||
length = reader->overflow_string_table[overflow_index].length;
|
||||
is_utf16 = reader->small_string_table[i].isUTF16;
|
||||
@@ -827,9 +849,14 @@ Result _hbc_reader_read_string_tables(HBCReader *reader) {
|
||||
}
|
||||
|
||||
/* Check bounds */
|
||||
if (offset + (is_utf16? length * 2: length) > reader->header.stringStorageSize) {
|
||||
free (string_storage);
|
||||
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "String offset/length out of bounds");
|
||||
if (offset + (is_utf16? length * 2: length) > string_storage_size) {
|
||||
fprintf (stderr, "Warning: String %u offset/length out of bounds, treating as empty string\n", i);
|
||||
reader->strings[i] = strdup("");
|
||||
if (!reader->strings[i]) {
|
||||
free (string_storage);
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate empty string");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Allocate string buffer - for UTF-16, length is in characters, so allocate 2 bytes per character */
|
||||
@@ -1159,7 +1186,6 @@ Result _hbc_reader_read_bigints(HBCReader *reader) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
value |= ((i64)bigint_storage[offset + j]) << (j * 8);
|
||||
}
|
||||
|
||||
reader->bigint_values[i] = value;
|
||||
}
|
||||
|
||||
@@ -1276,7 +1302,6 @@ Result _hbc_reader_read_regexp(HBCReader *reader) {
|
||||
hbc_debug_printf ("Warning: Not enough data for RegExp storage (need %u bytes)\n",
|
||||
reader->header.regExpStorageSize);
|
||||
reader->header.regExpStorageSize = reader->file_buffer.size - reader->file_buffer.position;
|
||||
|
||||
if (reader->header.regExpStorageSize <= 0) {
|
||||
reader->header.regExpStorageSize = 0;
|
||||
return SUCCESS_RESULT ();
|
||||
@@ -1514,17 +1539,8 @@ Result _hbc_reader_read_functions_robust(HBCReader *reader) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader is NULL");
|
||||
}
|
||||
|
||||
/* Set reasonable limits */
|
||||
const u32 MAX_SAFE_FUNCTIONS = 50000;
|
||||
u32 max_functions_to_read = reader->header.functionCount;
|
||||
|
||||
if (max_functions_to_read > MAX_SAFE_FUNCTIONS) {
|
||||
hbc_debug_printf ("Warning: Very large function count (%u). Limiting to %u for safety.\n",
|
||||
reader->header.functionCount,
|
||||
MAX_SAFE_FUNCTIONS);
|
||||
max_functions_to_read = MAX_SAFE_FUNCTIONS;
|
||||
}
|
||||
|
||||
/* Log position info */
|
||||
hbc_debug_printf ("Reading functions at position %zu of %zu bytes.\n",
|
||||
reader->file_buffer.position,
|
||||
@@ -1618,129 +1634,87 @@ Result _hbc_reader_read_functions_robust(HBCReader *reader) {
|
||||
FunctionHeader *header = &reader->function_headers[i];
|
||||
|
||||
/* Extract fields from raw data (small header) */
|
||||
u32 small_offset = raw_data[0] & 0x1FFFFFF; /* 25 bits */
|
||||
u32 small_paramCount = (raw_data[0] >> 25) & 0x7F; /* 7 bits */
|
||||
const u32 small_offset = raw_data[0] & 0x1FFFFFF; /* 25 bits */
|
||||
const u32 small_paramCount = (raw_data[0] >> 25) & 0x7F; /* 7 bits */
|
||||
const u32 small_bytecodeSizeInBytes = raw_data[1] & 0x7FFF; /* 15 bits */
|
||||
const u32 small_functionName = (raw_data[1] >> 15) & 0x1FFFF; /* 17 bits */
|
||||
const u32 small_infoOffset = raw_data[2] & 0x1FFFFFF; /* 25 bits */
|
||||
const u32 small_frameSize = (raw_data[2] >> 25) & 0x7F; /* 7 bits */
|
||||
const u8 small_environmentSize = (u8) (raw_data[3] & 0xFF);
|
||||
const u8 small_highestReadCacheIndex = (u8) ((raw_data[3] >> 8) & 0xFF);
|
||||
const u8 small_highestWriteCacheIndex = (u8) ((raw_data[3] >> 16) & 0xFF);
|
||||
const u8 small_prohibitInvoke = (raw_data[3] >> 24) & 0x3;
|
||||
const u8 small_strictMode = (raw_data[3] >> 26) & 0x1;
|
||||
const u8 small_hasExceptionHandler = (raw_data[3] >> 27) & 0x1;
|
||||
const u8 small_hasDebugInfo = (raw_data[3] >> 28) & 0x1;
|
||||
const u8 small_overflowed = (raw_data[3] >> 29) & 0x1;
|
||||
const u8 small_unused = (raw_data[3] >> 30) & 0x3;
|
||||
|
||||
u32 small_bytecodeSizeInBytes = raw_data[1] & 0x7FFF; /* 15 bits */
|
||||
u32 small_functionName = (raw_data[1] >> 15) & 0x1FFFF; /* 17 bits */
|
||||
/* Initialize header with small header values */
|
||||
header->offset = small_offset;
|
||||
header->paramCount = small_paramCount;
|
||||
header->bytecodeSizeInBytes = small_bytecodeSizeInBytes;
|
||||
header->functionName = small_functionName;
|
||||
header->infoOffset = small_infoOffset;
|
||||
header->frameSize = small_frameSize;
|
||||
header->environmentSize = small_environmentSize;
|
||||
header->highestReadCacheIndex = small_highestReadCacheIndex;
|
||||
header->highestWriteCacheIndex = small_highestWriteCacheIndex;
|
||||
header->prohibitInvoke = small_prohibitInvoke;
|
||||
header->strictMode = small_strictMode;
|
||||
header->hasExceptionHandler = small_hasExceptionHandler;
|
||||
header->hasDebugInfo = small_hasDebugInfo;
|
||||
header->overflowed = small_overflowed;
|
||||
header->unused = small_unused;
|
||||
|
||||
u32 small_infoOffset = raw_data[2] & 0x1FFFFFF; /* 25 bits */
|
||||
u32 small_frameSize = (raw_data[2] >> 25) & 0x7F; /* 7 bits */
|
||||
|
||||
u8 small_environmentSize = (u8) (raw_data[3] & 0xFF);
|
||||
u8 small_highestReadCacheIndex = (u8) ((raw_data[3] >> 8) & 0xFF);
|
||||
u8 small_highestWriteCacheIndex = (u8) ((raw_data[3] >> 16) & 0xFF);
|
||||
|
||||
u8 small_prohibitInvoke = (raw_data[3] >> 24) & 0x3;
|
||||
u8 small_strictMode = (raw_data[3] >> 26) & 0x1;
|
||||
u8 small_hasExceptionHandler = (raw_data[3] >> 27) & 0x1;
|
||||
u8 small_hasDebugInfo = (raw_data[3] >> 28) & 0x1;
|
||||
u8 small_overflowed = (raw_data[3] >> 29) & 0x1;
|
||||
u8 small_unused = (raw_data[3] >> 30) & 0x3;
|
||||
|
||||
/* If the small header overflowed OR looks truncated (e.g. size==0), load the large header as Python does */
|
||||
/* Try to read large header if needed and override values */
|
||||
if (small_overflowed || small_bytecodeSizeInBytes == 0) {
|
||||
/* Calculate absolute offset of the large header */
|
||||
u32 large_header_offset = (small_infoOffset << 16) | small_offset;
|
||||
const u32 large_header_offset = (small_infoOffset << 16) | small_offset;
|
||||
const Result sr = _hbc_buffer_reader_seek (&reader->file_buffer, large_header_offset);
|
||||
|
||||
/* Seek to large header */
|
||||
Result sr = _hbc_buffer_reader_seek (&reader->file_buffer, large_header_offset);
|
||||
if (sr.code == RESULT_SUCCESS) {
|
||||
/* Read large function header fields explicitly */
|
||||
LargeFunctionHeader large_header = { 0 };
|
||||
|
||||
/* Read in the same order as in the non-robust path */
|
||||
if (_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.offset).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.paramCount).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.bytecodeSizeInBytes).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.functionName).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.infoOffset).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.frameSize).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, &large_header.environmentSize).code == RESULT_SUCCESS) {
|
||||
/* Read tail bytes */
|
||||
_hbc_buffer_reader_read_u8 (&reader->file_buffer, &large_header.highestReadCacheIndex);
|
||||
_hbc_buffer_reader_read_u8 (&reader->file_buffer, &large_header.highestWriteCacheIndex);
|
||||
u8 flags = 0;
|
||||
_hbc_buffer_reader_read_u8 (&reader->file_buffer, &flags);
|
||||
large_header.prohibitInvoke = flags & 0x3;
|
||||
large_header.strictMode = (flags >> 2) & 0x1;
|
||||
large_header.hasExceptionHandler = (flags >> 3) & 0x1;
|
||||
large_header.hasDebugInfo = (flags >> 4) & 0x1;
|
||||
large_header.overflowed = (flags >> 5) & 0x1;
|
||||
large_header.unused = (flags >> 6) & 0x3;
|
||||
if (_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.offset).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.paramCount).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.bytecodeSizeInBytes).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.functionName).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.infoOffset).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.frameSize).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u32 (&reader->file_buffer, (u32 *)&large_header.environmentSize).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u8 (&reader->file_buffer, &large_header.highestReadCacheIndex).code == RESULT_SUCCESS &&
|
||||
_hbc_buffer_reader_read_u8 (&reader->file_buffer, &large_header.highestWriteCacheIndex).code == RESULT_SUCCESS) {
|
||||
|
||||
/* Copy into combined header */
|
||||
header->offset = large_header.offset;
|
||||
header->paramCount = large_header.paramCount;
|
||||
header->bytecodeSizeInBytes = large_header.bytecodeSizeInBytes;
|
||||
header->functionName = large_header.functionName;
|
||||
header->infoOffset = large_header.infoOffset;
|
||||
header->frameSize = large_header.frameSize;
|
||||
header->environmentSize = (u8)large_header.environmentSize;
|
||||
header->highestReadCacheIndex = large_header.highestReadCacheIndex;
|
||||
header->highestWriteCacheIndex = large_header.highestWriteCacheIndex;
|
||||
header->prohibitInvoke = large_header.prohibitInvoke;
|
||||
header->strictMode = large_header.strictMode;
|
||||
header->hasExceptionHandler = large_header.hasExceptionHandler;
|
||||
header->hasDebugInfo = large_header.hasDebugInfo;
|
||||
header->overflowed = large_header.overflowed;
|
||||
header->unused = large_header.unused;
|
||||
|
||||
/* Restore to next small header position */
|
||||
_hbc_buffer_reader_seek (&reader->file_buffer, start_position + 16);
|
||||
} else {
|
||||
/* Fallback to small header values on read error */
|
||||
header->offset = small_offset;
|
||||
header->paramCount = small_paramCount;
|
||||
header->bytecodeSizeInBytes = small_bytecodeSizeInBytes;
|
||||
header->functionName = small_functionName;
|
||||
header->infoOffset = small_infoOffset;
|
||||
header->frameSize = small_frameSize;
|
||||
header->environmentSize = small_environmentSize;
|
||||
header->highestReadCacheIndex = small_highestReadCacheIndex;
|
||||
header->highestWriteCacheIndex = small_highestWriteCacheIndex;
|
||||
header->prohibitInvoke = small_prohibitInvoke;
|
||||
header->strictMode = small_strictMode;
|
||||
header->hasExceptionHandler = small_hasExceptionHandler;
|
||||
header->hasDebugInfo = small_hasDebugInfo;
|
||||
header->overflowed = small_overflowed;
|
||||
header->unused = small_unused;
|
||||
u8 flags;
|
||||
if (_hbc_buffer_reader_read_u8 (&reader->file_buffer, &flags).code == RESULT_SUCCESS) {
|
||||
/* Override with large header values */
|
||||
header->offset = large_header.offset;
|
||||
header->paramCount = large_header.paramCount;
|
||||
header->bytecodeSizeInBytes = large_header.bytecodeSizeInBytes;
|
||||
header->functionName = large_header.functionName;
|
||||
header->infoOffset = large_header.infoOffset;
|
||||
header->frameSize = large_header.frameSize;
|
||||
header->environmentSize = (u8)large_header.environmentSize;
|
||||
header->highestReadCacheIndex = large_header.highestReadCacheIndex;
|
||||
header->highestWriteCacheIndex = large_header.highestWriteCacheIndex;
|
||||
header->prohibitInvoke = flags & 0x3;
|
||||
header->strictMode = (flags >> 2) & 0x1;
|
||||
header->hasExceptionHandler = (flags >> 3) & 0x1;
|
||||
header->hasDebugInfo = (flags >> 4) & 0x1;
|
||||
header->overflowed = (flags >> 5) & 0x1;
|
||||
header->unused = (flags >> 6) & 0x3;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Fallback to small header values if we can't seek */
|
||||
header->offset = small_offset;
|
||||
header->paramCount = small_paramCount;
|
||||
header->bytecodeSizeInBytes = small_bytecodeSizeInBytes;
|
||||
header->functionName = small_functionName;
|
||||
header->infoOffset = small_infoOffset;
|
||||
header->frameSize = small_frameSize;
|
||||
header->environmentSize = small_environmentSize;
|
||||
header->highestReadCacheIndex = small_highestReadCacheIndex;
|
||||
header->highestWriteCacheIndex = small_highestWriteCacheIndex;
|
||||
header->prohibitInvoke = small_prohibitInvoke;
|
||||
header->strictMode = small_strictMode;
|
||||
header->hasExceptionHandler = small_hasExceptionHandler;
|
||||
header->hasDebugInfo = small_hasDebugInfo;
|
||||
header->overflowed = small_overflowed;
|
||||
header->unused = small_unused;
|
||||
}
|
||||
} else {
|
||||
/* No overflow: use small header values directly */
|
||||
header->offset = small_offset;
|
||||
header->paramCount = small_paramCount;
|
||||
header->bytecodeSizeInBytes = small_bytecodeSizeInBytes;
|
||||
header->functionName = small_functionName;
|
||||
header->infoOffset = small_infoOffset;
|
||||
header->frameSize = small_frameSize;
|
||||
header->environmentSize = small_environmentSize;
|
||||
header->highestReadCacheIndex = small_highestReadCacheIndex;
|
||||
header->highestWriteCacheIndex = small_highestWriteCacheIndex;
|
||||
header->prohibitInvoke = small_prohibitInvoke;
|
||||
header->strictMode = small_strictMode;
|
||||
header->hasExceptionHandler = small_hasExceptionHandler;
|
||||
header->hasDebugInfo = small_hasDebugInfo;
|
||||
header->overflowed = small_overflowed;
|
||||
header->unused = small_unused;
|
||||
}
|
||||
|
||||
/* Always restore to next small header position */
|
||||
Result seek_result = _hbc_buffer_reader_seek (&reader->file_buffer, start_position + 16);
|
||||
if (seek_result.code != RESULT_SUCCESS) {
|
||||
hbc_debug_printf ("Error seeking to next function header after %u: %s\n",
|
||||
i,
|
||||
seek_result.error_message);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Validation - check for unreasonable values */
|
||||
@@ -1763,7 +1737,7 @@ Result _hbc_reader_read_functions_robust(HBCReader *reader) {
|
||||
successful_functions++;
|
||||
|
||||
/* Ensure position is at next small header (16 bytes per entry) */
|
||||
Result seek_result = _hbc_buffer_reader_seek (&reader->file_buffer, start_position + 16);
|
||||
seek_result = _hbc_buffer_reader_seek (&reader->file_buffer, start_position + 16);
|
||||
if (seek_result.code != RESULT_SUCCESS) {
|
||||
hbc_debug_printf ("Error seeking to next function header after %u: %s\n",
|
||||
i,
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include <hbc/parser.h>
|
||||
|
||||
/* Generate r2 script with function flags - robust version inspired by the Python implementation */
|
||||
Result hbc_generate_r2_script(const char *input_file, const char *output_file) {
|
||||
Result _hbc_generate_r2_script(const char *input_file, const char *output_file) {
|
||||
if (!input_file) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Input file is NULL");
|
||||
}
|
||||
|
||||
+32
-7
@@ -32,32 +32,49 @@ ARCH_SRC = arch_hbc.c
|
||||
BIN_SRC = bin_hbc.c
|
||||
ASM_SRC = asm_hbc.c
|
||||
CORE_SRC = core_hbc.c
|
||||
R2_PROVIDER_SRC = data_provider_r2.c
|
||||
CORE_ONE_SRC = core_hbc_one.c
|
||||
|
||||
# Object files
|
||||
ARCH_OBJ = arch_hbc.o
|
||||
BIN_OBJ = bin_hbc.o
|
||||
ASM_OBJ = asm_hbc.o
|
||||
CORE_OBJ = core_hbc.o
|
||||
R2_PROVIDER_OBJ = data_provider_r2.o
|
||||
CORE_ONE_OBJ = core_hbc_one.o
|
||||
|
||||
# Target plugins
|
||||
ARCH_PLUGIN = arch_hbc.$(EXT_SO)
|
||||
BIN_PLUGIN = bin_hbc.$(EXT_SO)
|
||||
ASM_PLUGIN = asm_hbc.$(EXT_SO)
|
||||
CORE_PLUGIN = core_hbc.$(EXT_SO)
|
||||
CORE_ONE_PLUGIN = core_hbc_one.$(EXT_SO)
|
||||
|
||||
# Library paths
|
||||
HERMES_LIB_DIR = ../../build
|
||||
HERMES_LIB = $(HERMES_LIB_DIR)/libhbc.a
|
||||
|
||||
|
||||
# All targets
|
||||
ALL_TARGETS = $(ARCH_PLUGIN) $(BIN_PLUGIN) $(ASM_PLUGIN) $(CORE_PLUGIN)
|
||||
# $(CORE_ONE_PLUGIN)
|
||||
|
||||
.PHONY: all clean install help check-r2
|
||||
.PHONY: all clean install help check-r2 r2one
|
||||
|
||||
all: check-deps $(ALL_TARGETS)
|
||||
|
||||
# Build core plugin with plugin registration enabled (r2one)
|
||||
r2one: check-deps
|
||||
# Compile core plugin with registration enabled
|
||||
$(CC) $(CFLAGS) -DHBC_CORE_REGISTER_PLUGINS -c $(CORE_SRC) -o $(CORE_OBJ)
|
||||
# Compile other plugins without radare_plugin (they'll be registered by core)
|
||||
$(CC) $(CFLAGS) -DR2_PLUGIN_INCORE -c $(ARCH_SRC) -o $(ARCH_OBJ)
|
||||
$(CC) $(CFLAGS) -DR2_PLUGIN_INCORE -c $(ASM_SRC) -o $(ASM_OBJ)
|
||||
$(CC) $(CFLAGS) -DR2_PLUGIN_INCORE -c $(BIN_SRC) -o $(BIN_OBJ)
|
||||
$(CC) $(LDFLAGS) -o $(CORE_PLUGIN) $(CORE_OBJ) $(ARCH_OBJ) $(ASM_OBJ) $(BIN_OBJ) $(HERMES_LIB)
|
||||
@echo "Plugin file: $(CORE_PLUGIN)"
|
||||
rm -f ~/.local/share/radare2/plugins/*hbc*
|
||||
cp -f core_hbc.dylib ~/.local/share/radare2/plugins
|
||||
r2 -q -c 'Lc~hbc' -c 'La~hbc' -c 'Lb~hbc' --
|
||||
|
||||
# Check if radare2 development headers are available
|
||||
check-deps: $(HERMES_LIB)
|
||||
@echo "Checking for radare2 development headers..."
|
||||
@@ -82,8 +99,12 @@ $(ASM_PLUGIN): $(ASM_OBJ)
|
||||
$(CC) $(LDFLAGS) -o $@ $(ASM_OBJ)
|
||||
|
||||
# Core decompiler plugin
|
||||
$(CORE_PLUGIN): $(CORE_OBJ) $(R2_PROVIDER_OBJ) $(HERMES_LIB)
|
||||
$(CC) $(LDFLAGS) -o $@ $(CORE_OBJ) $(R2_PROVIDER_OBJ) $(HERMES_LIB)
|
||||
$(CORE_PLUGIN): $(CORE_OBJ) $(HERMES_LIB)
|
||||
$(CC) $(LDFLAGS) -o $@ $(CORE_OBJ) $(HERMES_LIB)
|
||||
|
||||
# Core decompiler plugin with plugin registration enabled (r2one)
|
||||
$(CORE_ONE_PLUGIN): $(CORE_ONE_OBJ) $(HERMES_LIB)
|
||||
$(CC) $(LDFLAGS) -o $@ $(CORE_ONE_OBJ) $(HERMES_LIB)
|
||||
|
||||
# Compile object files
|
||||
$(ARCH_OBJ): $(ARCH_SRC)
|
||||
@@ -98,12 +119,13 @@ $(ASM_OBJ): $(ASM_SRC)
|
||||
$(CORE_OBJ): $(CORE_SRC)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(R2_PROVIDER_OBJ): $(R2_PROVIDER_SRC)
|
||||
$(CORE_ONE_OBJ): $(CORE_ONE_SRC)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
rm -f $(ARCH_OBJ) $(BIN_OBJ) $(ASM_OBJ) $(CORE_OBJ) $(R2_PROVIDER_OBJ) $(ALL_TARGETS)
|
||||
rm -f $(ARCH_OBJ) $(BIN_OBJ) $(ASM_OBJ) $(CORE_OBJ) $(CORE_ONE_OBJ) $(ALL_TARGETS)
|
||||
|
||||
# Install plugins (optional - requires radare2 installation)
|
||||
install: $(ALL_TARGETS)
|
||||
@@ -117,6 +139,7 @@ user-install:
|
||||
cp -f $(BIN_PLUGIN) $(R2_PLUGDIR)
|
||||
cp -f $(ASM_PLUGIN) $(R2_PLUGDIR)
|
||||
cp -f $(CORE_PLUGIN) $(R2_PLUGDIR)
|
||||
# cp -f $(CORE_ONE_PLUGIN) $(R2_PLUGDIR)
|
||||
|
||||
user-uninstall:
|
||||
rm -f $(R2_PLUGDIR)/$(ARCH_PLUGIN)
|
||||
@@ -128,6 +151,8 @@ user-uninstall:
|
||||
uninstall:
|
||||
rm -f $(R2_PREFIX)/lib/radare2/last/$(ARCH_PLUGIN)
|
||||
rm -f $(R2_PREFIX)/lib/radare2/last/$(BIN_PLUGIN)
|
||||
rm -f $(R2_PREFIX)/lib/radare2/last/$(ASM_PLUGIN)
|
||||
rm -f $(R2_PREFIX)/lib/radare2/last/$(CORE_PLUGIN)
|
||||
|
||||
# Check radare2 installation
|
||||
check-r2:
|
||||
|
||||
+29
-35
@@ -10,7 +10,7 @@
|
||||
|
||||
typedef struct {
|
||||
ut32 bytecode_version; /* cached from RBinInfo->cpu if available */
|
||||
HBC *hbc; /* Hermes data provider for query access */
|
||||
HBC *hbc; /* Hermes state for direct access */
|
||||
u32 string_count;
|
||||
const void *small_string_table;
|
||||
const void *overflow_string_table;
|
||||
@@ -58,35 +58,27 @@ static bool load_string_tables(HermesArchSession *hs, RArchSession *s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to open the file with hermesdec file provider (properly parses all tables) */
|
||||
if (hs->hbc) {
|
||||
hbc_free (hs->hbc);
|
||||
hs->hbc = NULL;
|
||||
}
|
||||
|
||||
hs->hbc = hbc_new_file (bi->file);
|
||||
if (!hs->hbc) {
|
||||
/* Open with HBC directly for better performance */
|
||||
Result res = hbc_open (bi->file, &hs->hbc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If we can, get the file header to determine exact bytecode version */
|
||||
HBCHeader hh;
|
||||
if (hbc_hdr (hs->hbc, &hh).code == RESULT_SUCCESS) {
|
||||
/* Cache version for instruction set selection */
|
||||
hs->bytecode_version = hh.version;
|
||||
res = hbc_get_header (hs->hbc, &hh);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
hs->bytecode_version = hh.version;
|
||||
|
||||
/* Get string count */
|
||||
u32 string_count;
|
||||
Result count_res = hbc_str_count (hs->hbc, &string_count);
|
||||
if (count_res.code == RESULT_SUCCESS) {
|
||||
hs->string_count = string_count;
|
||||
}
|
||||
hs->string_count = hbc_string_count (hs->hbc);
|
||||
|
||||
/* Extract string tables using the API */
|
||||
HBCStrs tables;
|
||||
Result table_res = hbc_str_tbl (hs->hbc, &tables);
|
||||
if (table_res.code != RESULT_SUCCESS) {
|
||||
res = hbc_get_string_tables (hs->hbc, &tables);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
hs->string_count = tables.string_count;
|
||||
@@ -107,12 +99,12 @@ static bool opcode_is_conditional(u8 opcode) {
|
||||
case OP_JmpUndefinedLong:
|
||||
return true;
|
||||
default:
|
||||
/* Relational and equality conditional jumps occupy 152..191 */
|
||||
if (opcode >= OP_JLess && opcode <= OP_JStrictNotEqualLong) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Relational and equality conditional jumps occupy 152..191 */
|
||||
if (opcode >= OP_JLess && opcode <= OP_JStrictNotEqualLong) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -176,11 +168,11 @@ static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size,
|
||||
ut32 string_id = operand_values[i];
|
||||
if (string_id < hs->string_count && hs->hbc) {
|
||||
HBCStringMeta meta;
|
||||
Result meta_result = hbc_str_meta (hs->hbc, string_id, &meta);
|
||||
Result meta_result = hbc_get_string_meta (hs->hbc, string_id, &meta);
|
||||
if (meta_result.code == RESULT_SUCCESS) {
|
||||
/* Set op->ptr to the virtual address of the string.
|
||||
* The binary is loaded at 0x10000000, so add the string offset to that. */
|
||||
op->ptr = (st64) (0x10000000 + hs->string_storage_offset + meta.offset);
|
||||
/* Set op->ptr to the file offset of the string.
|
||||
* meta.offset already includes string_storage_file_offset */
|
||||
op->ptr = (st64) meta.offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +182,7 @@ static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size,
|
||||
if (hs->hbc) {
|
||||
ut32 offset = 0;
|
||||
HBCFunc fi;
|
||||
Result func_result = hbc_func_info (hs->hbc, function_id, &fi);
|
||||
Result func_result = hbc_get_function_info (hs->hbc, function_id, &fi);
|
||||
if (func_result.code == RESULT_SUCCESS) {
|
||||
// name = fi.name;
|
||||
offset = fi.offset;
|
||||
@@ -240,7 +232,8 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
|
||||
.bytecode_version = hs->bytecode_version,
|
||||
.asm_syntax = true,
|
||||
.resolve_string_ids = true,
|
||||
.string_tables = &string_tables
|
||||
.string_tables = &string_tables,
|
||||
.hbc = hs->hbc
|
||||
};
|
||||
|
||||
HBCInsnInfo sinfo;
|
||||
@@ -248,7 +241,7 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
|
||||
return false;
|
||||
}
|
||||
|
||||
op->mnemonic = sinfo.text? strdup (sinfo.text): strdup ("unk");
|
||||
op->mnemonic = sinfo.text? sinfo.text: strdup ("unk");
|
||||
op->size = sinfo.size? sinfo.size: 1;
|
||||
op->type = R_ANAL_OP_TYPE_UNK;
|
||||
op->family = R_ANAL_OP_FAMILY_CPU;
|
||||
@@ -512,7 +505,7 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int info(RArchSession *s, ut32 q) {
|
||||
static int archinfo(RArchSession *s, ut32 q) {
|
||||
(void)s;
|
||||
switch (q) {
|
||||
case R_ARCH_INFO_CODE_ALIGN:
|
||||
@@ -533,6 +526,7 @@ static char *mnemonics(RArchSession *s, int id, bool json) {
|
||||
(void)s;
|
||||
(void)id;
|
||||
(void)json;
|
||||
// TODO: not implemented
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -595,7 +589,7 @@ static bool fini(RArchSession *s) {
|
||||
HermesArchSession *hs = (HermesArchSession *)s->data;
|
||||
if (hs) {
|
||||
if (hs->hbc) {
|
||||
hbc_free (hs->hbc);
|
||||
hbc_close (hs->hbc);
|
||||
hs->hbc = NULL;
|
||||
}
|
||||
free (hs);
|
||||
@@ -632,7 +626,7 @@ static char *regs(RArchSession *s) {
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
const RArchPlugin r_arch_plugin_hermes = {
|
||||
const RArchPlugin r_arch_plugin_r2hermes = {
|
||||
.meta = {
|
||||
.name = "hbc.arch",
|
||||
.author = "pancake",
|
||||
@@ -645,7 +639,7 @@ const RArchPlugin r_arch_plugin_hermes = {
|
||||
.decode = &decode,
|
||||
.encode = &encode,
|
||||
.regs = regs,
|
||||
.info = info,
|
||||
.info = archinfo,
|
||||
.mnemonics = mnemonics,
|
||||
.init = init,
|
||||
.fini = fini,
|
||||
@@ -654,7 +648,7 @@ const RArchPlugin r_arch_plugin_hermes = {
|
||||
#ifndef R2_PLUGIN_INCORE
|
||||
R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_ARCH,
|
||||
.data = (void *)&r_arch_plugin_hermes,
|
||||
.data = (void *)&r_arch_plugin_r2hermes,
|
||||
.version = R2_VERSION,
|
||||
.abiversion = R2_ABIVERSION
|
||||
};
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
/* radare2 - LGPL - Copyright 2025 - pancake */
|
||||
/* radare2 - LGPL - Copyright 2025-2026 - pancake */
|
||||
|
||||
#include <r_asm.h>
|
||||
|
||||
@@ -214,7 +214,7 @@ static char *parse(RAsmPluginSession *aps, const char *data) {
|
||||
return r_str_pseudo_transform (pseudo_rules, data);
|
||||
}
|
||||
|
||||
RAsmPlugin r_asm_plugin_hbc = {
|
||||
const RAsmPlugin r_asm_plugin_r2hermes = {
|
||||
.meta = {
|
||||
.name = "hbc.pseudo",
|
||||
.desc = "Hermes bytecode pseudo syntax",
|
||||
@@ -227,7 +227,7 @@ RAsmPlugin r_asm_plugin_hbc = {
|
||||
#ifndef R2_PLUGIN_INCORE
|
||||
R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_ASM,
|
||||
.data = &r_asm_plugin_hbc,
|
||||
.data = (void *)&r_asm_plugin_r2hermes,
|
||||
.version = R2_VERSION,
|
||||
.abiversion = R2_ABIVERSION
|
||||
};
|
||||
|
||||
+64
-55
@@ -1,16 +1,19 @@
|
||||
/* radare2 - LGPL - Copyright 2025 - pancake */
|
||||
/* radare2 - LGPL - Copyright 2025-2026 - pancake */
|
||||
|
||||
#include <r_bin.h>
|
||||
#include <hbc/hbc.h>
|
||||
#include "utils.inc.c"
|
||||
|
||||
#define HEADER_MAGIC 0x1f1903c103bc1fc6ULL
|
||||
#define HBC_VADDR_BASE 0x10000000
|
||||
|
||||
typedef struct {
|
||||
HBC *hbc;
|
||||
ut8 *data;
|
||||
} HBCBinObj;
|
||||
|
||||
static bool check(RBinFile *R_UNUSED bf, RBuffer *b) {
|
||||
static bool check(RBinFile *bf, RBuffer *b) {
|
||||
(void)bf;
|
||||
if (r_buf_size (b) >= 8) {
|
||||
ut64 magic = 0;
|
||||
r_buf_read_at (b, 0, (ut8 *)&magic, sizeof (magic));
|
||||
@@ -20,11 +23,18 @@ static bool check(RBinFile *R_UNUSED bf, RBuffer *b) {
|
||||
}
|
||||
|
||||
static bool load(RBinFile *bf, RBuffer *buf, ut64 R_UNUSED loadaddr) {
|
||||
if (check (bf, buf) && bf->file) {
|
||||
HBC *hbc = hbc_new_file (bf->file);
|
||||
if (hbc) {
|
||||
if (check (bf, buf)) {
|
||||
HBC *hbc = NULL;
|
||||
ut8 *data = NULL;
|
||||
Result res = hbc_open_from_buffer (buf, &hbc, &data);
|
||||
if (res.code == RESULT_SUCCESS) {
|
||||
HBCBinObj *bo = R_NEW0 (HBCBinObj);
|
||||
if (!bo) {
|
||||
hbc_free_data_and_close (&hbc, &data);
|
||||
return false;
|
||||
}
|
||||
bo->hbc = hbc;
|
||||
bo->data = data;
|
||||
bf->bo->bin_obj = bo;
|
||||
bf->buf = buf;
|
||||
return true;
|
||||
@@ -36,12 +46,12 @@ static bool load(RBinFile *bf, RBuffer *buf, ut64 R_UNUSED loadaddr) {
|
||||
static void destroy(RBinFile *bf) {
|
||||
HBCBinObj *bo = bf->bo->bin_obj;
|
||||
if (bo) {
|
||||
hbc_free (bo->hbc);
|
||||
hbc_free_data_and_close (&bo->hbc, &bo->data);
|
||||
free (bo);
|
||||
}
|
||||
}
|
||||
|
||||
static HBC *get_provider(RBinFile *bf) {
|
||||
static HBC *get_hbc(RBinFile *bf) {
|
||||
HBCBinObj *hbo = R_UNWRAP3 (bf, bo, bin_obj);
|
||||
return hbo? hbo->hbc: NULL;
|
||||
}
|
||||
@@ -67,20 +77,18 @@ static bool is_valid_entrypoint(RBuffer *buf, ut64 offset) {
|
||||
}
|
||||
|
||||
/* Unified entrypoint resolution with fallback chain */
|
||||
static ut64 resolve_entrypoint(RBinFile *bf, HBC *provider) {
|
||||
if (!provider) {
|
||||
static ut64 resolve_entrypoint(RBinFile *bf, HBC *hbc) {
|
||||
if (!hbc) {
|
||||
return 0;
|
||||
}
|
||||
/* Try 1: Find MainAppContent symbol */
|
||||
u32 func_count;
|
||||
if (hbc_func_count (provider, &func_count).code == RESULT_SUCCESS) {
|
||||
for (u32 i = 0; i < func_count; i++) {
|
||||
HBCFunc fi;
|
||||
if (hbc_func_info (provider, i, &fi).code == RESULT_SUCCESS) {
|
||||
if (fi.name && strcmp (fi.name, "MainAppContent") == 0) {
|
||||
if (is_valid_entrypoint (bf->buf, fi.offset)) {
|
||||
return fi.offset;
|
||||
}
|
||||
u32 func_count = hbc_function_count (hbc);
|
||||
for (u32 i = 0; i < func_count; i++) {
|
||||
HBCFunc fi;
|
||||
if (hbc_get_function_info (hbc, i, &fi).code == RESULT_SUCCESS) {
|
||||
if (fi.name && strcmp (fi.name, "MainAppContent") == 0) {
|
||||
if (is_valid_entrypoint (bf->buf, fi.offset)) {
|
||||
return fi.offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +96,7 @@ static ut64 resolve_entrypoint(RBinFile *bf, HBC *provider) {
|
||||
|
||||
/* Try 2: Use header globalCodeIndex */
|
||||
HBCHeader hh;
|
||||
if (hbc_hdr (provider, &hh).code == RESULT_SUCCESS) {
|
||||
if (hbc_get_header (hbc, &hh).code == RESULT_SUCCESS) {
|
||||
if (is_valid_entrypoint (bf->buf, hh.globalCodeIndex)) {
|
||||
return hh.globalCodeIndex;
|
||||
}
|
||||
@@ -97,7 +105,7 @@ static ut64 resolve_entrypoint(RBinFile *bf, HBC *provider) {
|
||||
/* Try 3: Use first function's offset */
|
||||
if (func_count > 0) {
|
||||
HBCFunc fi;
|
||||
if (hbc_func_info (provider, 0, &fi).code == RESULT_SUCCESS) {
|
||||
if (hbc_get_function_info (hbc, 0, &fi).code == RESULT_SUCCESS) {
|
||||
if (is_valid_entrypoint (bf->buf, fi.offset)) {
|
||||
return fi.offset;
|
||||
}
|
||||
@@ -128,15 +136,15 @@ static void fill_info(RBinInfo *ret, const char *file_path, bool has_version, ut
|
||||
ret->cpu = has_version? r_str_newf ("%u", version): strdup ("unknown");
|
||||
}
|
||||
|
||||
static RBinInfo *info(RBinFile *bf) {
|
||||
static RBinInfo *bininfo(RBinFile *bf) {
|
||||
RBinInfo *ret = R_NEW0 (RBinInfo);
|
||||
bool has_version = false;
|
||||
ut32 version = 0;
|
||||
|
||||
HBC *provider = get_provider (bf);
|
||||
if (provider) {
|
||||
HBC *hbc = get_hbc (bf);
|
||||
if (hbc) {
|
||||
HBCHeader hh;
|
||||
if (hbc_hdr (provider, &hh).code == RESULT_SUCCESS) {
|
||||
if (hbc_get_header (hbc, &hh).code == RESULT_SUCCESS) {
|
||||
has_version = true;
|
||||
version = hh.version;
|
||||
}
|
||||
@@ -181,8 +189,8 @@ static RList *entries(RBinFile *bf) {
|
||||
}
|
||||
|
||||
RBinAddr *addr = R_NEW0 (RBinAddr);
|
||||
HBC *provider = get_provider (bf);
|
||||
ut64 entrypoint = resolve_entrypoint (bf, provider);
|
||||
HBC *hbc = get_hbc (bf);
|
||||
ut64 entrypoint = resolve_entrypoint (bf, hbc);
|
||||
|
||||
addr->paddr = entrypoint;
|
||||
addr->vaddr = HBC_VADDR_BASE + entrypoint;
|
||||
@@ -196,20 +204,16 @@ static ut64 baddr(RBinFile *bf R_UNUSED) {
|
||||
}
|
||||
|
||||
static RList *symbols(RBinFile *bf) {
|
||||
RList *symbols = r_list_newf ((RListFree)free);
|
||||
HBC *provider = get_provider (bf);
|
||||
if (!provider) {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
u32 func_count;
|
||||
if (hbc_func_count (provider, &func_count).code != RESULT_SUCCESS) {
|
||||
RList *symbols = r_list_newf ((RListFree)r_bin_symbol_free);
|
||||
HBC *hbc = get_hbc (bf);
|
||||
if (!hbc) {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
u32 func_count = hbc_function_count (hbc);
|
||||
for (u32 i = 0; i < func_count; i++) {
|
||||
HBCFunc fi;
|
||||
if (hbc_func_info (provider, i, &fi).code != RESULT_SUCCESS) {
|
||||
if (hbc_get_function_info (hbc, i, &fi).code != RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -225,7 +229,7 @@ static RList *symbols(RBinFile *bf) {
|
||||
|
||||
/* Add container prefix if available */
|
||||
const char *src = NULL;
|
||||
if (hbc_src (provider, i, &src).code == RESULT_SUCCESS && src && *src) {
|
||||
if (hbc_get_function_source (hbc, i, &src).code == RESULT_SUCCESS && src && *src) {
|
||||
char *sp = r_name_filter_dup (src);
|
||||
if (sp && *sp) {
|
||||
char *withpref = r_str_newf ("%s__%s", sp, san);
|
||||
@@ -255,44 +259,49 @@ static RList *symbols(RBinFile *bf) {
|
||||
}
|
||||
|
||||
static RList *strings(RBinFile *bf) {
|
||||
RList *ret = r_list_newf ((RListFree)free);
|
||||
HBC *provider = get_provider (bf);
|
||||
if (!provider) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 string_count;
|
||||
if (hbc_str_count (provider, &string_count).code != RESULT_SUCCESS) {
|
||||
RList *ret = r_list_newf ((RListFree)r_bin_string_free);
|
||||
HBC *hbc = get_hbc (bf);
|
||||
if (!hbc) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 string_count = hbc_string_count (hbc);
|
||||
for (u32 i = 0; i < string_count; i++) {
|
||||
const char *str = NULL;
|
||||
if (hbc_str (provider, i, &str).code != RESULT_SUCCESS || !str) {
|
||||
if (hbc_get_string (hbc, i, &str).code != RESULT_SUCCESS || !str) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HBCStringMeta meta;
|
||||
if (hbc_str_meta (provider, i, &meta).code != RESULT_SUCCESS) {
|
||||
if (hbc_get_string_meta (hbc, i, &meta).code != RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t str_len = strlen (str);
|
||||
if (str_len == 0 || str_len >= R_BIN_SIZEOF_STRINGS) {
|
||||
if (meta.length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RBinString *ptr = R_NEW0 (RBinString);
|
||||
ptr->string = strdup (str);
|
||||
size_t str_len = strlen (str);
|
||||
if (str_len >= R_BIN_SIZEOF_STRINGS) {
|
||||
ptr->string = r_str_ndup (str, R_BIN_SIZEOF_STRINGS - 4);
|
||||
if (ptr->string) {
|
||||
char *tmp = r_str_newf ("%s...", ptr->string);
|
||||
free (ptr->string);
|
||||
ptr->string = tmp;
|
||||
}
|
||||
} else {
|
||||
ptr->string = strdup (str);
|
||||
}
|
||||
if (!ptr->string) {
|
||||
free (ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr->paddr = meta.offset;
|
||||
ptr->vaddr = HBC_VADDR_BASE + meta.offset;
|
||||
ptr->size = str_len;
|
||||
ptr->length = str_len;
|
||||
ptr->vaddr = meta.offset;
|
||||
ptr->size = meta.isUTF16 ? (meta.length * 2) : meta.length;
|
||||
ptr->length = meta.length;
|
||||
ptr->ordinal = i;
|
||||
r_list_append (ret, ptr);
|
||||
}
|
||||
@@ -300,14 +309,14 @@ static RList *strings(RBinFile *bf) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
RBinPlugin r_bin_plugin_hermes = {
|
||||
const RBinPlugin r_bin_plugin_r2hermes = {
|
||||
.meta = {
|
||||
.name = "hbc.bin",
|
||||
.author = "pancake",
|
||||
.desc = "Hermes bytecode format",
|
||||
.license = "BSD",
|
||||
},
|
||||
.info = &info,
|
||||
.info = &bininfo,
|
||||
.load = &load,
|
||||
.destroy = &destroy,
|
||||
.check = &check,
|
||||
@@ -321,7 +330,7 @@ RBinPlugin r_bin_plugin_hermes = {
|
||||
#ifndef R2_PLUGIN_INCORE
|
||||
R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_BIN,
|
||||
.data = &r_bin_plugin_hermes,
|
||||
.data = (void *)&r_bin_plugin_r2hermes,
|
||||
.version = R2_VERSION,
|
||||
.abiversion = R2_ABIVERSION
|
||||
};
|
||||
|
||||
+227
-243
@@ -1,27 +1,37 @@
|
||||
/* radare2 - LGPL - Copyright 2025 - pancake */
|
||||
/* radare2 - LGPL - Copyright 2025-2026 - pancake */
|
||||
|
||||
/* r2 core plugin for Hermes bytecode decompilation */
|
||||
|
||||
#include <r_core.h>
|
||||
#include <hbc/hbc.h>
|
||||
#include "utils.inc.c"
|
||||
|
||||
/* Plugin registration - need these when HBC_CORE_REGISTER_PLUGINS is enabled */
|
||||
#ifdef HBC_CORE_REGISTER_PLUGINS
|
||||
extern const RArchPlugin r_arch_plugin_r2hermes;
|
||||
extern const RBinPlugin r_bin_plugin_r2hermes;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
HBC *hbc; /* Cached HBC data provider per file */
|
||||
HBC *hbc;
|
||||
RCore *core;
|
||||
char *file_path;
|
||||
ut8 *data;
|
||||
} HbcContext;
|
||||
|
||||
static HbcContext hbc_ctx = {
|
||||
.hbc = NULL,
|
||||
.core = NULL,
|
||||
.file_path = NULL
|
||||
};
|
||||
|
||||
static const char *safe_errmsg(const char *s) {
|
||||
return (s && *s)? s: "Unknown error";
|
||||
return r_str_get_fail (s, "Unknown error");
|
||||
}
|
||||
|
||||
static const char *safe_name(const char *s) {
|
||||
return (s && *s)? s: "unknown";
|
||||
static ut32 parse_function_id(const char *addr_str) {
|
||||
const char *as = r_str_trim_head_ro (addr_str);
|
||||
if (*as) {
|
||||
ut64 fid = r_num_get (NULL, as);
|
||||
if (fid != UT64_MAX && fid < UT32_MAX) {
|
||||
return fid & UT32_MAX;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Comment callback for retrieving r2 comments (CC command) at an address */
|
||||
@@ -46,10 +56,6 @@ static char *r2_flag_callback(void *context, u64 address) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define HBC_CONS(core) ((core)->cons)
|
||||
#define HBC_PRINTF(core, fmt, ...) r_cons_printf(HBC_CONS(core),(fmt), ## __VA_ARGS__)
|
||||
#define HBC_PRINT(core, s) r_cons_print(HBC_CONS(core),(s))
|
||||
|
||||
static const char *current_file_path(RCore *core) {
|
||||
if (core && core->bin) {
|
||||
RBinInfo *bi = r_bin_get_info (core->bin);
|
||||
@@ -61,24 +67,20 @@ static const char *current_file_path(RCore *core) {
|
||||
}
|
||||
|
||||
/* Helper to load the current binary as HBC using provider API */
|
||||
static Result hbc_load_current_binary(RCore *core) {
|
||||
static Result hbc_load_current_binary(HbcContext *ctx, RCore *core) {
|
||||
const char *file_path = current_file_path (core);
|
||||
if (R_STR_ISEMPTY (file_path)) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "No binary loaded in r2");
|
||||
}
|
||||
|
||||
/* Reload if file changed */
|
||||
if (hbc_ctx.hbc && hbc_ctx.file_path && !strcmp (hbc_ctx.file_path, file_path)) {
|
||||
if (ctx->hbc && ctx->file_path && !strcmp (ctx->file_path, file_path)) {
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/* Clean up old provider */
|
||||
if (hbc_ctx.hbc) {
|
||||
hbc_free (hbc_ctx.hbc);
|
||||
hbc_ctx.hbc = NULL;
|
||||
}
|
||||
free (hbc_ctx.file_path);
|
||||
hbc_ctx.file_path = NULL;
|
||||
hbc_free_data_and_close (&ctx->hbc, &ctx->data);
|
||||
R_FREE (ctx->file_path);
|
||||
|
||||
/* Get RBinFile from r2 (already parsed by r2) */
|
||||
RBinFile *bf = core->bin->cur;
|
||||
@@ -86,45 +88,41 @@ static Result hbc_load_current_binary(RCore *core) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "No binary file loaded or r2 version incompatible");
|
||||
}
|
||||
|
||||
/* Create provider from r2 RBinFile (NO file I/O) */
|
||||
hbc_ctx.hbc = hbc_new_r2 (bf);
|
||||
if (!hbc_ctx.hbc) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to create provider");
|
||||
/* Open HBC from buffer */
|
||||
Result res = hbc_open_from_buffer (bf->buf, &ctx->hbc, &ctx->data);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
hbc_ctx.core = core;
|
||||
hbc_ctx.file_path = strdup (file_path);
|
||||
if (!hbc_ctx.file_path) {
|
||||
hbc_free (hbc_ctx.hbc);
|
||||
hbc_ctx.hbc = NULL;
|
||||
ctx->core = core;
|
||||
ctx->file_path = strdup (file_path);
|
||||
if (!ctx->file_path) {
|
||||
hbc_free_data_and_close (&ctx->hbc, &ctx->data);
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Out of memory");
|
||||
}
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/* Find function ID at a given offset */
|
||||
static int find_function_at_offset(u32 offset, u32 *out_id) {
|
||||
if (!out_id || !hbc_ctx.hbc) {
|
||||
static int find_function_at_offset(HbcContext *ctx, u32 offset, u32 *out_id) {
|
||||
if (!out_id || !ctx->hbc) {
|
||||
return -1;
|
||||
}
|
||||
u32 count;
|
||||
Result res = hbc_func_count (hbc_ctx.hbc, &count);
|
||||
if (res.code == RESULT_SUCCESS) {
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
HBCFunc fi;
|
||||
res = hbc_func_info (hbc_ctx.hbc, i, &fi);
|
||||
if (res.code == RESULT_SUCCESS) {
|
||||
if (offset >= fi.offset && offset < (fi.offset + fi.size)) {
|
||||
*out_id = i;
|
||||
return 0;
|
||||
}
|
||||
u32 count = hbc_function_count (ctx->hbc);
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
HBCFunc fi;
|
||||
Result res = hbc_get_function_info (ctx->hbc, i, &fi);
|
||||
if (res.code == RESULT_SUCCESS) {
|
||||
if (offset >= fi.offset && offset < (fi.offset + fi.size)) {
|
||||
*out_id = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create decompile options with r2 integration */
|
||||
/* Create decompile options from radare2 settings */
|
||||
static HBCDecompOptions make_decompile_options(RCore *core, bool show_offsets, u64 function_base) {
|
||||
HBCDecompOptions opts = {
|
||||
.pretty_literals = r_config_get_b (core->config, "hbc.pretty_literals"),
|
||||
@@ -147,165 +145,133 @@ static HBCDecompOptions make_decompile_options(RCore *core, bool show_offsets, u
|
||||
}
|
||||
|
||||
/* Decompile function at current offset or all functions if not in a function */
|
||||
static void cmd_decompile_current_ex(RCore *core, bool show_offsets) {
|
||||
Result result = hbc_load_current_binary (core);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: %s\n", safe_errmsg (result.error_message));
|
||||
static void cmd_decompile_current_ex(HbcContext *ctx, RCore *core, bool show_offsets) {
|
||||
Result res = hbc_load_current_binary (ctx, core);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
R_LOG_ERROR ("%s", safe_errmsg (res.error_message));
|
||||
return;
|
||||
}
|
||||
|
||||
u32 function_id = 0;
|
||||
/* Get current offset from r2 core */
|
||||
int found = find_function_at_offset ((u32)core->addr, &function_id);
|
||||
int found = find_function_at_offset (ctx, (u32)core->addr, &function_id);
|
||||
|
||||
if (found == 0) {
|
||||
/* Found function at current offset - get its base address */
|
||||
HBCFunc fi;
|
||||
Result fres = hbc_func_info (hbc_ctx.hbc, function_id, &fi);
|
||||
Result fres = hbc_get_function_info (ctx->hbc, function_id, &fi);
|
||||
u64 func_base = (fres.code == RESULT_SUCCESS)? fi.offset: 0;
|
||||
HBCDecompOptions opts = make_decompile_options (core, show_offsets, func_base);
|
||||
char *output = NULL;
|
||||
result = hbc_decomp_fn (hbc_ctx.hbc, function_id, opts, &output);
|
||||
if (result.code == RESULT_SUCCESS && output) {
|
||||
res = hbc_decomp_fn (ctx->hbc, function_id, opts, &output);
|
||||
if (res.code == RESULT_SUCCESS && output) {
|
||||
r_str_trim (output);
|
||||
HBC_PRINTF (core, "%s\n", output);
|
||||
r_cons_println (core->cons, output);
|
||||
} else {
|
||||
HBC_PRINTF (core, "Error decompiling function %u: %s\n", function_id, safe_errmsg (result.error_message));
|
||||
R_LOG_ERROR ("Decompiling function %u: %s", function_id, safe_errmsg (res.error_message));
|
||||
}
|
||||
free (output);
|
||||
} else {
|
||||
/* Not in a function, decompile all - use 0 as base since multiple functions */
|
||||
HBCDecompOptions opts = make_decompile_options (core, show_offsets, 0);
|
||||
char *output = NULL;
|
||||
result = hbc_decomp_all (hbc_ctx.hbc, opts, &output);
|
||||
if (result.code == RESULT_SUCCESS && output) {
|
||||
res = hbc_decomp_all (ctx->hbc, opts, &output);
|
||||
if (res.code == RESULT_SUCCESS && output) {
|
||||
r_str_trim (output);
|
||||
HBC_PRINTF (core, "%s\n", output);
|
||||
r_cons_println (core->cons, output);
|
||||
} else {
|
||||
HBC_PRINTF (core, "Error decompiling: %s\n", safe_errmsg (result.error_message));
|
||||
R_LOG_ERROR ("Error decompiling: %s", safe_errmsg (res.error_message));
|
||||
}
|
||||
free (output);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_decompile_current(RCore *core) {
|
||||
cmd_decompile_current_ex (core, false);
|
||||
}
|
||||
|
||||
/* Decompile all functions */
|
||||
static void cmd_decompile_all_ex(RCore *core, bool show_offsets) {
|
||||
Result result = hbc_load_current_binary (core);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: %s\n", safe_errmsg (result.error_message));
|
||||
static void cmd_decompile_all_ex(HbcContext *ctx, RCore *core, bool show_offsets) {
|
||||
Result res = hbc_load_current_binary (ctx, core);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
R_LOG_ERROR ("%s", safe_errmsg (res.error_message));
|
||||
return;
|
||||
}
|
||||
|
||||
HBCDecompOptions opts = make_decompile_options (core, show_offsets, 0);
|
||||
char *output = NULL;
|
||||
result = hbc_decomp_all (hbc_ctx.hbc, opts, &output);
|
||||
if (result.code == RESULT_SUCCESS && output) {
|
||||
res = hbc_decomp_all (ctx->hbc, opts, &output);
|
||||
if (res.code == RESULT_SUCCESS && output) {
|
||||
r_str_trim (output);
|
||||
HBC_PRINTF (core, "%s\n", output);
|
||||
r_cons_println (core->cons, output);
|
||||
} else {
|
||||
HBC_PRINTF (core, "Error decompiling: %s\n", safe_errmsg (result.error_message));
|
||||
R_LOG_ERROR ("Error decompiling: %s", safe_errmsg (res.error_message));
|
||||
}
|
||||
free (output);
|
||||
}
|
||||
|
||||
static void cmd_decompile_all(RCore *core) {
|
||||
cmd_decompile_all_ex (core, false);
|
||||
}
|
||||
|
||||
/* Decompile current function by address */
|
||||
static void cmd_decompile_function_ex(RCore *core, const char *addr_str, bool show_offsets) {
|
||||
Result result = hbc_load_current_binary (core);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: %s\n", safe_errmsg (result.error_message));
|
||||
return;
|
||||
}
|
||||
|
||||
u32 function_id = 0;
|
||||
if (addr_str && *addr_str) {
|
||||
char *end = NULL;
|
||||
unsigned long parsed = strtoul (addr_str, &end, 0);
|
||||
if (!end || end == addr_str) {
|
||||
HBC_PRINTF (core, "Error: invalid function id '%s'\n", addr_str);
|
||||
return;
|
||||
}
|
||||
function_id = (u32)parsed;
|
||||
}
|
||||
|
||||
u32 count;
|
||||
result = hbc_func_count (hbc_ctx.hbc, &count);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: cannot get function count\n");
|
||||
static void cmd_decompile_function_ex(HbcContext *ctx, RCore *core, const char *addr_str, bool show_offsets) {
|
||||
Result res = hbc_load_current_binary (ctx, core);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
R_LOG_ERROR ("%s", safe_errmsg (res.error_message));
|
||||
return;
|
||||
}
|
||||
u32 function_id = parse_function_id (addr_str);
|
||||
u32 count = hbc_function_count (ctx->hbc);
|
||||
if (function_id >= count) {
|
||||
HBC_PRINTF (core, "Error: function id %u out of range (count=%u)\n", function_id, count);
|
||||
R_LOG_ERROR ("function id %u out of range (count=%u)", function_id, count);
|
||||
return;
|
||||
}
|
||||
/* Get function base address for offset calculation */
|
||||
HBCFunc fi;
|
||||
Result fres = hbc_func_info (hbc_ctx.hbc, function_id, &fi);
|
||||
Result fres = hbc_get_function_info (ctx->hbc, function_id, &fi);
|
||||
u64 func_base = (fres.code == RESULT_SUCCESS)? fi.offset: 0;
|
||||
HBCDecompOptions opts = make_decompile_options (core, show_offsets, func_base);
|
||||
char *output = NULL;
|
||||
result = hbc_decomp_fn (hbc_ctx.hbc, function_id, opts, &output);
|
||||
if (result.code == RESULT_SUCCESS && output) {
|
||||
res = hbc_decomp_fn (ctx->hbc, function_id, opts, &output);
|
||||
if (res.code == RESULT_SUCCESS && output) {
|
||||
r_str_trim (output);
|
||||
HBC_PRINTF (core, "%s\n", output);
|
||||
r_cons_println (core->cons, output);
|
||||
} else {
|
||||
HBC_PRINTF (core, "Error decompiling function %u: %s\n", function_id, safe_errmsg (result.error_message));
|
||||
R_LOG_ERROR ("Decompiling function %u: %s", function_id, safe_errmsg (res.error_message));
|
||||
}
|
||||
free (output);
|
||||
}
|
||||
|
||||
static void cmd_decompile_function(RCore *core, const char *addr_str) {
|
||||
cmd_decompile_function_ex (core, addr_str, false);
|
||||
}
|
||||
|
||||
/* List available functions */
|
||||
static void cmd_list_functions(RCore *core) {
|
||||
Result result = hbc_load_current_binary (core);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: %s\n", safe_errmsg (result.error_message));
|
||||
static void cmd_list_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;
|
||||
}
|
||||
|
||||
u32 count;
|
||||
result = hbc_func_count (hbc_ctx.hbc, &count);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: cannot get function count\n");
|
||||
return;
|
||||
}
|
||||
HBC_PRINTF (core, "Functions (%u):\n", count);
|
||||
u32 count = hbc_function_count (ctx->hbc);
|
||||
r_cons_printf (core->cons, "Functions (%u):\n", count);
|
||||
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
HBCFunc info;
|
||||
Result res = hbc_func_info (hbc_ctx.hbc, i, &info);
|
||||
Result res = hbc_get_function_info (ctx->hbc, i, &info);
|
||||
if (res.code == RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, " [%3u] %s at 0x%08x size=0x%x params=%u\n", i, safe_name (info.name), info.offset, info.size, info.param_count);
|
||||
r_cons_printf (core->cons, " [%3u] %s at 0x%08x size=0x%x params=%u\n", i, r_str_get_fail (info.name, "unknown"), info.offset, info.size, info.param_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Show file information */
|
||||
static void cmd_file_info(RCore *core) {
|
||||
Result result = hbc_load_current_binary (core);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error: %s\n", safe_errmsg (result.error_message));
|
||||
static void cmd_file_info(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;
|
||||
result = hbc_hdr (hbc_ctx.hbc, &header);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "Error reading header: %s\n", safe_errmsg (result.error_message));
|
||||
res = hbc_get_header (ctx->hbc, &header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
R_LOG_ERROR ("reading header: %s", safe_errmsg (res.error_message));
|
||||
return;
|
||||
}
|
||||
RCons *cons = core->cons;
|
||||
|
||||
HBC_PRINTF (core,
|
||||
r_cons_printf (cons,
|
||||
"Hermes Bytecode File Information:\n"
|
||||
" Version: %u\n"
|
||||
" File Length: %u bytes\n"
|
||||
@@ -326,117 +292,128 @@ static void cmd_file_info(RCore *core) {
|
||||
|
||||
char hex[41] = { 0 };
|
||||
r_hex_bin2str (header.sourceHash, 20, hex);
|
||||
HBC_PRINTF (core, "\nHash Information (for binary patching):\n");
|
||||
HBC_PRINTF (core, " Source Hash (header): %s\n", hex);
|
||||
r_cons_printf (cons,
|
||||
"\nHash Information (for binary patching):\n"
|
||||
" Source Hash (header): %s\n",
|
||||
hex);
|
||||
const ut64 file_size = r_io_size (core->io);
|
||||
const ut64 expected_size = (ut64)header.fileLength + 20;
|
||||
|
||||
ut64 file_size = r_io_size (core->io);
|
||||
bool has_footer = (file_size == (ut64)header.fileLength + 20);
|
||||
r_cons_printf (cons, " File size: %" PFMT64u " bytes\n", file_size);
|
||||
r_cons_printf (cons, " Header fileLength: %u bytes\n", header.fileLength);
|
||||
|
||||
if (has_footer) {
|
||||
if (file_size >= expected_size) {
|
||||
/* Footer exists or file has extra bytes - read and verify */
|
||||
ut8 footer[20] = { 0 };
|
||||
r_io_read_at (core->io, header.fileLength, footer, 20);
|
||||
r_hex_bin2str (footer, 20, hex);
|
||||
HBC_PRINTF (core, " Footer Hash (stored): %s\n", hex);
|
||||
r_cons_printf (core->cons, " Footer Hash (stored): %s\n", hex);
|
||||
|
||||
char *computed = r_core_cmd_strf (core, "ph sha1 %u @0", header.fileLength);
|
||||
if (computed) {
|
||||
r_str_trim (computed);
|
||||
HBC_PRINTF (core, " Footer Hash (computed): %s\n", computed);
|
||||
r_cons_printf (core->cons, " Footer Hash (computed): %s\n", computed);
|
||||
bool valid = (strlen (computed) == 40 && !strcmp (computed, hex));
|
||||
HBC_PRINTF (core, " Status: %s\n", valid? "VALID": "INVALID");
|
||||
if (!valid) {
|
||||
HBC_PRINTF (core, " Fix: .(fix-hbc) or r2 -wqc '.(fix-hbc)' file.hbc\n");
|
||||
r_cons_printf (core->cons, " Status: %s\n", valid? "VALID": "INVALID");
|
||||
if (!valid || file_size != expected_size) {
|
||||
r_cons_printf (core->cons, " Fix: .(fix-hbc) or r2 -wqc '.(fix-hbc)' file.hbc\n");
|
||||
}
|
||||
if (file_size > expected_size) {
|
||||
r_cons_printf (core->cons, " Warning: File has %" PFMT64u " extra bytes after footer\n", file_size - expected_size);
|
||||
}
|
||||
free (computed);
|
||||
}
|
||||
} else if (file_size == (ut64)header.fileLength && file_size >= 20) {
|
||||
/* fileLength == file_size: Check if last 20 bytes are a valid footer
|
||||
*(some versions include footer in fileLength) */
|
||||
ut64 content_size = file_size - 20;
|
||||
ut8 footer[20] = { 0 };
|
||||
r_io_read_at (core->io, content_size, footer, 20);
|
||||
r_hex_bin2str (footer, 20, hex);
|
||||
|
||||
char *computed = r_core_cmd_strf (core, "ph sha1 %" PFMT64u " @0", content_size);
|
||||
if (computed) {
|
||||
r_str_trim (computed);
|
||||
bool valid = (strlen (computed) == 40 && !strcmp (computed, hex));
|
||||
if (valid) {
|
||||
r_cons_printf (core->cons, " Footer Hash (stored): %s\n", hex);
|
||||
r_cons_printf (core->cons, " Footer Hash (computed): %s\n", computed);
|
||||
r_cons_printf (core->cons, " Status: VALID (footer included in fileLength)\n");
|
||||
} else {
|
||||
r_cons_printf (core->cons, " Footer: NOT PRESENT (file ends at fileLength)\n");
|
||||
r_cons_printf (core->cons, " Fix: .(fix-hbc) or r2 -wqc '.(fix-hbc)' file.hbc\n");
|
||||
}
|
||||
free (computed);
|
||||
} else {
|
||||
r_cons_printf (core->cons, " Footer: NOT PRESENT (file ends at fileLength)\n");
|
||||
r_cons_printf (core->cons, " Fix: .(fix-hbc) or r2 -wqc '.(fix-hbc)' file.hbc\n");
|
||||
}
|
||||
} else {
|
||||
HBC_PRINTF (core, " Footer: NOT PRESENT\n");
|
||||
HBC_PRINTF (core, " Fix: .(fix-hbc) or r2 -wqc '.(fix-hbc)' file.hbc\n");
|
||||
r_cons_printf (core->cons, " Footer: UNKNOWN (file size mismatch)\n");
|
||||
r_cons_printf (core->cons, " Expected: %" PFMT64u " or %u bytes, got %" PFMT64u "\n", expected_size, header.fileLength, file_size);
|
||||
r_cons_printf (core->cons, " Fix: .(fix-hbc) or r2 -wqc '.(fix-hbc)' file.hbc\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* JSON output for current function */
|
||||
static void cmd_json(RCore *core, const char *addr_str) {
|
||||
Result result = hbc_load_current_binary (core);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "{\"error\":\"%s\"}\n", safe_errmsg (result.error_message));
|
||||
static void cmd_json(HbcContext *ctx, RCore *core, const char *addr_str) {
|
||||
Result res = hbc_load_current_binary (ctx, core);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
PJ *pj = r_core_pj_new (core);
|
||||
pj_o (pj);
|
||||
pj_ks (pj, "error", safe_errmsg (res.error_message));
|
||||
pj_end (pj);
|
||||
char *s = pj_drain (pj);
|
||||
r_cons_println (core->cons, s);
|
||||
free (s);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 function_id = 0;
|
||||
if (addr_str && *addr_str) {
|
||||
char *end = NULL;
|
||||
unsigned long parsed = strtoul (addr_str, &end, 0);
|
||||
if (!end || end == addr_str) {
|
||||
HBC_PRINT (core, "{\"error\":\"invalid function id\"}\n");
|
||||
return;
|
||||
}
|
||||
function_id = (u32)parsed;
|
||||
}
|
||||
|
||||
u32 function_id = parse_function_id (addr_str);
|
||||
HBCDecompOptions opts = { .pretty_literals = true, .suppress_comments = false };
|
||||
u32 count;
|
||||
result = hbc_func_count (hbc_ctx.hbc, &count);
|
||||
if (result.code != RESULT_SUCCESS) {
|
||||
HBC_PRINTF (core, "{\"error\":\"cannot get function count\"}\n");
|
||||
u32 count = hbc_function_count (ctx->hbc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
PJ *pj = r_core_pj_new (core);
|
||||
pj_o (pj);
|
||||
pj_ks (pj, "error", "cannot get function count");
|
||||
pj_end (pj);
|
||||
char *s = pj_drain (pj);
|
||||
r_cons_println (core->cons, s);
|
||||
free (s);
|
||||
return;
|
||||
}
|
||||
PJ *pj = r_core_pj_new (core);
|
||||
pj_o (pj);
|
||||
pj_kn (pj, "function_id", function_id);
|
||||
pj_k (pj, "decompilation");
|
||||
if (function_id >= count) {
|
||||
HBC_PRINTF (core, "{\"function_id\":%u,\"decompilation\":null,\"error\":\"function id out of range\",\"count\":%u}\n", function_id, count);
|
||||
pj_null (pj);
|
||||
pj_ks (pj, "error", "function id out of range");
|
||||
pj_kn (pj, "count", count);
|
||||
pj_end (pj);
|
||||
char *s = pj_drain (pj);
|
||||
r_cons_println (core->cons, s);
|
||||
free (s);
|
||||
return;
|
||||
}
|
||||
char *output = NULL;
|
||||
result = hbc_decomp_fn (hbc_ctx.hbc, function_id, opts, &output);
|
||||
res = hbc_decomp_fn (ctx->hbc, function_id, opts, &output);
|
||||
|
||||
RStrBuf *sb = r_strbuf_newf ("{\"function_id\":%u,\"decompilation\":", function_id);
|
||||
if (!sb) {
|
||||
free (output);
|
||||
HBC_PRINT (core, "{\"error\":\"out of memory\"}\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.code == RESULT_SUCCESS && output) {
|
||||
r_strbuf_append (sb, "\"");
|
||||
for (const char *p = output; *p; p++) {
|
||||
const unsigned char ch = (unsigned char)*p;
|
||||
switch (ch) {
|
||||
case '"':
|
||||
r_strbuf_append (sb, "\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
r_strbuf_append (sb, "\\\\");
|
||||
break;
|
||||
case '\n':
|
||||
r_strbuf_append (sb, "\\n");
|
||||
break;
|
||||
case '\r':
|
||||
r_strbuf_append (sb, "\\r");
|
||||
break;
|
||||
case '\t':
|
||||
r_strbuf_append (sb, "\\t");
|
||||
break;
|
||||
default:
|
||||
if (ch < 0x20) {
|
||||
r_strbuf_appendf (sb, "\\u%04x", (unsigned int)ch);
|
||||
} else {
|
||||
r_strbuf_appendf (sb, "%c", (char)ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
r_strbuf_append (sb, "\"");
|
||||
if (res.code == RESULT_SUCCESS && output) {
|
||||
pj_s (pj, output);
|
||||
} else {
|
||||
r_strbuf_append (sb, "null");
|
||||
pj_null (pj);
|
||||
}
|
||||
r_strbuf_append (sb, "}\n");
|
||||
HBC_PRINT (core, r_strbuf_get (sb));
|
||||
r_strbuf_free (sb);
|
||||
pj_end (pj);
|
||||
char *s = pj_drain (pj);
|
||||
r_cons_println (core->cons, s);
|
||||
free (s);
|
||||
free (output);
|
||||
}
|
||||
|
||||
/* Show help */
|
||||
static void cmd_help(RCore *core) {
|
||||
HBC_PRINT (core,
|
||||
r_cons_print (core->cons,
|
||||
"Usage: pd:h[subcommand]\n"
|
||||
"Hermes bytecode decompiler via libhbc\n\n"
|
||||
"Subcommands:\n"
|
||||
@@ -459,10 +436,13 @@ static bool cmd_handler(RCorePluginSession *s, const char *input) {
|
||||
if (!core || !input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!r_str_startswith (input, "pd:h")) {
|
||||
return false;
|
||||
}
|
||||
HbcContext *ctx = s->data;
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *arg = input + 4;
|
||||
|
||||
@@ -470,50 +450,43 @@ static bool cmd_handler(RCorePluginSession *s, const char *input) {
|
||||
case '\0': /* pd:h */
|
||||
case ' ': /* pd:h (with spaces) */
|
||||
if (*arg == ' ' && arg[1] != '\0' && !isspace ((unsigned char)arg[1])) {
|
||||
HBC_PRINT (core, "Unknown subcommand. Use pd:h? for help.\n");
|
||||
R_LOG_ERROR ("Unknown subcommand. Use pd:h? for help");
|
||||
break;
|
||||
}
|
||||
cmd_decompile_current (core);
|
||||
cmd_decompile_current_ex (ctx, core, false);
|
||||
break;
|
||||
case 'a': /* pd:ha */
|
||||
cmd_decompile_all (core);
|
||||
cmd_decompile_all_ex (ctx, core, false);
|
||||
break;
|
||||
case 'c': { /* pd:hc [id] */
|
||||
const char *addr_str = arg + 1;
|
||||
while (*addr_str && isspace ((unsigned char)*addr_str)) {
|
||||
addr_str++;
|
||||
}
|
||||
cmd_decompile_function (core, addr_str);
|
||||
case 'c': /* pd:hc [id] */
|
||||
cmd_decompile_function_ex (ctx, core, arg + 1, false);
|
||||
break;
|
||||
}
|
||||
case 'f': /* pd:hf */
|
||||
cmd_list_functions (core);
|
||||
cmd_list_functions (ctx, core);
|
||||
break;
|
||||
case 'i': /* pd:hi */
|
||||
cmd_file_info (core);
|
||||
cmd_file_info (ctx, core);
|
||||
break;
|
||||
case 'j': { /* pd:hj [id] */
|
||||
const char *addr_str = arg + 1;
|
||||
while (*addr_str && isspace ((unsigned char)*addr_str)) {
|
||||
addr_str++;
|
||||
}
|
||||
cmd_json (core, addr_str);
|
||||
cmd_json (ctx, core, addr_str);
|
||||
break;
|
||||
}
|
||||
case 'o': { /* pd:ho [id] or pd:hoa */
|
||||
const char *sub = arg + 1;
|
||||
if (*sub == 'a') {
|
||||
/* pd:hoa */
|
||||
cmd_decompile_all_ex (core, true);
|
||||
} else {
|
||||
/* pd:ho [id] */
|
||||
if (*sub == 'a') { // "pd:hoa"
|
||||
cmd_decompile_all_ex (ctx, core, true);
|
||||
} else { // "pd:ho" [id]
|
||||
while (*sub && isspace ((unsigned char)*sub)) {
|
||||
sub++;
|
||||
}
|
||||
if (*sub) {
|
||||
cmd_decompile_function_ex (core, sub, true);
|
||||
cmd_decompile_function_ex (ctx, core, sub, true);
|
||||
} else {
|
||||
cmd_decompile_current_ex (core, true);
|
||||
cmd_decompile_current_ex (ctx, core, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -522,27 +495,39 @@ static bool cmd_handler(RCorePluginSession *s, const char *input) {
|
||||
cmd_help (core);
|
||||
break;
|
||||
default:
|
||||
HBC_PRINT (core, "Unknown subcommand. Use pd:h? for help.\n");
|
||||
R_LOG_ERROR ("Unknown subcommand. Use pd:h? for help");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool plugin_init(struct r_core_plugin_session_t *s) {
|
||||
static bool plugin_init(RCorePluginSession *s) {
|
||||
RCore *core = s->core;
|
||||
RConfig *cfg = core->config;
|
||||
|
||||
HbcContext *ctx = R_NEW0 (HbcContext);
|
||||
ctx->core = core;
|
||||
s->data = ctx;
|
||||
|
||||
#ifdef HBC_CORE_REGISTER_PLUGINS
|
||||
/* Register arch and bin plugins when enabled */
|
||||
if (core->anal && core->anal->arch) {
|
||||
r_arch_plugin_add (core->anal->arch, (RArchPlugin *)&r_arch_plugin_r2hermes);
|
||||
}
|
||||
if (core->bin) {
|
||||
r_bin_plugin_add (core->bin, (RBinPlugin *)&r_bin_plugin_r2hermes);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Define fix-hbc macro using r2's generic ph and wx commands
|
||||
* 1. Resize file to fileLength+20 (reads fileLength from header at offset 32)
|
||||
* 2. Write SHA1 hash of bytes 0 to ($s-20) at offset ($s-20)
|
||||
* If footer already exists, resize is no-op */
|
||||
r_core_cmd0 (core, "'(fix-hbc; ?e Fixing HBC footer hash...; r `pv4 @32`+20; wx `ph sha1 $s-20 @0` @ $s-20)");
|
||||
* 1. Resize file to fileLength+20 (using $ () for nested eval)
|
||||
* 2. Write SHA1 hash of bytes 0 to ($s-20) at offset ($s-20) */
|
||||
r_core_cmd0 (core, "'(fix-hbc; ?e Fixing HBC footer hash...; r `?vi $(pv4 @32)+20`; wx `ph sha1 $s-20 @0` @ $s-20)");
|
||||
|
||||
r_config_lock (cfg, false);
|
||||
|
||||
RConfigNode *node;
|
||||
node = r_config_set (cfg, "hbc.pretty_literals", "true");
|
||||
RConfigNode *node = r_config_set (cfg, "hbc.pretty_literals", "true");
|
||||
if (node) {
|
||||
r_config_node_desc (node, "Format literals nicely (objects, arrays, etc)");
|
||||
}
|
||||
@@ -598,20 +583,19 @@ static bool plugin_init(struct r_core_plugin_session_t *s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool plugin_fini(struct r_core_plugin_session_t *s) {
|
||||
(void)s;
|
||||
if (hbc_ctx.hbc) {
|
||||
hbc_free (hbc_ctx.hbc);
|
||||
hbc_ctx.hbc = NULL;
|
||||
static bool plugin_fini(RCorePluginSession *s) {
|
||||
HbcContext *ctx = s->data;
|
||||
if (ctx) {
|
||||
hbc_free_data_and_close (&ctx->hbc, &ctx->data);
|
||||
R_FREE (ctx->file_path);
|
||||
free (ctx);
|
||||
s->data = NULL;
|
||||
}
|
||||
hbc_ctx.core = NULL;
|
||||
free (hbc_ctx.file_path);
|
||||
hbc_ctx.file_path = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Plugin initialization */
|
||||
static RCorePlugin r_core_plugin_hbc = {
|
||||
RCorePlugin r_core_plugin_r2hermes = {
|
||||
.meta = {
|
||||
.name = "core_hbc",
|
||||
.desc = "Hermes bytecode decompiler plugin",
|
||||
@@ -626,7 +610,7 @@ static RCorePlugin r_core_plugin_hbc = {
|
||||
#ifndef R2_PLUGIN_INCORE
|
||||
R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_CORE,
|
||||
.data = &r_core_plugin_hbc,
|
||||
.data = (void *)&r_core_plugin_r2hermes,
|
||||
.version = R2_VERSION,
|
||||
.abiversion = R2_ABIVERSION
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* radare2 - LGPL - Copyright 2025-2026 - pancache */
|
||||
|
||||
/* Core HBC plugin with plugin registration enabled for r2one */
|
||||
|
||||
#define HBC_CORE_REGISTER_PLUGINS 1
|
||||
#define R2_PLUGIN_INCORE 1
|
||||
#include "bin_hbc.c"
|
||||
#include "arch_hbc.c"
|
||||
#include "core_hbc.c"
|
||||
@@ -1,431 +0,0 @@
|
||||
/* radare2 - LGPL - Copyright 2025 - pancake */
|
||||
/* R2 HBC Provider: Read HBC data from r2 RBinFile without separate file I/O */
|
||||
|
||||
#include <r_bin.h>
|
||||
#include <hbc/hbc.h>
|
||||
#include <hbc/common.h>
|
||||
#include <hbc/parser.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* R2 provider reads from an r2 RBinFile without opening a separate file.
|
||||
* Data is already parsed and cached by r2's bin_hbc plugin.
|
||||
*/
|
||||
struct HBC {
|
||||
RBinFile *bf; /* r2 binary file handle (not owned) */
|
||||
RBin *bin; /* r2 bin handle (not owned) */
|
||||
void *buf; /* r2 buffer for binary data (not owned) */
|
||||
|
||||
HBCHeader cached_header; /* Cache to avoid re-parsing */
|
||||
bool header_loaded;
|
||||
|
||||
/* String tables cache */
|
||||
HBCStrs cached_string_tables;
|
||||
bool string_tables_loaded;
|
||||
|
||||
/* Temporary buffer for bytecode reads */
|
||||
ut8 *tmp_read_buffer;
|
||||
size_t tmp_buffer_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse HBC header from RBuffer at offset 0.
|
||||
*/
|
||||
static Result parse_header_from_buffer(void *buf, HBCHeader *out) {
|
||||
if (!buf || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
ut8 header_buf[256];
|
||||
if (r_buf_read_at (buf, 0, header_buf, 256) < 32) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Cannot read header");
|
||||
}
|
||||
|
||||
memcpy (&out->magic, header_buf + 0, 8);
|
||||
out->version = r_read_le32 (header_buf + 8);
|
||||
memcpy (out->sourceHash, header_buf + 12, 20);
|
||||
out->fileLength = r_read_le32 (header_buf + 32);
|
||||
out->globalCodeIndex = r_read_le32 (header_buf + 36);
|
||||
out->functionCount = r_read_le32 (header_buf + 40);
|
||||
out->stringKindCount = r_read_le32 (header_buf + 44);
|
||||
out->identifierCount = r_read_le32 (header_buf + 48);
|
||||
out->stringCount = r_read_le32 (header_buf + 52);
|
||||
out->overflowStringCount = r_read_le32 (header_buf + 56);
|
||||
out->stringStorageSize = r_read_le32 (header_buf + 60);
|
||||
out->bigIntCount = r_read_le32 (header_buf + 64);
|
||||
out->bigIntStorageSize = r_read_le32 (header_buf + 68);
|
||||
out->regExpCount = r_read_le32 (header_buf + 72);
|
||||
out->regExpStorageSize = r_read_le32 (header_buf + 76);
|
||||
out->arrayBufferSize = r_read_le32 (header_buf + 80);
|
||||
out->objKeyBufferSize = r_read_le32 (header_buf + 84);
|
||||
out->objValueBufferSize = r_read_le32 (header_buf + 88);
|
||||
out->segmentID = r_read_le32 (header_buf + 92);
|
||||
out->cjsModuleCount = r_read_le32 (header_buf + 96);
|
||||
out->functionSourceCount = r_read_le32 (header_buf + 100);
|
||||
out->debugInfoOffset = r_read_le32 (header_buf + 104);
|
||||
out->staticBuiltins = (header_buf[108] & 0x01) != 0;
|
||||
out->cjsModulesStaticallyResolved = (header_buf[108] & 0x02) != 0;
|
||||
out->hasAsync = (header_buf[108] & 0x04) != 0;
|
||||
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and cache string tables from the binary.
|
||||
*/
|
||||
static Result parse_string_tables(HBC *hbc) {
|
||||
if (!hbc || !hbc->buf) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (!hbc->header_loaded) {
|
||||
Result res = parse_header_from_buffer (hbc->buf, &hbc->cached_header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
hbc->header_loaded = true;
|
||||
}
|
||||
|
||||
HBCHeader *h = &hbc->cached_header;
|
||||
|
||||
ut64 string_kind_offset = 112 + (h->functionCount * 32);
|
||||
|
||||
ut8 *string_kinds = (ut8 *)malloc (h->stringKindCount);
|
||||
if (!string_kinds) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Out of memory");
|
||||
}
|
||||
if (r_buf_read_at (hbc->buf, string_kind_offset, string_kinds, h->stringKindCount) != (int)h->stringKindCount) {
|
||||
free (string_kinds);
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Cannot read string kinds");
|
||||
}
|
||||
|
||||
ut64 identifier_offset = string_kind_offset + h->stringKindCount;
|
||||
ut64 small_table_offset = identifier_offset + (h->identifierCount * 4);
|
||||
small_table_offset = (small_table_offset + 3) & ~3;
|
||||
|
||||
StringTableEntry *small_table = (StringTableEntry *)calloc (h->stringCount, sizeof (StringTableEntry));
|
||||
if (!small_table) {
|
||||
free (string_kinds);
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Out of memory");
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < h->stringCount; i++) {
|
||||
ut8 entry_bytes[4];
|
||||
if (r_buf_read_at (hbc->buf, small_table_offset + (i * 4), entry_bytes, 4) != 4) {
|
||||
free (string_kinds);
|
||||
free (small_table);
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Cannot read small string table entry");
|
||||
}
|
||||
|
||||
u32 entry = entry_bytes[0] | (entry_bytes[1] << 8) | (entry_bytes[2] << 16) | (entry_bytes[3] << 24);
|
||||
|
||||
if (h->version >= 56) {
|
||||
small_table[i].isUTF16 = entry & 0x1;
|
||||
small_table[i].offset = (entry >> 1) & 0x7FFFFF;
|
||||
small_table[i].length = (entry >> 24) & 0xFF;
|
||||
small_table[i].isIdentifier = 0;
|
||||
} else {
|
||||
small_table[i].isUTF16 = entry & 0x1;
|
||||
small_table[i].isIdentifier = (entry >> 1) & 0x1;
|
||||
small_table[i].offset = (entry >> 2) & 0x3FFFFF;
|
||||
small_table[i].length = (entry >> 24) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
ut64 overflow_table_offset = small_table_offset + (h->stringCount * 4);
|
||||
overflow_table_offset = (overflow_table_offset + 3) & ~3;
|
||||
|
||||
OffsetLengthPair *overflow_table = NULL;
|
||||
if (h->overflowStringCount > 0) {
|
||||
overflow_table = (OffsetLengthPair *)malloc (h->overflowStringCount * sizeof (OffsetLengthPair));
|
||||
if (!overflow_table) {
|
||||
free (string_kinds);
|
||||
free (small_table);
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Out of memory");
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < h->overflowStringCount; i++) {
|
||||
ut8 offset_bytes[4], length_bytes[4];
|
||||
ut64 entry_offset = overflow_table_offset + (i * 8);
|
||||
|
||||
if (r_buf_read_at (hbc->buf, entry_offset, offset_bytes, 4) != 4 ||
|
||||
r_buf_read_at (hbc->buf, entry_offset + 4, length_bytes, 4) != 4) {
|
||||
free (string_kinds);
|
||||
free (small_table);
|
||||
free (overflow_table);
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Cannot read overflow string table");
|
||||
}
|
||||
|
||||
overflow_table[i].offset = offset_bytes[0] | (offset_bytes[1] << 8) | (offset_bytes[2] << 16) | (offset_bytes[3] << 24);
|
||||
overflow_table[i].length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16) | (length_bytes[3] << 24);
|
||||
}
|
||||
}
|
||||
|
||||
ut64 string_storage_offset = overflow_table_offset + (h->overflowStringCount * 8);
|
||||
string_storage_offset = (string_storage_offset + 3) & ~3;
|
||||
|
||||
hbc->cached_string_tables.string_count = h->stringCount;
|
||||
hbc->cached_string_tables.small_string_table = small_table;
|
||||
hbc->cached_string_tables.overflow_string_table = overflow_table;
|
||||
hbc->cached_string_tables.string_storage_offset = string_storage_offset;
|
||||
hbc->string_tables_loaded = true;
|
||||
|
||||
free (string_kinds);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Public API Implementation
|
||||
* ============================================================================ */
|
||||
|
||||
HBC *hbc_new_r2(RBinFile *bf) {
|
||||
if (!bf || !bf->buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HBC *hbc = (HBC *)malloc (sizeof (HBC));
|
||||
if (!hbc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset (hbc, 0, sizeof (HBC));
|
||||
hbc->bf = bf;
|
||||
hbc->bin = bf->rbin;
|
||||
hbc->buf = bf->buf;
|
||||
hbc->header_loaded = false;
|
||||
hbc->string_tables_loaded = false;
|
||||
hbc->tmp_read_buffer = NULL;
|
||||
hbc->tmp_buffer_size = 0;
|
||||
|
||||
return hbc;
|
||||
}
|
||||
|
||||
Result hbc_hdr(HBC *hbc, HBCHeader *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (hbc->header_loaded) {
|
||||
memcpy (out, &hbc->cached_header, sizeof (HBCHeader));
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result res = parse_header_from_buffer (hbc->buf, &hbc->cached_header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
hbc->header_loaded = true;
|
||||
memcpy (out, &hbc->cached_header, sizeof (HBCHeader));
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_func_count(HBC *hbc, u32 *out_count) {
|
||||
if (!hbc || !out_count) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
HBCHeader header;
|
||||
Result res = hbc_hdr (hbc, &header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*out_count = header.functionCount;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_func_info(HBC *hbc, u32 function_id, HBCFunc *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (!hbc->bf || !hbc->bf->bo) {
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "No binary object in file");
|
||||
}
|
||||
|
||||
RBinObject *bo = (RBinObject *)hbc->bf->bo;
|
||||
if (!bo->symbols) {
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "No symbols in binary");
|
||||
}
|
||||
|
||||
RListIter *iter;
|
||||
RBinSymbol *sym;
|
||||
r_list_foreach (bo->symbols, iter, sym) {
|
||||
if (sym && sym->ordinal == function_id) {
|
||||
out->name = sym->name? (const char *)sym->name: "unknown";
|
||||
out->offset = sym->paddr;
|
||||
out->size = sym->size;
|
||||
out->param_count = 0;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "Function not found");
|
||||
}
|
||||
|
||||
Result hbc_str_count(HBC *hbc, u32 *out_count) {
|
||||
if (!hbc || !out_count) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
HBCHeader header;
|
||||
Result res = hbc_hdr (hbc, &header);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*out_count = header.stringCount;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_str(HBC *hbc, u32 string_id, const char **out_str) {
|
||||
if (!hbc || !out_str) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (!hbc->bf || !hbc->bf->bo) {
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "No binary object");
|
||||
}
|
||||
|
||||
RBinObject *bo = (RBinObject *)hbc->bf->bo;
|
||||
if (!bo->strings) {
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "No strings");
|
||||
}
|
||||
|
||||
RListIter *iter;
|
||||
RBinString *str;
|
||||
r_list_foreach (bo->strings, iter, str) {
|
||||
if (str && str->ordinal == string_id) {
|
||||
*out_str = str->string;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "String not found");
|
||||
}
|
||||
|
||||
Result hbc_str_meta(HBC *hbc, u32 string_id, HBCStringMeta *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (!hbc->bf || !hbc->bf->bo) {
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "No binary object");
|
||||
}
|
||||
|
||||
RBinObject *bo = (RBinObject *)hbc->bf->bo;
|
||||
if (!bo->strings) {
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "No strings");
|
||||
}
|
||||
|
||||
RListIter *iter;
|
||||
RBinString *str;
|
||||
r_list_foreach (bo->strings, iter, str) {
|
||||
if (str && str->ordinal == string_id) {
|
||||
out->offset = str->paddr;
|
||||
out->length = str->length;
|
||||
out->isUTF16 = false;
|
||||
out->kind = HERMES_STRING_KIND_STRING;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_RESULT (RESULT_ERROR_NOT_FOUND, "String not found");
|
||||
}
|
||||
|
||||
Result hbc_bytecode(HBC *hbc, u32 function_id, const u8 **out_ptr, u32 *out_size) {
|
||||
if (!hbc || !out_ptr || !out_size) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
HBCFunc info;
|
||||
Result res = hbc_func_info (hbc, function_id, &info);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!hbc->tmp_read_buffer || hbc->tmp_buffer_size < info.size) {
|
||||
ut8 *new_buf = (ut8 *)realloc (hbc->tmp_read_buffer, info.size);
|
||||
if (!new_buf) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
|
||||
}
|
||||
hbc->tmp_read_buffer = new_buf;
|
||||
hbc->tmp_buffer_size = info.size;
|
||||
}
|
||||
|
||||
int bytes_read = r_buf_read_at (hbc->buf, info.offset, hbc->tmp_read_buffer, info.size);
|
||||
if (bytes_read != (int)info.size) {
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Cannot read bytecode");
|
||||
}
|
||||
|
||||
*out_ptr = hbc->tmp_read_buffer;
|
||||
*out_size = info.size;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_str_tbl(HBC *hbc, HBCStrs *out) {
|
||||
if (!hbc || !out) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (!hbc->string_tables_loaded) {
|
||||
Result res = parse_string_tables (hbc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*out = hbc->cached_string_tables;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_src(HBC *hbc, u32 function_id, const char **out_src) {
|
||||
if (!hbc || !out_src) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
(void)function_id;
|
||||
*out_src = NULL;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
Result hbc_read(HBC *hbc, u64 offset, u32 size, const u8 **out_ptr) {
|
||||
if (!hbc || !out_ptr) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "NULL pointer");
|
||||
}
|
||||
|
||||
if (!hbc->tmp_read_buffer || hbc->tmp_buffer_size < size) {
|
||||
ut8 *new_buf = (ut8 *)realloc (hbc->tmp_read_buffer, size);
|
||||
if (!new_buf) {
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
|
||||
}
|
||||
hbc->tmp_read_buffer = new_buf;
|
||||
hbc->tmp_buffer_size = size;
|
||||
}
|
||||
|
||||
int bytes_read = r_buf_read_at (hbc->buf, (ut64)offset, hbc->tmp_read_buffer, size);
|
||||
if (bytes_read != (int)size) {
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Cannot read buffer");
|
||||
}
|
||||
|
||||
*out_ptr = hbc->tmp_read_buffer;
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
void hbc_free(HBC *hbc) {
|
||||
if (!hbc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hbc->string_tables_loaded) {
|
||||
free ((void *)hbc->cached_string_tables.small_string_table);
|
||||
free ((void *)hbc->cached_string_tables.overflow_string_table);
|
||||
}
|
||||
|
||||
if (hbc->tmp_read_buffer) {
|
||||
free (hbc->tmp_read_buffer);
|
||||
}
|
||||
|
||||
free (hbc);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/* radare2 - LGPL - Copyright 2025-2026 - pancake */
|
||||
|
||||
#ifndef R2HERMES_UTILS_INC_C
|
||||
#define R2HERMES_UTILS_INC_C
|
||||
|
||||
#include <r_util.h>
|
||||
|
||||
static inline bool r_buf_read_alloc(RBuffer *buf, ut8 **data, ut64 *out_size) {
|
||||
ut64 size = r_buf_size (buf);
|
||||
if (size == 0 || size > SIZE_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ut8 *ptr = malloc (size);
|
||||
if (!ptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((ut64)r_buf_read_at (buf, 0, ptr, size) != size) {
|
||||
free (ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = ptr;
|
||||
if (out_size) {
|
||||
*out_size = size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Result hbc_open_from_buffer(RBuffer *buf, HBC **out_hbc, ut8 **out_data) {
|
||||
if (!buf || !out_hbc) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
|
||||
}
|
||||
|
||||
ut64 size = r_buf_size (buf);
|
||||
if (size == 0 || size > SIZE_MAX) {
|
||||
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid buffer size");
|
||||
}
|
||||
|
||||
ut8 *data = NULL;
|
||||
if (!r_buf_read_alloc (buf, &data, NULL)) {
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Failed to read buffer");
|
||||
}
|
||||
|
||||
Result res = hbc_open_from_memory (data, size, out_hbc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
free (data);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (out_data) {
|
||||
*out_data = data;
|
||||
} else {
|
||||
free (data);
|
||||
}
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
static inline void hbc_safe_close(HBC **hbc) {
|
||||
if (hbc && *hbc) {
|
||||
hbc_close (*hbc);
|
||||
*hbc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hbc_free_data_and_close(HBC **hbc, ut8 **data) {
|
||||
if (data && *data) {
|
||||
R_FREE (*data);
|
||||
}
|
||||
hbc_safe_close (hbc);
|
||||
}
|
||||
|
||||
#endif
|
||||
+91
-178
@@ -1,6 +1,8 @@
|
||||
#include <hbc/common.h>
|
||||
#include <hbc/hbc.h>
|
||||
#include <hbc/decompilation/literals.h>
|
||||
#include <hbc/decompilation/decompiler.h>
|
||||
#include <hbc/disasm.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
@@ -168,121 +170,50 @@ static Result parse_hex_bytes(const char *s, u8 *out, size_t out_cap, size_t *ou
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
static Result parse_output_and_disasm_options(int argc, char **argv, const char **out_path, HBCDisOptions *opt) {
|
||||
*out_path = NULL;
|
||||
*opt = (HBCDisOptions){ 0 };
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char *a = argv[i];
|
||||
if (!a) {
|
||||
continue;
|
||||
}
|
||||
if (a[0] != '-') {
|
||||
if (*out_path) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", a);
|
||||
}
|
||||
*out_path = a;
|
||||
continue;
|
||||
}
|
||||
if (streq (a, "--verbose") || streq (a, "-v")) {
|
||||
opt->verbose = true;
|
||||
} else if (streq (a, "--json") || streq (a, "-j")) {
|
||||
opt->output_json = true;
|
||||
} else if (streq (a, "--bytecode") || streq (a, "-b")) {
|
||||
opt->show_bytecode = true;
|
||||
} else if (streq (a, "--debug") || streq (a, "-d")) {
|
||||
opt->show_debug_info = true;
|
||||
} else if (streq (a, "--asmsyntax")) {
|
||||
opt->asm_syntax = true;
|
||||
} else {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unknown option: %s", a);
|
||||
}
|
||||
}
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
static Result parse_output_and_decompile_options(int argc, char **argv, const char **out_path, HBCDecompOptions *opt) {
|
||||
*out_path = NULL;
|
||||
*opt = (HBCDecompOptions){
|
||||
.pretty_literals = LITERALS_PRETTY_AUTO,
|
||||
.suppress_comments = false,
|
||||
.force_dispatch = false,
|
||||
.inline_closures = true,
|
||||
.inline_threshold = 0
|
||||
};
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char *a = argv[i];
|
||||
if (!a) {
|
||||
continue;
|
||||
}
|
||||
if (a[0] != '-') {
|
||||
if (*out_path) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", a);
|
||||
}
|
||||
*out_path = a;
|
||||
continue;
|
||||
}
|
||||
if (streq (a, "--pretty-literals") || streq (a, "-P")) {
|
||||
opt->pretty_literals = true;
|
||||
} else if (streq (a, "--no-pretty-literals") || streq (a, "-N")) {
|
||||
opt->pretty_literals = false;
|
||||
} else if (streq (a, "--pretty-auto")) {
|
||||
} else if (streq (a, "--no-comments") || streq (a, "-C")) {
|
||||
opt->suppress_comments = true;
|
||||
} else {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unknown option: %s", a);
|
||||
}
|
||||
}
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
static Result cmd_d(const CliContext *ctx, int argc, char **argv) {
|
||||
(void)ctx;
|
||||
if (argc < 1) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Usage: %s d <input_file> [options] [output_file]", ctx->program_name);
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Usage: %s d <input_file>", ctx->program_name);
|
||||
}
|
||||
const char *input = argv[0];
|
||||
const char *output = NULL;
|
||||
HBCDisOptions opt = { 0 };
|
||||
RETURN_IF_ERROR (parse_output_and_disasm_options (argc - 1, argv + 1, &output, &opt));
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return ERROR_RESULT (RESULT_ERROR_READ, "Failed to open file: %s");
|
||||
}
|
||||
char *disasm_str = NULL;
|
||||
Result r = hbc_disasm_all (hbc, opt, &disasm_str);
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return r;
|
||||
|
||||
// Just dump function info using printf directly since we don't need the complex provider API
|
||||
u32 func_count = hbc_function_count (hbc);
|
||||
printf ("HBC Disassembly Output:\n");
|
||||
printf ("=====================\n\n");
|
||||
printf ("Total functions: %u\n\n", func_count);
|
||||
|
||||
for (u32 i = 0; i < func_count; i++) {
|
||||
HBCFunc fi;
|
||||
res = hbc_get_function_info (hbc, i, &fi);
|
||||
if (res.code == RESULT_SUCCESS) {
|
||||
printf ("Function %u: %s\n", i, fi.name? fi.name: "unnamed");
|
||||
printf (" Offset: 0x%08x\n", fi.offset);
|
||||
printf (" Size: %u bytes\n", fi.size);
|
||||
printf (" Params: %u\n\n", fi.param_count);
|
||||
}
|
||||
}
|
||||
r = write_text (output, disasm_str);
|
||||
free (disasm_str);
|
||||
hbc_free (hbc);
|
||||
return r;
|
||||
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
static Result cmd_c(const CliContext *ctx, int argc, char **argv) {
|
||||
(void)ctx;
|
||||
if (argc < 1) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Usage: %s c <input_file> [options] [output_file]", ctx->program_name);
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Usage: %s c <input_file>", ctx->program_name);
|
||||
}
|
||||
const char *input = argv[0];
|
||||
const char *output = NULL;
|
||||
HBCDecompOptions opt = { 0 };
|
||||
RETURN_IF_ERROR (parse_output_and_decompile_options (argc - 1, argv + 1, &output, &opt));
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
char *decomp_str = NULL;
|
||||
Result r = hbc_decomp_all (hbc, opt, &decomp_str);
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return r;
|
||||
}
|
||||
r = write_text (output, decomp_str);
|
||||
free (decomp_str);
|
||||
hbc_free (hbc);
|
||||
return r;
|
||||
// Use the internal API that handles file I/O
|
||||
Result res = _hbc_decompile_file (input, NULL);
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result cmd_dis(const CliContext *ctx, int argc, char **argv) {
|
||||
@@ -304,7 +235,7 @@ static Result cmd_dis(const CliContext *ctx, int argc, char **argv) {
|
||||
|
||||
HBCInsnInfo sinfo;
|
||||
memset (&sinfo, 0, sizeof (sinfo));
|
||||
Result r = hbc_dec_insn (bytes, bcount, 96, 0, asm_syntax, false, NULL, &sinfo);
|
||||
Result r = hbc_dec_insn (bytes, bcount, 96, 0, asm_syntax, false, NULL, NULL, &sinfo);
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
@@ -384,7 +315,7 @@ static Result cmd_r(const CliContext *ctx, int argc, char **argv) {
|
||||
if (argc > 2) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
return hbc_generate_r2_script (input, output);
|
||||
return _hbc_generate_r2_script (input, output);
|
||||
}
|
||||
|
||||
static Result cmd_v(const CliContext *ctx, int argc, char **argv) {
|
||||
@@ -398,16 +329,17 @@ static Result cmd_v(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result r = hbc_open (input, &hbc);
|
||||
if (r.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
|
||||
/* Get header to verify validity */
|
||||
HBCHeader header;
|
||||
Result r = hbc_hdr (hbc, &header);
|
||||
r = hbc_get_header (hbc, &header);
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -415,7 +347,7 @@ static Result cmd_v(const CliContext *ctx, int argc, char **argv) {
|
||||
StringBuffer sb;
|
||||
r = _hbc_string_buffer_init (&sb, 1024);
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -438,7 +370,7 @@ static Result cmd_v(const CliContext *ctx, int argc, char **argv) {
|
||||
|
||||
r = write_text (output, sb.data);
|
||||
_hbc_string_buffer_free (&sb);
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -453,14 +385,15 @@ static Result cmd_h(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result r = hbc_open (input, &hbc);
|
||||
if (r.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
HBCHeader hh;
|
||||
Result r = hbc_hdr (hbc, &hh);
|
||||
r = hbc_get_header (hbc, &hh);
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -468,7 +401,7 @@ static Result cmd_h(const CliContext *ctx, int argc, char **argv) {
|
||||
if (output) {
|
||||
out = fopen (output, "w");
|
||||
if (!out) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return errorf (RESULT_ERROR_FILE_NOT_FOUND, "Failed to open output file: %s", output);
|
||||
}
|
||||
}
|
||||
@@ -509,7 +442,7 @@ static Result cmd_h(const CliContext *ctx, int argc, char **argv) {
|
||||
if (output) {
|
||||
fclose (out);
|
||||
}
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
@@ -530,25 +463,21 @@ static Result cmd_f(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
u32 fc;
|
||||
Result res = hbc_func_count (hbc, &fc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return res;
|
||||
}
|
||||
u32 fc = hbc_function_count (hbc);
|
||||
u32 count = fc < n? fc: n;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
HBCFunc fi;
|
||||
if (hbc_func_info (hbc, i, &fi).code != RESULT_SUCCESS) {
|
||||
if (hbc_get_function_info (hbc, i, &fi).code != RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
printf ("id=%u offset=0x%08x size=%u name=%s\n", i, fi.offset, fi.size, fi.name? fi.name: "");
|
||||
}
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
@@ -569,22 +498,18 @@ static Result cmd_cmp(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
u32 fc;
|
||||
Result res = hbc_func_count (hbc, &fc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return res;
|
||||
}
|
||||
u32 fc = hbc_function_count (hbc);
|
||||
u32 count = fc < n? fc: n;
|
||||
|
||||
const char *py_path = "parser.txt";
|
||||
FILE *py = fopen (py_path, "r");
|
||||
if (!py) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return errorf (RESULT_ERROR_FILE_NOT_FOUND, "could not open %s", py_path);
|
||||
}
|
||||
|
||||
@@ -592,7 +517,7 @@ static Result cmd_cmp(const CliContext *ctx, int argc, char **argv) {
|
||||
u32 *py_offs = (u32 *)calloc (count, sizeof (u32));
|
||||
if (!py_sizes || !py_offs) {
|
||||
fclose (py);
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
free (py_sizes);
|
||||
free (py_offs);
|
||||
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "OOM");
|
||||
@@ -636,7 +561,7 @@ static Result cmd_cmp(const CliContext *ctx, int argc, char **argv) {
|
||||
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
HBCFunc fi;
|
||||
if (hbc_func_info (hbc, i, &fi).code != RESULT_SUCCESS) {
|
||||
if (hbc_get_function_info (hbc, i, &fi).code != RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
u32 po = py_offs[i];
|
||||
@@ -646,7 +571,7 @@ static Result cmd_cmp(const CliContext *ctx, int argc, char **argv) {
|
||||
}
|
||||
free (py_sizes);
|
||||
free (py_offs);
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
@@ -658,14 +583,14 @@ static Result cmd_cf(const CliContext *ctx, int argc, char **argv) {
|
||||
const char *python_dis_file = argv[1];
|
||||
u32 function_id = (u32)strtoul (argv[2], NULL, 0);
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
u32 fc;
|
||||
Result res = hbc_func_count (hbc, &fc);
|
||||
if (res.code != RESULT_SUCCESS || function_id >= fc) {
|
||||
hbc_free (hbc);
|
||||
u32 fc = hbc_function_count (hbc);
|
||||
if (function_id >= fc) {
|
||||
hbc_close (hbc);
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Invalid function id %u", function_id);
|
||||
}
|
||||
|
||||
@@ -673,14 +598,14 @@ static Result cmd_cf(const CliContext *ctx, int argc, char **argv) {
|
||||
char *disasm_str = NULL;
|
||||
res = hbc_disasm_fn (hbc, function_id, opt, &disasm_str);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return res;
|
||||
}
|
||||
|
||||
FILE *py = fopen (python_dis_file, "r");
|
||||
if (!py) {
|
||||
free (disasm_str);
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return errorf (RESULT_ERROR_FILE_NOT_FOUND, "could not open %s", python_dis_file);
|
||||
}
|
||||
|
||||
@@ -717,7 +642,7 @@ static Result cmd_cf(const CliContext *ctx, int argc, char **argv) {
|
||||
}
|
||||
fclose (py);
|
||||
free (disasm_str);
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
@@ -735,24 +660,20 @@ static Result cmd_s(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
u32 sc;
|
||||
Result res = hbc_str_count (hbc, &sc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return res;
|
||||
}
|
||||
u32 sc = hbc_string_count (hbc);
|
||||
if ((u32)idx >= sc) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Index out of range (max %u)", sc);
|
||||
}
|
||||
const char *s = NULL;
|
||||
hbc_str (hbc, (u32)idx, &s);
|
||||
hbc_get_string (hbc, (u32)idx, &s);
|
||||
printf ("idx=%ld name=%s\n", idx, s? s: "");
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
@@ -767,24 +688,20 @@ static Result cmd_fs(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
u32 sc;
|
||||
Result res = hbc_str_count (hbc, &sc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return res;
|
||||
}
|
||||
u32 sc = hbc_string_count (hbc);
|
||||
for (u32 i = 0; i < sc; i++) {
|
||||
const char *s = NULL;
|
||||
hbc_str (hbc, i, &s);
|
||||
hbc_get_string (hbc, i, &s);
|
||||
if (s && strstr (s, needle)) {
|
||||
printf ("idx=%u name=%s\n", i, s);
|
||||
}
|
||||
}
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
@@ -802,24 +719,20 @@ static Result cmd_sm(const CliContext *ctx, int argc, char **argv) {
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Unexpected argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
HBC *hbc = hbc_new_file (input);
|
||||
if (!hbc) {
|
||||
HBC *hbc = NULL;
|
||||
Result res = hbc_open (input, &hbc);
|
||||
if (res.code != RESULT_SUCCESS || !hbc) {
|
||||
return errorf (RESULT_ERROR_READ, "Failed to open file: %s", input);
|
||||
}
|
||||
u32 sc;
|
||||
Result res = hbc_str_count (hbc, &sc);
|
||||
if (res.code != RESULT_SUCCESS) {
|
||||
hbc_free (hbc);
|
||||
return res;
|
||||
}
|
||||
u32 sc = hbc_string_count (hbc);
|
||||
if ((u32)idx >= sc) {
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return errorf (RESULT_ERROR_INVALID_ARGUMENT, "Index out of range (max %u)", sc);
|
||||
}
|
||||
HBCStringMeta sm;
|
||||
hbc_str_meta (hbc, (u32)idx, &sm);
|
||||
hbc_get_string_meta (hbc, (u32)idx, &sm);
|
||||
printf ("idx=%ld isUTF16=%u off=0x%x len=%u\n", idx, sm.isUTF16? 1u: 0u, sm.offset, sm.length);
|
||||
hbc_free (hbc);
|
||||
hbc_close (hbc);
|
||||
return SUCCESS_RESULT ();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ EXPECT=<<EOF
|
||||
hbc
|
||||
;-- entry0:
|
||||
;-- global_0x000000b0:
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0x100000a2 ; 0x1000013e
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0xa2 ; "print(123);"
|
||||
0x000000b4 5e000000 direct_eval r0, r0, 0
|
||||
EOF
|
||||
RUN
|
||||
+10
-16
@@ -7,7 +7,7 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
;-- entry0:
|
||||
;-- global_0x000000b0:
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0x100000a2 ; 0x1000013e
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0xa2 ; "print(123);"
|
||||
0x000000b4 5e000000 direct_eval r0, r0, 0
|
||||
0x000000b8 5c00 ret r0
|
||||
EOF
|
||||
@@ -91,7 +91,7 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
;-- entry0:
|
||||
;-- global_0x000000b0:
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0x100000a2 ; 0x1000013e
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0xa2 ; "print(123);"
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -128,7 +128,7 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
;-- entry0:
|
||||
;-- global_0x000000b0:
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0x100000a2 ; 0x1000013e
|
||||
0x000000b0 73000100 load_const_string r0, str.print_123__ ; 0xa2 ; "print(123);"
|
||||
0x000000b4 5e000000 direct_eval r0, r0, 0
|
||||
EOF
|
||||
RUN
|
||||
@@ -185,10 +185,10 @@ NAME=hbc try_get_by_id instruction
|
||||
FILE=bins/hbc/index.android.bundle
|
||||
CMDS=<<EOF
|
||||
s 0x3e512
|
||||
pd 1
|
||||
ao~mnemonic
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x0003e512 390000011400 try_get_by_id r0, r0, 1, str.Map ; 0x1001eae6 ; 0x1003d2b6
|
||||
mnemonic: try_get_by_id
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -196,10 +196,10 @@ NAME=hbc get_by_id_short instruction
|
||||
FILE=bins/hbc/index.android.bundle
|
||||
CMDS=<<EOF
|
||||
s 0x3e518
|
||||
pd 1
|
||||
ao~mnemonic
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x0003e518 36010002d8 get_by_id_short r1, r0, 2, str.prototype ; 0x10022097 ; 0x10040867
|
||||
mnemonic: get_by_id_short
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -273,19 +273,13 @@ NAME=hbc complete function disasm
|
||||
FILE=bins/hbc/index.android.bundle
|
||||
CMDS=<<EOF
|
||||
s 0x3e510
|
||||
pd 10
|
||||
pd 3~get_global
|
||||
pd 1 @ 0x3e51d~create_this
|
||||
pd 1 @ 0x3e533~ret
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
;-- clear_0x0003e510:
|
||||
0x0003e510 3000 get_global_object r0
|
||||
0x0003e512 390000011400 try_get_by_id r0, r0, 1, str.Map ; 0x1001eae6 ; 0x1003d2b6
|
||||
0x0003e518 36010002d8 get_by_id_short r1, r0, 2, str.prototype ; 0x10022097 ; 0x10040867
|
||||
0x0003e51d 6a010100 create_this r1, r1, r0
|
||||
0x0003e521 080201 mov r2, r1
|
||||
0x0003e524 50000001 construct r0, r0, 1
|
||||
0x0003e528 6b000100 select_object r0, r1, r0
|
||||
0x0003e52c 290100 get_environment r1, 0
|
||||
0x0003e52f 2a010100 store_to_environment r1, 1, r0
|
||||
0x0003e533 5c00 ret r0
|
||||
EOF
|
||||
RUN
|
||||
|
||||
Reference in new issue
Block a user