static void decode_de(yystype *params[], struct text_buffer *txtbuf) { const char *p = TOKEN_GET(params[0], cstring); const long a = TOKEN_GET(params[1], ival); /*const char *c = params[2];*/ char *k = TOKEN_GET(params[3], string); /*const char *r = params[5];*/ unsigned val=0; unsigned nsplit = 0; const char* o; const char **tokens; memset(txtbuf, 0, sizeof(*txtbuf)); if(!p || !k ) return; for(o = k; *o; o++) if(*o == '|') nsplit++; nsplit++; tokens = malloc(sizeof(char*)*nsplit); if(!tokens) { return; } cli_strtokenize(k,'|',nsplit, tokens); do { while(*p && !isalnum(*p)) { if(*p=='\\' && (p[1] == '\'' || p[1] == '\"')) p++; else textbuffer_putc(txtbuf, *p++); } if(!*p) break; val = 0; o = p; while(*p && isalnum(*p)) { unsigned x; unsigned char v = *p++; /* TODO: use a table here */ if(v >= 'a') x = 10+v-'a'; else if(v >= 'A') x = 36+v-'A'; else x = v-'0'; val = val*a+x; } if(val >= nsplit || !tokens[val] || !tokens[val][0]) while(o!=p) textbuffer_putc(txtbuf, *o++); else textbuffer_append(txtbuf, tokens[val]); } while (*p); free(tokens); textbuffer_append(txtbuf, "\0"); }
/* return class of last character */ static char output_token(const yystype *token, struct scope *scope, struct buf *out, char lastchar) { char sbuf[128]; const char *s = TOKEN_GET(token, cstring); /* TODO: use a local buffer, instead of FILE* */ switch(token->type) { case TOK_StringLiteral: output_space(lastchar,'"', out); buf_outc('"', out); if(s) { buf_outs(s, out); } buf_outc('"', out); return '\"'; case TOK_NumericInt: output_space(lastchar,'0', out); snprintf(sbuf, sizeof(sbuf), "%ld", TOKEN_GET(token, ival)); buf_outs(sbuf, out); return '0'; case TOK_NumericFloat: output_space(lastchar,'0', out); snprintf(sbuf, sizeof(sbuf), "%g", TOKEN_GET(token, dval)); buf_outs(sbuf, out); return '0'; case TOK_IDENTIFIER_NAME: output_space(lastchar,'a', out); if(s) { long id = scope_lookup(scope, s, strlen(s)); if(id == -1) { /* identifier not normalized */ buf_outs(s, out); } else { snprintf(sbuf, sizeof(sbuf), "n%03ld",id); buf_outs(sbuf, out); } } return 'a'; case TOK_FUNCTION: output_space(lastchar,'a', out); buf_outs("function",out); return 'a'; default: if(s) { const size_t len = strlen(s); output_space(lastchar,s[0], out); buf_outs(s, out); return len ? s[len-1] : '\0'; } return '\0'; } }
END_TEST START_TEST (test_token_cstring) { const char *str = "test"; yystype tok; memset(&tok, 0, sizeof(tok)); TOKEN_SET(&tok, cstring, str); fail_unless(TOKEN_GET(&tok, string) == NULL, "token cstring->string"); fail_unless(TOKEN_GET(&tok, cstring) == str, "token string->cstring"); fail_unless(TOKEN_GET(&tok, scope) == NULL, "token string->scope"); fail_unless(TOKEN_GET(&tok, ival) == -1, "token string->ival"); }
END_TEST START_TEST (test_token_scope) { struct scope *sc = (struct scope*)0xdeadbeef; yystype tok; memset(&tok, 0, sizeof(tok)); TOKEN_SET(&tok, scope, sc); fail_unless(TOKEN_GET(&tok, string) == NULL, "token scope->string"); fail_unless(TOKEN_GET(&tok, cstring) == NULL, "token scope->cstring"); fail_unless(TOKEN_GET(&tok, scope) == sc, "token scope->scope"); fail_unless(TOKEN_GET(&tok, ival) == -1, "token scope->ival"); }
END_TEST #endif /* CHECK_HAVE_LOOPS */ START_TEST (test_token_string) { char str[] = "test"; yystype tok; memset(&tok, 0, sizeof(tok)); TOKEN_SET(&tok, string, str); fail_unless(TOKEN_GET(&tok, string) == str, "token string get/set"); fail_unless(TOKEN_GET(&tok, cstring) == str, "token string->cstring"); fail_unless(TOKEN_GET(&tok, scope) == NULL, "token string->scope"); fail_unless(TOKEN_GET(&tok, ival) == -1, "token string->ival"); }
END_TEST START_TEST (test_token_dval) { int val = 0.12345; yystype tok; memset(&tok, 0, sizeof(tok)); TOKEN_SET(&tok, dval, val); fail_unless(TOKEN_GET(&tok, string) == NULL, "token dval->string"); fail_unless(TOKEN_GET(&tok, cstring) == NULL, "token dval->cstring"); fail_unless(TOKEN_GET(&tok, scope) == NULL, "token dval->scope"); fail_unless(TOKEN_GET(&tok, dval) - val < 1e-9, "token dval->dval"); fail_unless(TOKEN_GET(&tok, ival) == -1, "token dval->ival"); }
/* scriptasylum dot com's JS encoder */ static void handle_df(const yystype *tokens, size_t start, struct decode_result *res) { char *str, *s1; size_t len, s1_len, i; unsigned char clast; char *R; if(tokens[start].type != TOK_StringLiteral) return; str = TOKEN_GET(&tokens[start], string); if(!str) return; len = strlen(str); if(!len) return; clast = str[len-1] - '0'; str[len-1] = '\0'; s1 = cli_unescape(str); s1_len = strlen(s1); for(i=0;i<s1_len;i++) { s1[i] -= clast; } R = cli_unescape(s1); free(s1); res->pos_begin = start-2; res->pos_end = start+2; res->txtbuf.data = R; res->txtbuf.pos = strlen(R); res->append = 1; }
static void handle_eval(struct tokens *tokens, size_t start, struct decode_result *res) { res->txtbuf.data = TOKEN_GET(&tokens->data[start], string); if(res->txtbuf.data && tokens->data[start+1].type == TOK_PAR_CLOSE) { TOKEN_SET(&tokens->data[start], string, NULL); res->txtbuf.pos = strlen(res->txtbuf.data); res->pos_begin = start-2; res->pos_end = start+2; } }
static void run_folders(struct tokens *tokens) { size_t i; for(i = 0; i < tokens->cnt; i++) { const char *cstring = TOKEN_GET(&tokens->data[i], cstring); if(i+2 < tokens->cnt && tokens->data[i].type == TOK_IDENTIFIER_NAME && cstring && !strcmp("unescape", cstring) && tokens->data[i+1].type == TOK_PAR_OPEN) { handle_unescape(tokens, i+2); } } }
static inline int state_update_scope(struct parser_state *state, const yystype *token) { if(token->type == TOK_FUNCTION) { struct scope *scope = TOKEN_GET(token, scope); if(scope) { state->current = scope; } else { /* dummy token marking function end */ if(state->current->parent) state->current = state->current->parent; /* don't output this token, it is just a dummy marker */ return 0; } } return 1; }
static int handle_unescape(struct tokens *tokens, size_t start) { if(tokens->data[start].type == TOK_StringLiteral) { char *R; struct tokens new_tokens; yystype tok; R = cli_unescape(TOKEN_GET(&tokens->data[start], cstring)); tok.type = TOK_StringLiteral; TOKEN_SET(&tok, string, R); new_tokens.capacity = new_tokens.cnt = 1; new_tokens.data = &tok; if(replace_token_range(tokens, start-2, start+2, &new_tokens) < 0) return CL_EMEM; } return CL_SUCCESS; }
static int match_parameters(const yystype *tokens, const char ** param_names, size_t count) { size_t i,j=0; if(tokens[0].type != TOK_PAR_OPEN) return -1; i=1; while(count--) { const char *token_val = TOKEN_GET(&tokens[i], cstring); if(tokens[i].type != TOK_IDENTIFIER_NAME || !token_val || strcmp(token_val, param_names[j++])) return -1; ++i; if((count && tokens[i].type != TOK_COMMA) || (!count && tokens[i].type != TOK_PAR_CLOSE)) return -1; ++i; } return 0; }
/* buffer is html-normlike "chunk", if original file is bigger than buffer, * we rewind to a space, so we'll know that tokens won't be broken in half at * the end of a buffer. All tokens except string-literals of course. * So we can assume that after the buffer there is either a space, EOF, or a * chunk of text not containing whitespace at all (for which we care only if its * a stringliteral)*/ void cli_js_process_buffer(struct parser_state *state, const char *buf, size_t n) { struct scope* current = state->current; YYSTYPE val; int yv; YY_BUFFER_STATE yyb; if(!state->global) { /* this state has either not been initialized, * or cli_js_parse_done() was already called on it */ cli_warnmsg(MODULE "invalid state\n"); return; } yyb = yy_scan_bytes(buf, n, state->scanner); memset(&val, 0, sizeof(val)); val.vtype = vtype_undefined; /* on EOF yylex will return 0 */ while( (yv=yylex(&val, state->scanner)) != 0) { const char *text; size_t leng; val.type = yv; switch(yv) { case TOK_VAR: current->fsm_state = InsideVar; break; case TOK_IDENTIFIER_NAME: text = yyget_text(state->scanner); leng = yyget_leng(state->scanner); if(current->last_token == TOK_DOT) { /* this is a member name, don't normalize */ TOKEN_SET(&val, string, cli_strdup(text)); val.type = TOK_UNNORM_IDENTIFIER; } else { switch(current->fsm_state) { case WaitParameterList: state->syntax_errors++; /* fall through */ case Base: case InsideInitializer: TOKEN_SET(&val, cstring, scope_use(current, text, leng)); break; case InsideVar: case InsideFunctionDecl: TOKEN_SET(&val, cstring, scope_declare(current, text, leng, state)); current->fsm_state = InsideInitializer; current->brackets = 0; break; case WaitFunctionName: TOKEN_SET(&val, cstring, scope_declare(current, text, leng, state)); current->fsm_state = WaitParameterList; break; } } break; case TOK_PAR_OPEN: switch(current->fsm_state) { case WaitFunctionName: /* fallthrough */ case WaitParameterList: current->fsm_state = InsideFunctionDecl; break; default: /* noop */ break; } break; case TOK_PAR_CLOSE: switch(current->fsm_state) { case WaitFunctionName: state->syntax_errors++; break; case WaitParameterList: current->fsm_state = Base; break; default: /* noop */ break; } break; case TOK_CURLY_BRACE_OPEN: switch(current->fsm_state) { case WaitFunctionName: /* fallthrough */ case WaitParameterList: case InsideFunctionDecl: /* in a syntactically correct * file, we would already be in * the Base state when we see a { */ current->fsm_state = Base; /* fall-through */ case InsideVar: case InsideInitializer: state->syntax_errors++; /* fall-through */ case Base: default: current->blocks++; break; } break; case TOK_CURLY_BRACE_CLOSE: if(current->blocks > 0) current->blocks--; else state->syntax_errors++; if(!current->blocks) { if(current->parent) { /* add dummy FUNCTION token to * mark function end */ TOKEN_SET(&val, cstring, "}"); add_token(state, &val); TOKEN_SET(&val, scope, NULL); val.type = TOK_FUNCTION; state->current = current = current->parent; } else{ /* extra } */ state->syntax_errors++; } } break; case TOK_BRACKET_OPEN: current->brackets++; break; case TOK_BRACKET_CLOSE: if(current->brackets > 0) current->brackets--; else state->syntax_errors++; break; case TOK_COMMA: if (current->fsm_state == InsideInitializer && current->brackets == 0 && current->blocks == 0) { /* initializer ended only if we * encountered a comma, and [] are * balanced. * This avoids switching state on: * var x = [4,y,u];*/ current->fsm_state = InsideVar; } break; case TOK_SEMICOLON: if (current->brackets == 0 && current->blocks == 0) { /* avoid switching state on unbalanced []: * var x = [test;testi]; */ current->fsm_state = Base; } break; case TOK_FUNCTION: current = scope_new(state); current->fsm_state = WaitFunctionName; TOKEN_SET(&val, scope, state->current); break; case TOK_StringLiteral: if(state->tokens.cnt > 1 && state->tokens.data[state->tokens.cnt-1].type == TOK_PLUS) { /* see if can fold */ yystype *prev_string = &state->tokens.data[state->tokens.cnt-2]; if(prev_string->type == TOK_StringLiteral) { char *str = TOKEN_GET(prev_string, string); size_t str_len = strlen(str); text = yyget_text(state->scanner); leng = yyget_leng(state->scanner); /* delete TOK_PLUS */ free_token(&state->tokens.data[--state->tokens.cnt]); str = cli_realloc(str, str_len + leng + 1); if (!str) break; strncpy(str+str_len, text, leng); str[str_len + leng] = '\0'; TOKEN_SET(prev_string, string, str); free(val.val.string); memset(&val, 0, sizeof(val)); val.vtype = vtype_undefined; continue; } } break; } if(val.vtype == vtype_undefined) { text = yyget_text(state->scanner); TOKEN_SET(&val, string, cli_strdup(text)); abort(); } add_token(state, &val); current->last_token = yv; memset(&val, 0, sizeof(val)); val.vtype = vtype_undefined; } }
static void run_decoders(struct parser_state *state) { size_t i; const char* name; struct tokens *tokens = &state->tokens; for(i = 0; i < tokens->cnt; i++) { const char *cstring = TOKEN_GET(&tokens->data[i], cstring); struct decode_result res; res.pos_begin = res.pos_end = 0; res.append = 0; if(tokens->data[i].type == TOK_FUNCTION && i+13 < tokens->cnt) { name = NULL; ++i; if(tokens->data[i].type == TOK_IDENTIFIER_NAME) { cstring = TOKEN_GET(&tokens->data[i], cstring); name = cstring; ++i; } if(match_parameters(&tokens->data[i], de_packer_3, sizeof(de_packer_3)/sizeof(de_packer_3[0])) != -1 || match_parameters(&tokens->data[i], de_packer_2, sizeof(de_packer_2)/sizeof(de_packer_2[0])) != -1) { /* find function decl. end */ handle_de(tokens->data, i, tokens->cnt, name, &res); } } else if(i+2 < tokens->cnt && tokens->data[i].type == TOK_IDENTIFIER_NAME && cstring && !strcmp("dF", cstring) && tokens->data[i+1].type == TOK_PAR_OPEN) { /* TODO: also match signature of dF function (possibly * declared using unescape */ handle_df(tokens->data, i+2, &res); } else if(i+2 < tokens->cnt && tokens->data[i].type == TOK_IDENTIFIER_NAME && cstring && !strcmp("eval", cstring) && tokens->data[i+1].type == TOK_PAR_OPEN) { handle_eval(tokens, i+2, &res); } if(res.pos_end > res.pos_begin) { struct tokens parent_tokens; if(res.pos_end < tokens->cnt && tokens->data[res.pos_end].type == TOK_SEMICOLON) res.pos_end++; parent_tokens = state->tokens;/* save current tokens */ /* initialize embedded context */ memset(&state->tokens, 0, sizeof(state->tokens)); if(++state->rec > 16) cli_dbgmsg(MODULE "recursion limit reached\n"); else { cli_js_process_buffer(state, res.txtbuf.data, res.txtbuf.pos); --state->rec; } free(res.txtbuf.data); /* state->tokens still refers to the embedded/nested context * here */ if(!res.append) { replace_token_range(&parent_tokens, res.pos_begin, res.pos_end, &state->tokens); } else { /* delete tokens */ replace_token_range(&parent_tokens, res.pos_begin, res.pos_end, NULL); append_tokens(&parent_tokens, &state->tokens); } /* end of embedded context, restore tokens state */ free(state->tokens.data); state->tokens = parent_tokens; } state_update_scope(state, &state->tokens.data[i]); } }
static void handle_de(yystype *tokens, size_t start, const size_t cnt, const char *name, struct decode_result *res) { /* find function decl. end */ size_t i, nesting = 1, j; yystype* parameters [6]; const size_t parameters_cnt = 6; for(i=start;i < cnt; i++) { if(tokens[i].type == TOK_FUNCTION) { if(TOKEN_GET(&tokens[i], scope)) nesting++; else nesting--; if(!nesting) break; } } if(nesting) return; memset(parameters, 0, sizeof(parameters)); if(name) { /* find call to function */ for(;i+2 < cnt; i++) { const char* token_val = TOKEN_GET(&tokens[i], cstring); if(tokens[i].type == TOK_IDENTIFIER_NAME && token_val && !strcmp(name, token_val) && tokens[i+1].type == TOK_PAR_OPEN) { i += 2; for(j = 0;j < parameters_cnt && i < cnt;j++) { parameters[j] = &tokens[i++]; if(j != parameters_cnt-1) while (tokens[i].type != TOK_COMMA && i < cnt) i++; else while (tokens[i].type != TOK_PAR_CLOSE && i < cnt) i++; i++; } if(j == parameters_cnt) decode_de(parameters, &res->txtbuf); } } } else { while(i<cnt && tokens[i].type != TOK_PAR_OPEN) i++; ++i; if(i >= cnt) return; /* TODO: move this v to another func */ for(j = 0;j < parameters_cnt && i < cnt;j++) { parameters[j] = &tokens[i++]; if(j != parameters_cnt-1) while (tokens[i].type != TOK_COMMA && i < cnt) i++; else while (tokens[i].type != TOK_PAR_CLOSE && i < cnt) i++; i++; } if(j == parameters_cnt) decode_de(parameters, &res->txtbuf); } if(parameters[0] && parameters[parameters_cnt-1]) { res->pos_begin = parameters[0] - tokens; res->pos_end = parameters[parameters_cnt-1] - tokens + 1; if(tokens[res->pos_end].type == TOK_BRACKET_OPEN && tokens[res->pos_end+1].type == TOK_BRACKET_CLOSE && tokens[res->pos_end+2].type == TOK_PAR_CLOSE) res->pos_end += 3; /* {}) */ else res->pos_end++; /* ) */ } }
static int emulate (RAnalEsil *c, char *buf, int *tkns) { ut64 num = 0; char *op = NULL; char *str; int i, type; c->opstack = r_list_new (); c->stack = r_list_new (); c->opsize = 0; c->rightside = 0; for (i=0; tkns[i]; i+=2) { TOKEN_GET (type, str); eprintf ("(%d) (%s)\n", type, str); switch (type) { // case 0 handled in for conditional case 1: /* special command */ if (!strcmp (str, "[")) { int curstack = r_list_length (c->stack); eprintf ("STACK POINTER %d\n", curstack); c->opsize = (int)num; // TODO: test for size // read tokens until ']' // TOKEN_UNTIL (1, "]"); for (i+=2; tkns[i]; i+=2) { TOKEN_GET (type, str); eprintf ("--- %d (%s)\n", tkns[i], buf+tkns[i+1]); switch (tkns[i]) { case 1: if (!strcmp (str, "]")) { if (!c->opsize) c->opsize = c->anal->bits==64?8:4; //int j, len = r_list_length (c->stack) - curstack; char *a; OPUSH (op); while ((a = OPOP ())) { // eprintf ("---> op %s\n", op); esil_commit (c, a); } //op = NULL; printf (" %s (size %d)\n", c->rightside?"GET":"SET", (int)num); goto dungeon; // set destination for write // expect '=' } break; case 2: op = str; OPUSH (op); break; case 3: PUSH (str); break; } } if (!tkns[i]) { printf ("Unexpected eof\n"); return 1; } } else if (!strcmp (str, "?")) { printf (" CONDITIONAL\n"); i += 2; TOKEN_GET (type, str); if (!type) { eprintf (" UNEXPECTED EOF\n"); return 1; } if (type!=3) { printf (" UNEXPECTED TOKEN\n"); return 1; } //while () { i += 2; } } break; case 2: if (op) { //eprintf (" XXX Redefine op %s\n", op); if (!strcmp (op, "*")) { // prio esil_commit (c, op); } else OPUSH (op); } op = str; if (IS ("=")) { c->rightside = 1; } break; case 3: num = c->num (c, str); // // eprintf ("; push %s\n" , str); PUSH (str); break; case 4: esil_commit (c, op); op = NULL; break; case 5: // newcontext(); //esil_push_scope (c); eprintf ("OPEN SCOPE\n"); break; case 6: { //char *res = esil_pop_scope (c); // if scope > 0 : //PUSH (res); esil_commit (c, op); // free (res); // commit() // closecontext() // push result eprintf ("CLOSE SCOPE\n"); } break; } dungeon: {/*trick*/int/*label*/x/*parsing*/=/*fix*/0;} } eprintf (";;; COMMIT (%s) (%s)\n", op, str); esil_commit (c, op); if (r_list_length (c->opstack)>0) { char *a; while ((a = OPOP ())) { esil_commit (c, a); } } op = NULL; return 0; }