Skip to content

Commit 906664e

Browse files
authored
Create cutter native plugin (#52)
1 parent 0f966e3 commit 906664e

12 files changed

+549
-12
lines changed

.ci-scripts/ci-build-linux.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ rm -rf "rizin-*"
2828
cd "$CI_JSDEC"
2929

3030
# build jsdec and install in the rizin dir.
31-
meson setup --buildtype=release -Dstandalone=false build
31+
meson setup --buildtype=release -Dbuild_type=rizin build
3232
sudo ninja -C build install
3333

3434
# check if it was installed correctly and try to run it.

.reuse/dep5

+4
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,7 @@ License: BSD-3-Clause
4646
Files: .clang-format
4747
Copyright: 2021 Giovanni Dante Grazioli <[email protected]>
4848
License: BSD-3-Clause
49+
50+
Files: cutter-plugin/CMakeLists.txt
51+
Copyright: 2024 Giovanni Dante Grazioli <[email protected]>
52+
License: BSD-3-Clause

c/jsdec-cutter.c

+359
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
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+
}

c/jsdec-cutter.h

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
#ifndef JSDEC_CUTTER_H
5+
#define JSDEC_CUTTER_H
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
#include <rz_util/rz_annotated_code.h>
12+
13+
RzAnnotatedCode *jsdec_as_annotation(RzCore *core, ut64 addr);
14+
15+
#ifdef __cplusplus
16+
}
17+
#endif
18+
19+
#endif /* JSDEC_CUTTER_H */

0 commit comments

Comments
 (0)