|
| 1 | +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]> |
| 2 | +// SPDX-License-Identifier: BSD-3-Clause |
| 3 | + |
| 4 | +#include <stdlib.h> |
| 5 | +#include <string.h> |
| 6 | +#include <rz_types.h> |
| 7 | +#include <rz_lib.h> |
| 8 | +#include <rz_cmd.h> |
| 9 | +#include <rz_core.h> |
| 10 | +#include <rz_cons.h> |
| 11 | +#include <rz_analysis.h> |
| 12 | + |
| 13 | +#include "jsdec.h" |
| 14 | +#include "jsdec-cutter.h" |
| 15 | + |
| 16 | +typedef struct rz_core_t RzCore; |
| 17 | + |
| 18 | +typedef struct exec_context_t { |
| 19 | + RzCore *core; |
| 20 | + void *bed; |
| 21 | + RzStrBuf *anno; |
| 22 | + JSValue shared; |
| 23 | +} ExecContext; |
| 24 | + |
| 25 | +static JSValue js_analysis_bytes(JSContext *ctx, RzCore *core, RzAnalysisBytes *ab) { |
| 26 | + RzAnalysisOp *aop = ab->op; |
| 27 | + JSValue op = JS_NewObject(ctx); |
| 28 | + JS_SetPropertyStr(ctx, op, "offset", JS_NewBigUint64(ctx, aop->addr)); |
| 29 | + if (aop->ptr != UT64_MAX) { |
| 30 | + JS_SetPropertyStr(ctx, op, "ptr", JS_NewBigUint64(ctx, aop->ptr)); |
| 31 | + } |
| 32 | + if (aop->val != UT64_MAX) { |
| 33 | + JS_SetPropertyStr(ctx, op, "val", JS_NewBigUint64(ctx, aop->val)); |
| 34 | + } |
| 35 | + JS_SetPropertyStr(ctx, op, "opcode", JS_NewString(ctx, rz_str_get_null(ab->opcode))); |
| 36 | + JS_SetPropertyStr(ctx, op, "disasm", JS_NewString(ctx, rz_str_get_null(ab->disasm))); |
| 37 | + JS_SetPropertyStr(ctx, op, "type", JS_NewString(ctx, rz_analysis_optype_to_string(aop->type))); |
| 38 | + if (aop->jump != UT64_MAX) { |
| 39 | + JS_SetPropertyStr(ctx, op, "jump", JS_NewBigInt64(ctx, aop->jump)); |
| 40 | + } |
| 41 | + if (aop->fail != UT64_MAX) { |
| 42 | + JS_SetPropertyStr(ctx, op, "fail", JS_NewBigUint64(ctx, aop->fail)); |
| 43 | + } |
| 44 | + const char *comment = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, aop->addr); |
| 45 | + if (RZ_STR_ISNOTEMPTY(comment)) { |
| 46 | + JS_SetPropertyStr(ctx, op, "comment", JS_NewString(ctx, comment)); |
| 47 | + } |
| 48 | + return op; |
| 49 | +} |
| 50 | + |
| 51 | +static JSValue js_analysis_opcodes(JSContext *ctx, RzCore *core) { |
| 52 | + RzAnalysisBytes *ab; |
| 53 | + JSValue ops = JS_NewArray(ctx); |
| 54 | + st64 op_idx = 0; |
| 55 | + |
| 56 | + RzIterator *iter = rz_core_analysis_bytes(core, core->offset, core->block, core->blocksize, 0); |
| 57 | + if (!iter) { |
| 58 | + return ops; |
| 59 | + } |
| 60 | + rz_iterator_foreach(iter, ab) { |
| 61 | + if (!ab || !ab->op || !strcmp(ab->opcode, "nop")) { |
| 62 | + continue; |
| 63 | + } |
| 64 | + JSValue op = js_analysis_bytes(ctx, core, ab); |
| 65 | + JS_SetPropertyInt64(ctx, ops, op_idx, op); |
| 66 | + op_idx++; |
| 67 | + } |
| 68 | + rz_iterator_free(iter); |
| 69 | + return ops; |
| 70 | +} |
| 71 | + |
| 72 | +static JSValue js_function_bbs(JSContext *ctx, RzCore *core, RzAnalysisFunction *fcn) { |
| 73 | + JSValue bbs = JS_NewArray(ctx); |
| 74 | + RzAnalysisBlock *bbi; |
| 75 | + RzListIter *iter; |
| 76 | + st64 bbs_idx = 0; |
| 77 | + ut64 old_offset = core->offset; |
| 78 | + ut64 old_bsize = core->blocksize; |
| 79 | + rz_list_foreach (fcn->bbs, iter, bbi) { |
| 80 | + rz_core_block_size(core, bbi->size); |
| 81 | + rz_core_seek(core, bbi->addr, true); |
| 82 | + |
| 83 | + JSValue bb = JS_NewObject(ctx); |
| 84 | + JS_SetPropertyStr(ctx, bb, "address", JS_NewBigUint64(ctx, bbi->addr)); |
| 85 | + if (bbi->jump != UT64_MAX) { |
| 86 | + JS_SetPropertyStr(ctx, bb, "jump", JS_NewBigUint64(ctx, bbi->jump)); |
| 87 | + } |
| 88 | + if (bbi->fail != UT64_MAX) { |
| 89 | + JS_SetPropertyStr(ctx, bb, "fail", JS_NewBigUint64(ctx, bbi->fail)); |
| 90 | + } |
| 91 | + JSValue ops = js_analysis_opcodes(ctx, core); |
| 92 | + JS_SetPropertyStr(ctx, bb, "ops", ops); |
| 93 | + JS_SetPropertyInt64(ctx, bbs, bbs_idx, bb); |
| 94 | + bbs_idx++; |
| 95 | + } |
| 96 | + rz_core_block_size(core, old_bsize); |
| 97 | + rz_core_seek(core, old_offset, true); |
| 98 | + return bbs; |
| 99 | +} |
| 100 | + |
| 101 | +static JSValue js_command(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { |
| 102 | + if (argc != 1) { |
| 103 | + return JS_EXCEPTION; |
| 104 | + } |
| 105 | + |
| 106 | + const char *command = JS_ToCString(ctx, argv[0]); |
| 107 | + if (!command) { |
| 108 | + return JS_EXCEPTION; |
| 109 | + } |
| 110 | + |
| 111 | + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); |
| 112 | + RzCore *core = ectx->core; |
| 113 | + rz_cons_sleep_end(ectx->bed); |
| 114 | + |
| 115 | + char *output = rz_core_cmd_str(core, command); |
| 116 | + JS_FreeCString(ctx, command); |
| 117 | + JSValue result = JS_NewString(ctx, output ? output : ""); |
| 118 | + free(output); |
| 119 | + |
| 120 | + ectx->bed = rz_cons_sleep_begin(); |
| 121 | + return result; |
| 122 | +} |
| 123 | + |
| 124 | +static JSValue js_graph(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { |
| 125 | + if (argc != 0) { |
| 126 | + return JS_EXCEPTION; |
| 127 | + } |
| 128 | + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); |
| 129 | + RzCore *core = ectx->core; |
| 130 | + rz_cons_sleep_end(ectx->bed); |
| 131 | + |
| 132 | + RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1); |
| 133 | + if (!fcn) { |
| 134 | + ectx->bed = rz_cons_sleep_begin(); |
| 135 | + return JS_ThrowInternalError(ctx, "Cannot find function at 0x%08" PFMT64x, core->offset); |
| 136 | + } |
| 137 | + |
| 138 | + JSValue graph = JS_NewArray(ctx); |
| 139 | + JSValue object = JS_NewObject(ctx); |
| 140 | + JS_SetPropertyInt64(ctx, graph, 0, object); |
| 141 | + JS_SetPropertyStr(ctx, object, "name", JS_NewString(ctx, rz_str_get_null(fcn->name))); |
| 142 | + JSValue blocks = js_function_bbs(ctx, core, fcn); |
| 143 | + JS_SetPropertyStr(ctx, object, "blocks", blocks); |
| 144 | + ectx->bed = rz_cons_sleep_begin(); |
| 145 | + return graph; |
| 146 | +} |
| 147 | + |
| 148 | +static JSValue js_console_log(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { |
| 149 | + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); |
| 150 | + for (int i = 0; i < argc; ++i) { |
| 151 | + if (i != 0) { |
| 152 | + rz_strbuf_append_n(ectx->anno, " ", 1); |
| 153 | + } |
| 154 | + const char *str = JS_ToCString(ctx, argv[i]); |
| 155 | + if (!str) { |
| 156 | + return JS_EXCEPTION; |
| 157 | + } |
| 158 | + rz_strbuf_append(ectx->anno, str); |
| 159 | + JS_FreeCString(ctx, str); |
| 160 | + } |
| 161 | + rz_strbuf_append_n(ectx->anno, "\n", 1); |
| 162 | + return JS_UNDEFINED; |
| 163 | +} |
| 164 | + |
| 165 | +static JSValue js_get_global(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { |
| 166 | + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); |
| 167 | + return JS_GetPropertyStr(ctx, ectx->shared, "Shared"); |
| 168 | +} |
| 169 | + |
| 170 | +static jsdec_t *jsdec_create(ExecContext *ec) { |
| 171 | + jsdec_t *dec = jsdec_new(); |
| 172 | + if (!dec) { |
| 173 | + return NULL; |
| 174 | + } |
| 175 | + |
| 176 | + JSContext *ctx = jsdec_context(dec); |
| 177 | + JS_SetContextOpaque(ctx, ec); |
| 178 | + ec->shared = JS_NewObject(ctx); |
| 179 | + JS_SetPropertyStr(ctx, ec->shared, "Shared", JS_NewObject(ctx)); |
| 180 | + |
| 181 | + JSValue global = JS_GetGlobalObject(ctx); |
| 182 | + JS_SetPropertyStr(ctx, global, "Global", JS_NewCFunction(ctx, js_get_global, "Global", 1)); |
| 183 | + |
| 184 | + JSValue console = JS_NewObject(ctx); |
| 185 | + JS_SetPropertyStr(ctx, global, "console", console); |
| 186 | + JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, js_console_log, "log", 1)); |
| 187 | + |
| 188 | + JSValue rizin = JS_NewObject(ctx); |
| 189 | + JS_SetPropertyStr(ctx, global, "rizin", rizin); |
| 190 | + JS_SetPropertyStr(ctx, rizin, "command", JS_NewCFunction(ctx, js_command, "command", 1)); |
| 191 | + JS_SetPropertyStr(ctx, rizin, "graph", JS_NewCFunction(ctx, js_graph, "graph", 1)); |
| 192 | + |
| 193 | + JSValue process = JS_NewObject(ctx); |
| 194 | + JS_SetPropertyStr(ctx, global, "process", process); |
| 195 | + JSValue args = JS_NewArray(ctx); |
| 196 | + JS_SetPropertyInt64(ctx, args, 0, JS_NewString(ctx, "--annotation")); |
| 197 | + JS_SetPropertyStr(ctx, process, "args", args); |
| 198 | + |
| 199 | + JS_FreeValue(ctx, global); |
| 200 | + return dec; |
| 201 | +} |
| 202 | + |
| 203 | +static void jsdec_destroy(jsdec_t *dec, ExecContext *ec) { |
| 204 | + JSContext *ctx = jsdec_context(dec); |
| 205 | + JS_FreeValue(ctx, ec->shared); |
| 206 | + jsdec_free(dec); |
| 207 | +} |
| 208 | + |
| 209 | +static char *json_to_strdup(const RzJson *object, const char *key) { |
| 210 | + const RzJson *j = rz_json_get(object, key); |
| 211 | + if (!j || j->type != RZ_JSON_STRING) { |
| 212 | + return NULL; |
| 213 | + } |
| 214 | + return rz_str_dup(j->str_value); |
| 215 | +} |
| 216 | + |
| 217 | +static const char *json_as_string(const RzJson *object, const char *key) { |
| 218 | + const RzJson *j = rz_json_get(object, key); |
| 219 | + if (!j || j->type != RZ_JSON_STRING) { |
| 220 | + return NULL; |
| 221 | + } |
| 222 | + return j->str_value; |
| 223 | +} |
| 224 | + |
| 225 | +static ut64 json_as_ut64(const RzJson *object, const char *key) { |
| 226 | + const RzJson *j = rz_json_get(object, key); |
| 227 | + if (!j || (j->type != RZ_JSON_STRING && j->type != RZ_JSON_INTEGER)) { |
| 228 | + return 0; |
| 229 | + } |
| 230 | + if (j->type == RZ_JSON_INTEGER) { |
| 231 | + return j->num.u_value; |
| 232 | + } else if (rz_num_is_valid_input(NULL, j->str_value)) { |
| 233 | + return rz_num_get_input_value(NULL, j->str_value); |
| 234 | + } |
| 235 | + return 0; |
| 236 | +} |
| 237 | + |
| 238 | +static RzAnnotatedCode *parse_json(RzStrBuf *sb) { |
| 239 | + char *text = rz_strbuf_drain(sb); |
| 240 | + if (RZ_STR_ISEMPTY(text)) { |
| 241 | + free(text); |
| 242 | + return NULL; |
| 243 | + } |
| 244 | + |
| 245 | + // text is modified by rz_json_parse and |
| 246 | + // is freed by rz_json_free |
| 247 | + RzJson *json = rz_json_parse(text); |
| 248 | + if (!json) { |
| 249 | + RZ_LOG_ERROR("failed to parse from json string\n"); |
| 250 | + return NULL; |
| 251 | + } |
| 252 | + |
| 253 | + char *raw_code = json_to_strdup(json, "code"); |
| 254 | + if (!raw_code) { |
| 255 | + rz_json_free(json); |
| 256 | + RZ_LOG_ERROR("failed to dup code\n"); |
| 257 | + return NULL; |
| 258 | + } |
| 259 | + |
| 260 | + RzAnnotatedCode *code = rz_annotated_code_new(raw_code); |
| 261 | + if (!code) { |
| 262 | + rz_json_free(json); |
| 263 | + RZ_LOG_ERROR("failed to create RzAnnotatedCode\n"); |
| 264 | + return NULL; |
| 265 | + } |
| 266 | + |
| 267 | + const RzJson *annotations = rz_json_get(json, "annotations"); |
| 268 | + const RzJson *element = NULL; |
| 269 | + for (size_t idx = 0; annotations && (element = rz_json_item(annotations, idx)); idx++) { |
| 270 | + RzCodeAnnotation annotation = { 0 }; |
| 271 | + annotation.start = json_as_ut64(element, "start"); |
| 272 | + annotation.end = json_as_ut64(element, "end"); |
| 273 | + const char *type = json_as_string(element, "type"); |
| 274 | + if (!strcmp(type, "offset")) { |
| 275 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_OFFSET; |
| 276 | + annotation.offset.offset = json_as_ut64(element, "offset"); |
| 277 | + } else if (!strcmp(type, "function_name")) { |
| 278 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME; |
| 279 | + annotation.reference.name = json_to_strdup(element, "name"); |
| 280 | + annotation.reference.offset = json_as_ut64(element, "offset"); |
| 281 | + } else if (!strcmp(type, "global_variable")) { |
| 282 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE; |
| 283 | + annotation.reference.offset = json_as_ut64(element, "offset"); |
| 284 | + } else if (!strcmp(type, "constant_variable")) { |
| 285 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE; |
| 286 | + annotation.reference.offset = json_as_ut64(element, "offset"); |
| 287 | + } else if (!strcmp(type, "local_variable")) { |
| 288 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE; |
| 289 | + annotation.variable.name = json_to_strdup(element, "name"); |
| 290 | + } else if (!strcmp(type, "function_parameter")) { |
| 291 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER; |
| 292 | + annotation.variable.name = json_to_strdup(element, "name"); |
| 293 | + } else if (!strcmp(type, "syntax_highlight")) { |
| 294 | + annotation.type = RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT; |
| 295 | + const char *highlightType = json_as_string(element, "syntax_highlight"); |
| 296 | + if (!strcmp(highlightType, "keyword")) { |
| 297 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD; |
| 298 | + } else if (!strcmp(highlightType, "comment")) { |
| 299 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT; |
| 300 | + } else if (!strcmp(highlightType, "datatype")) { |
| 301 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE; |
| 302 | + } else if (!strcmp(highlightType, "function_name")) { |
| 303 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME; |
| 304 | + } else if (!strcmp(highlightType, "function_parameter")) { |
| 305 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER; |
| 306 | + } else if (!strcmp(highlightType, "local_variable")) { |
| 307 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE; |
| 308 | + } else if (!strcmp(highlightType, "constant_variable")) { |
| 309 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE; |
| 310 | + } else if (!strcmp(highlightType, "global_variable")) { |
| 311 | + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE; |
| 312 | + } |
| 313 | + } |
| 314 | + rz_annotated_code_add_annotation(code, &annotation); |
| 315 | + } |
| 316 | + |
| 317 | + rz_json_free(json); |
| 318 | + return code; |
| 319 | +} |
| 320 | + |
| 321 | +RzAnnotatedCode *jsdec_as_annotation(RzCore *core, ut64 addr) { |
| 322 | + ExecContext ectx; |
| 323 | + ectx.core = core; |
| 324 | + ectx.anno = rz_strbuf_new(""); |
| 325 | + if (!ectx.anno) { |
| 326 | + rz_strbuf_free(ectx.anno); |
| 327 | + RZ_LOG_ERROR("failed to create RzStrBuf\n"); |
| 328 | + return NULL; |
| 329 | + } |
| 330 | + |
| 331 | + jsdec_t *dec = jsdec_create(&ectx); |
| 332 | + if (!dec) { |
| 333 | + rz_strbuf_free(ectx.anno); |
| 334 | + RZ_LOG_ERROR("failed to create jsdec_t\n"); |
| 335 | + return NULL; |
| 336 | + } |
| 337 | + |
| 338 | + ut64 offset = core->offset; |
| 339 | + if (offset != addr) { |
| 340 | + rz_core_seek(core, addr, true); |
| 341 | + } |
| 342 | + |
| 343 | + ectx.bed = rz_cons_sleep_begin(); |
| 344 | + bool ret = jsdec_run(dec); |
| 345 | + rz_cons_sleep_end(ectx.bed); |
| 346 | + |
| 347 | + if (offset != addr) { |
| 348 | + rz_core_seek(core, offset, true); |
| 349 | + } |
| 350 | + |
| 351 | + jsdec_destroy(dec, &ectx); |
| 352 | + if (!ret) { |
| 353 | + rz_strbuf_free(ectx.anno); |
| 354 | + RZ_LOG_ERROR("jsdec_run returned false\n"); |
| 355 | + return NULL; |
| 356 | + } |
| 357 | + |
| 358 | + return parse_json(ectx.anno); |
| 359 | +} |
0 commit comments