btcs

bitcoin script parser/evaluator/compiler/decompiler
git clone git://jb55.com/btcs
Log | Files | Refs | README | LICENSE

commit 142a953fa026aa904e52e846967d5dc6af3271b9
parent 23876114bf7ecf114f825fadfeafcfd0de97cf94
Author: William Casarin <jb55@jb55.com>
Date:   Wed, 12 Jun 2019 23:58:36 -0700

decompiling working

Diffstat:
MMakefile | 6+++---
Mmain.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mscript.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mscript.h | 3+++
Autil.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.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 */