commit 142a953fa026aa904e52e846967d5dc6af3271b9
parent 23876114bf7ecf114f825fadfeafcfd0de97cf94
Author: William Casarin <jb55@jb55.com>
Date: Wed, 12 Jun 2019 23:58:36 -0700
decompiling working
Diffstat:
M | Makefile | | | 6 | +++--- |
M | main.c | | | 161 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
M | script.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
M | script.h | | | 3 | +++ |
A | util.c | | | 88 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.h | | | 13 | +++++++++++++ |
6 files changed, 300 insertions(+), 38 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,4 +1,3 @@
-
CFLAGS=-g -DHAVE_LITTLE_ENDIAN -O2 -Ideps -std=c99 -Wall -Wextra -Werror -Wno-unused-variable -Wno-unused-function -Wunreachable-code
DEPS=script.c \
@@ -10,11 +9,12 @@ DEPS=script.c \
sha256.c \
ripemd160.c \
compiler.c \
+ util.c \
stack.c
CLIDEPS=parser.tab.c \
- lex.yy.c \
- main.c
+ lex.yy.c \
+ main.c
TESTDEPS=test.c
diff --git a/main.c b/main.c
@@ -1,9 +1,13 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
#include "stack.h"
#include "script.h"
#include "alloc.h"
+#include "util.h"
extern int yyparse();
extern FILE* yyin;
@@ -17,13 +21,24 @@ void yy_scan_string(const char *);
void yyerror(const char* s);
+#define streq(a,b) strcmp(a,b) == 0
+
void parse_error(char* err) {
fprintf(stderr, "[btcs] parse error: %s\n", err);
exit(1);
}
-int main(int argc, const char *argv[]) {
+#define COMPILE_SHOW_SCRIPT (1 << 0)
+#define COMPILE_SHOW_SCRIPT_HEX (1 << 1)
+#define COMPILE_SHOW_STACK (1 << 2)
+#define COMPILE_SHOW_STACK_HEX (1 << 3)
+#define COMPILE_SHOW_RESULTS (1 << 4)
+#define COMPILE_SHOW_LABELS (1 << 5)
+#define COMPILE_SHOW_ALL 0x3F
+
+static int compile(int compile_options, int argc, const char *argv[])
+{
yyin = stdin;
struct result result;
@@ -38,7 +53,7 @@ int main(int argc, const char *argv[]) {
stack_init(&g_reader_stack);
if (argc > 1) {
- for (int i = 1; i < argc; ++i) {
+ for (i = 1; i < argc; ++i) {
yy_scan_string(argv[i]);
yyparse();
}
@@ -49,31 +64,52 @@ int main(int argc, const char *argv[]) {
} while(!feof(yyin));
}
+ bool nol = (compile_options & COMPILE_SHOW_LABELS) == 0;
+
/* size = g_reader_buf_top - g_reader_buf; */
- printf("script ");
- script_print_vals(&g_reader_stack);
+ if (compile_options & COMPILE_SHOW_SCRIPT) {
+ if (compile_options & COMPILE_SHOW_LABELS)
+ printf("script ");
+ script_print_vals(&g_reader_stack);
+ }
+
script_serialize(&g_reader_stack, buf, bufsize, &compiled_len);
script_eval(buf, compiled_len, &tmp_stack, &result);
- printf("script_hex ");
- for(i = 0; i < compiled_len; ++i)
- printf("%02x", buf[i]);
- printf("\n");
+ if (compile_options & COMPILE_SHOW_SCRIPT_HEX) {
+ if (compile_options & COMPILE_SHOW_LABELS)
+ printf("script_hex ");
+ for(i = 0; i < compiled_len; ++i)
+ printf("%02x", buf[i]);
+ printf("\n");
+ }
+
+ if (compile_options & COMPILE_SHOW_STACK) {
+ if (compile_options & COMPILE_SHOW_LABELS)
+ printf("stack ");
+ script_print_vals(&tmp_stack);
+ }
- printf("stack ");
- script_print_vals(&tmp_stack);
stack_serialize(&tmp_stack, buf, bufsize, &compiled_len);
- printf("stack_hex ");
- for(i = 0; i < compiled_len; ++i)
- printf("%02x", buf[i]);
- printf("\n");
+ if (compile_options & COMPILE_SHOW_STACK_HEX) {
+ if (compile_options & COMPILE_SHOW_LABELS)
+ printf("stack_hex ");
+ for(i = 0; i < compiled_len; ++i)
+ printf("%02x", buf[i]);
+ printf("\n");
+ }
- printf("results ");
- if (result.error) printf(" error:%d:%s:%s", result.op_count, result.error,
- op_name(result.last_op));
- else printf(" success");
- printf("\n");
+ if (compile_options & COMPILE_SHOW_RESULTS) {
+ if (compile_options & COMPILE_SHOW_LABELS)
+ printf("results ");
+ if (result.error)
+ printf(" error:%d:%s:%s", result.op_count, result.error,
+ op_name(result.last_op));
+ else
+ printf(" success");
+ printf("\n");
+ }
stack_free(&g_reader_stack);
stack_free(&tmp_stack);
@@ -82,6 +118,93 @@ int main(int argc, const char *argv[]) {
return !!result.error;
}
+static void fail(int err, const char *msg)
+{
+ fprintf(stderr, "error: %s\n", msg);
+ exit(err);
+}
+
+
+static int decompile(const char *str, int strlen)
+{
+ static u8 buf[10000];
+
+ if (strlen % 2 != 0)
+ return 0;
+
+ hex_decode(str, strlen, buf, sizeof(buf));
+ size_t nbytes = strlen / 2;
+
+ script_print(buf, nbytes);
+
+ return 1;
+}
+
+
+static void usage()
+{
+ fprintf(stderr, "usage: btcs [OPTIONS] <script>\n\n");
+ fprintf(stderr, " OPTIONS\n\n");
+ fprintf(stderr, " -d,--decompile decompile a base16 string to bitcoin script\n");
+ exit(1);
+}
+
+int main(int argc, const char *argv[])
+{
+ static u8 buf[20000];
+
+ bool is_decompile = false;
+ const char *input = NULL;
+ size_t written;
+ int compile_options = 0;
+ int last_opt = 0;
+ bool hide_labels = false;
+
+ if (argc == 2 && (streq(argv[1], "-h") || streq(argv[1], "--help")))
+ usage();
+
+ for (int i = 1; i < argc; i++) {
+ if (streq(argv[i], "-d") || streq(argv[i], "--decompile"))
+ is_decompile = true;
+ else if (streq(argv[i], "+sh"))
+ compile_options |= COMPILE_SHOW_SCRIPT_HEX;
+ else if (streq(argv[i], "+s"))
+ compile_options |= COMPILE_SHOW_SCRIPT_HEX;
+ else if (streq(argv[i], "+st"))
+ compile_options |= COMPILE_SHOW_STACK;
+ else if (streq(argv[i], "+sth"))
+ compile_options |= COMPILE_SHOW_STACK_HEX;
+ else if (streq(argv[i], "-q") || streq(argv[i], "--hide-labels"))
+ hide_labels = true;
+ else {
+ last_opt = i-1;
+ input = argv[i];
+ break;
+ }
+ }
+
+ if (is_decompile) {
+ int ok = read_arg_or_stdin(input, buf, sizeof(buf), &written);
+
+ if (!ok)
+ fail(3, "failed to read file or stdin");
+
+ ok = decompile((const char *)buf, written);
+
+ if (!ok)
+ fail(4, "failed to decompile");
+ }
+ else {
+ if (!compile_options)
+ compile_options = COMPILE_SHOW_ALL;
+
+ if (hide_labels)
+ compile_options &= ~COMPILE_SHOW_LABELS;
+
+ exit( compile(compile_options, argc - last_opt, argv + last_opt) );
+ }
+}
+
void yyerror(const char* s) {
fprintf(stderr, "Parse error: %s\n", s);
exit(1);
diff --git a/script.c b/script.c
@@ -50,8 +50,9 @@ cast_to_bool(struct val val) {
assert(!"Unhandled val.type in cast_to_bool");
}
-int
-script_getop(const u8 **p, const u8 *end, enum opcode *popcode, u8 *buf, int bufsize, u32 *outlen) {
+int script_getop(const u8 **p, const u8 *end, enum opcode *popcode, u8 *buf,
+ int bufsize, u32 *outlen)
+{
*popcode = OP_INVALIDOPCODE;
u32 nsize = 0;
@@ -363,9 +364,9 @@ script_eval(const u8 *script, size_t script_size, struct stack *stack,
// (x - 0 | x x)
if (stack_size(stack) < 1)
SCRIPTERR("INVALID_STACK_OPERATION");
- struct val val = stack_top_val(stack, -1);
- if (cast_to_bool(val))
- stack_push_val(stack, val_copy(val));
+ struct val v = stack_top_val(stack, -1);
+ if (cast_to_bool(v))
+ stack_push_val(stack, val_copy(v));
}
break;
@@ -374,8 +375,8 @@ script_eval(const u8 *script, size_t script_size, struct stack *stack,
// -- stacksize
struct num sn;
sn_from_int(stack_size(stack), &sn);
- struct val val = sn_to_val(&sn);
- stack_push_val(stack, val);
+ struct val v = sn_to_val(&sn);
+ stack_push_val(stack, v);
}
break;
@@ -723,30 +724,64 @@ evalerror:
return !err;
}
-void script_print(u8 *script, size_t script_size) {
+
+static char hexchar(unsigned int val)
+{
+ if (val < 10)
+ return '0' + val;
+ if (val < 16)
+ return 'a' + val - 10;
+ assert(!"hexchar invalid val");
+}
+
+static void hex_print(const u8 *buf, size_t bufsize) {
+ for (size_t i = 0; i < bufsize; i++) {
+ unsigned int c = buf[i];
+ printf("%c%c", hexchar(c >> 4), hexchar(c & 0xF) );
+ }
+}
+
+
+static bool is_push_data(enum opcode op)
+{
+ return (op > OP_0 && op < OP_PUSHDATA1) ||
+ op == OP_PUSHDATA1 ||
+ op == OP_PUSHDATA2 ||
+ op == OP_PUSHDATA4;
+}
+
+void script_print(const u8 *script, size_t script_size) {
u32 len;
static u8 tmpbuf[4096];
- u8 *t = tmpbuf;
const u8 *p = script;
const u8 *top = script + script_size;
+
while (p < top) {
enum opcode opcode;
- script_getop(&p, top, &opcode, tmpbuf, ARRAY_SIZE(tmpbuf), &len);
- u8 *ttop = tmpbuf + len;
- printf("%s ", op_name(opcode));
- if (t < ttop)
- printf("%02x", *t++);
+ script_getop(&p, top, &opcode, tmpbuf, sizeof(tmpbuf), &len);
+
+ if (is_push_data(opcode)) {
+ hex_print(tmpbuf, len);
+ printf(" ");
+ }
+ else
+ printf("%s ", op_name(opcode));
+
+ len = 0;
}
printf("\n");
}
void script_print_vals(struct stack *stack) {
void **p = stack->bottom;
- int c = 0;
+ bool first = true;
while (p < stack->top) {
struct val val;
memcpy(&val, &*p++, sizeof(struct val));
- printf(" ");
+ if (first)
+ first = false;
+ else
+ printf(" ");
val_print(val);
}
putchar('\n');
diff --git a/script.h b/script.h
@@ -24,6 +24,9 @@ void script_push_datastr(struct stack *, const char *str, int israw);
void script_serialize(struct stack *stack, u8 *buf, int buflen, int* len);
void stack_serialize(struct stack *stack, u8 *buf, int buflen, int* len);
+void script_print(const u8 *script, size_t script_size);
+int script_getop(const u8 **p, const u8 *end, enum opcode *popcode, u8 *buf, int bufsize, u32 *outlen);
+
extern int g_silence_script_err;
extern int g_silence_script_warn;
diff --git a/util.c b/util.c
@@ -0,0 +1,88 @@
+
+#include "util.h"
+
+static bool char_to_hex(unsigned char *val, char c)
+{
+ if (c >= '0' && c <= '9') {
+ *val = c - '0';
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *val = c - 'a' + 10;
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *val = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
+{
+ unsigned char v1, v2;
+ unsigned char *p = buf;
+
+ while (slen > 1) {
+ if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1]))
+ return false;
+ if (!bufsize)
+ return false;
+ *(p++) = (v1 << 4) | v2;
+ str += 2;
+ slen -= 2;
+ bufsize--;
+ }
+ return slen == 0 && bufsize == 0;
+}
+
+
+int read_fd(FILE *fd, unsigned char *buf, size_t buflen, size_t *written)
+{
+ unsigned char *p = buf;
+ int len = 0;
+ *written = 0;
+
+ do {
+ len = fread(p, 1, 4096, fd);
+ *written += len;
+ p += len;
+ if (p > buf + buflen)
+ return 0;
+ } while (len == 4096);
+
+ return 1;
+}
+
+
+int read_arg_or_stdin(const char *arg, unsigned char *buf, size_t buflen,
+ size_t *written)
+{
+ if (arg != NULL) {
+ unsigned char *p = buf;
+ bool done = false;
+ for (size_t i = 0; i < buflen; i++) {
+ *p = arg[i];
+ if (arg[i] == 0) {
+ done = true;
+ break;
+ }
+ p++;
+ }
+
+ *written = p - buf;
+
+ return done;
+ }
+ else {
+ int ok = read_fd(stdin, buf, buflen, written);
+ if (!ok)
+ return ok;
+ if (*written == 0)
+ return 0;
+ (*written)--;
+ buf[*written] = 0;
+ return ok;
+ }
+}
+
diff --git a/util.h b/util.h
@@ -0,0 +1,13 @@
+
+#ifndef BTCS_UTIL_H
+#define BTCS_UTIL_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+int read_arg_or_stdin(const char *arg, unsigned char *buf, size_t buflen, size_t *written);
+bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize);
+
+#endif /* UTIL_H */