/* Simply overwrite the hash entry we found before. */ enum TDB_ERROR replace_in_hash(struct tdb_context *tdb, struct hash_info *h, tdb_off_t new_off) { return tdb_write_off(tdb, hbucket_off(h->group_start, h->found_bucket), encode_offset(new_off, h)); }
/* * parse a function and its arguments and fill the structure */ int encode_function(char *string, struct filter_op *fop) { char *str = strdup(string); int ret = -ENOTFOUND; char *name, *args; int nargs = 0, i; char **dec_args = NULL; char *tok; memset(fop, 0, sizeof(struct filter_op)); /* get the name of the function */ name = ec_strtok(string, "(", &tok); /* get all the args */ args = name + strlen(name) + 1; /* analyze the arguments */ dec_args = decode_args(args, &nargs); /* this fop is a function */ fop->opcode = FOP_FUNC; /* check if it is a known function */ if (!strcmp(name, "search")) { if (nargs == 2) { /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_SEARCH; fop->op.func.string = (u_char*)strdup(dec_args[1]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "regex")) { if (nargs == 2) { int err; regex_t regex; char errbuf[100]; /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_REGEX; fop->op.func.string = (u_char*)strdup(dec_args[1]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); /* check if the regex is valid */ err = regcomp(®ex, (const char*)fop->op.func.string, REG_EXTENDED | REG_NOSUB | REG_ICASE ); if (err) { regerror(err, ®ex, errbuf, sizeof(errbuf)); SCRIPT_ERROR("%s", errbuf); } regfree(®ex); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "pcre_regex")) { #ifndef HAVE_PCRE WARNING("The script contains pcre_regex, but you don't have support for it."); #else pcre *pregex; const char *errbuf = NULL; int erroff; if (nargs == 2) { /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_PCRE; fop->op.func.string = strdup(dec_args[1]); fop->op.func.slen = strlen(fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); /* check if the pcre is valid */ pregex = pcre_compile(fop->op.func.string, 0, &errbuf, &erroff, NULL ); if (pregex == NULL) SCRIPT_ERROR("%s\n", errbuf); pcre_free(pregex); } else if (nargs == 3) { fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_PCRE; /* substitution always at layer DATA */ fop->op.func.level = 5; fop->op.func.string = strdup(dec_args[1]); fop->op.func.slen = strlen(fop->op.func.string); fop->op.func.replace = strdup(dec_args[2]); fop->op.func.rlen = strlen(fop->op.func.replace); ret = ESUCCESS; /* check if the pcre is valid */ pregex = pcre_compile(fop->op.func.string, 0, &errbuf, &erroff, NULL ); if (pregex == NULL) SCRIPT_ERROR("%s\n", errbuf); pcre_free(pregex); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); #endif } else if (!strcmp(name, "replace")) { if (nargs == 2) { fop->op.func.op = FFUNC_REPLACE; /* replace always operate at DATA level */ fop->op.func.level = 5; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); fop->op.func.replace = (u_char*)strdup(dec_args[1]); fop->op.func.rlen = strescape((char*)fop->op.func.replace, (char*)fop->op.func.replace); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "inject")) { if (nargs == 1) { fop->op.func.op = FFUNC_INJECT; /* inject always operate at DATA level */ fop->op.func.level = 5; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "execinject")) { if (nargs == 1) { fop->op.func.op = FFUNC_EXECINJECT; /* execinject always operate at DATA level */ fop->op.func.level = 5; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "log")) { if (nargs == 2) { /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_LOG; fop->op.func.string = (u_char*)strdup(dec_args[1]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "drop")) { if (nargs == 0) { fop->op.func.op = FFUNC_DROP; ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "kill")) { if (nargs == 0) { fop->op.func.op = FFUNC_KILL; ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "msg")) { if (nargs == 1) { fop->op.func.op = FFUNC_MSG; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "exec")) { if (nargs == 1) { fop->op.func.op = FFUNC_EXEC; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "exit")) { if (nargs == 0) { fop->opcode = FOP_EXIT; ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } /* free the array */ for (i = 0; i < nargs; i++) SAFE_FREE(dec_args[i]); SAFE_FREE(dec_args); SAFE_FREE(str); return ret; }
bool MethodTransform::try_sync() { TRACE(MTRANS, 5, "Syncing %s\n", SHOW(m_method)); auto code = m_method->get_code(); auto& opout = code->get_instructions(); opout.clear(); uint32_t addr = 0; addr_mei_t addr_to_mei; // Step 1, regenerate opcode list for the method, and // and calculate the opcode entries address offsets. TRACE(MTRANS, 5, "Emitting opcodes\n"); for (auto miter = m_fmethod->begin(); miter != m_fmethod->end(); miter++) { MethodItemEntry* mentry = &*miter; TRACE(MTRANS, 5, "Analyzing mentry %p\n", mentry); mentry->addr = addr; if (mentry->type == MFLOW_OPCODE) { if ((mentry->insn->opcode() == FOPCODE_FILLED_ARRAY) && (addr & 1)) { opout.push_back(new DexInstruction(OPCODE_NOP)); ++addr; } addr_to_mei[addr] = mentry; TRACE(MTRANS, 5, "Emitting mentry %p at %08x\n", mentry, addr); opout.push_back(mentry->insn); addr += mentry->insn->size(); } } // Step 2, recalculate branches..., save off multi-branch data. TRACE(MTRANS, 5, "Recalculating branches\n"); std::vector<MethodItemEntry*> multi_branches; std::unordered_map<MethodItemEntry*, std::vector<BranchTarget*>> multis; std::unordered_map<BranchTarget*, uint32_t> multi_targets; std::unordered_map<DexTryItem*, std::vector<MethodItemEntry*>> try_items; for (auto miter = m_fmethod->begin(); miter != m_fmethod->end(); miter++) { MethodItemEntry* mentry = &*miter; if (mentry->type == MFLOW_OPCODE) { auto opcode = mentry->insn->opcode(); if (is_branch(opcode) && is_multi_branch(opcode)) { multi_branches.push_back(mentry); } } if (mentry->type == MFLOW_TARGET) { BranchTarget* bt = mentry->target; if (bt->type == BRANCH_MULTI) { multis[bt->src].push_back(bt); multi_targets[bt] = mentry->addr; // We can't fix the primary switch opcodes address until we emit // the fopcode, which comes later. } else if (bt->type == BRANCH_SIMPLE) { MethodItemEntry* tomutate = bt->src; int32_t branchoffset = mentry->addr - tomutate->addr; if ((tomutate->insn->opcode() == OPCODE_FILL_ARRAY_DATA) && (mentry->addr & 1)) { ++branchoffset; // account for nop spacer } auto encode_result = encode_offset(tomutate->insn, branchoffset); if (!encode_result.success) { auto inst = tomutate->insn; tomutate->insn = new DexInstruction(encode_result.newopcode); delete inst; return false; } } } if (mentry->type == MFLOW_TRY) { try_items[mentry->tentry->tentry].push_back(mentry); } } TRACE(MTRANS, 5, "Emitting multi-branches\n"); // Step 3, generate multi-branch fopcodes for (auto multiopcode : multi_branches) { auto targets = multis[multiopcode]; std::sort(targets.begin(), targets.end(), multi_target_compare_index); if (multi_contains_gaps(targets)) { // Emit sparse. unsigned long count = (targets.size() * 4) + 2; uint16_t sparse_payload[count]; sparse_payload[0] = FOPCODE_SPARSE_SWITCH; sparse_payload[1] = targets.size(); uint32_t* spkeys = (uint32_t*)&sparse_payload[2]; uint32_t* sptargets = (uint32_t*)&sparse_payload[2 + (targets.size() * 2)]; for (auto target : targets) { *spkeys++ = target->index; *sptargets++ = multi_targets[target] - multiopcode->addr; } // Emit align nop if (addr & 1) { DexInstruction* nop = new DexInstruction(0); opout.push_back(nop); addr++; } // Insert the new fopcode... DexInstruction* fop = new DexOpcodeData(sparse_payload, (int) (count - 1)); opout.push_back(fop); // re-write the source opcode with the address of the // fopcode, increment the address of the fopcode. encode_offset(multiopcode->insn, addr - multiopcode->addr); multiopcode->insn->set_opcode(OPCODE_SPARSE_SWITCH); addr += count; } else { // Emit packed. unsigned long count = (targets.size() * 2) + 4; uint16_t packed_payload[count]; packed_payload[0] = FOPCODE_PACKED_SWITCH; packed_payload[1] = targets.size(); uint32_t* psdata = (uint32_t*)&packed_payload[2]; *psdata++ = targets.front()->index; for (auto target : targets) { *psdata++ = multi_targets[target] - multiopcode->addr; } // Emit align nop if (addr & 1) { DexInstruction* nop = new DexInstruction(0); opout.push_back(nop); addr++; } // Insert the new fopcode... DexInstruction* fop = new DexOpcodeData(packed_payload, (int) (count - 1)); opout.push_back(fop); // re-write the source opcode with the address of the // fopcode, increment the address of the fopcode. encode_offset(multiopcode->insn, addr - multiopcode->addr); multiopcode->insn->set_opcode(OPCODE_PACKED_SWITCH); addr += count; } } // Step 4, emit debug opcodes TRACE(MTRANS, 5, "Emitting debug opcodes\n"); auto debugitem = code->get_debug_item(); if (debugitem) { auto& entries = debugitem->get_entries(); entries.clear(); for (auto& mentry : *m_fmethod) { if (mentry.type == MFLOW_DEBUG) { entries.emplace_back(mentry.addr, mentry.dbgop->clone()); } else if (mentry.type == MFLOW_POSITION) { entries.emplace_back(mentry.addr, mentry.pos); } } } // Step 5, try/catch blocks auto& tries = code->get_tries(); tries.clear(); for (auto tryitem : try_items) { DexTryItem* dextry = tryitem.first; auto& tryentries = tryitem.second; sort(tryentries.begin(), tryentries.end(), order_try_entries); dextry->m_catches.clear(); MethodItemEntry* try_start = nullptr; bool suppress = false; for (auto tryentry : tryentries) { switch (tryentry->tentry->type) { case TRY_START: dextry->m_start_addr = tryentry->addr; try_start = tryentry; break; case TRY_END: assert(try_start != nullptr); assert(try_start->addr <= tryentry->addr); dextry->m_insn_count = tryentry->addr - try_start->addr; if (dextry->m_insn_count == 0) { suppress = true; } break; case TRY_CATCH: if (tryentry->tentry->centry == nullptr) { /* Catch all */ dextry->m_catchall = tryentry->addr; } else { dextry->m_catches.push_back( std::make_pair(tryentry->tentry->centry, tryentry->addr)); } break; default: always_assert_log(false, "Invalid try entry type"); } } if (!suppress) { tries.push_back(dextry); } } std::sort(tries.begin(), tries.end(), [](const DexTryItem* a, const DexTryItem* b) { return a->m_start_addr < b->m_start_addr; }); return true; }
bool IRCode::try_sync(DexCode* code) { std::unordered_map<MethodItemEntry*, uint32_t> entry_to_addr; uint32_t addr = 0; // Step 1, regenerate opcode list for the method, and // and calculate the opcode entries address offsets. TRACE(MTRANS, 5, "Emitting opcodes\n"); for (auto miter = m_ir_list->begin(); miter != m_ir_list->end(); ++miter) { MethodItemEntry* mentry = &*miter; TRACE(MTRANS, 5, "Analyzing mentry %p\n", mentry); entry_to_addr[mentry] = addr; if (mentry->type == MFLOW_DEX_OPCODE) { TRACE(MTRANS, 5, "Emitting mentry %p at %08x\n", mentry, addr); addr += mentry->dex_insn->size(); } } // Step 2, Branch relaxation: calculate branch offsets for if-* and goto // opcodes, resizing them where necessary. Since resizing opcodes affects // address offsets, we need to iterate this to a fixed point. // // For instructions that use address offsets but never need resizing (i.e. // switch and fill-array-data opcodes), we calculate their offsets after // we have reached the fixed point. TRACE(MTRANS, 5, "Recalculating branches\n"); std::vector<MethodItemEntry*> multi_branches; std::unordered_map<MethodItemEntry*, std::vector<BranchTarget*>> multis; std::unordered_map<BranchTarget*, uint32_t> multi_targets; bool needs_resync = false; for (auto miter = m_ir_list->begin(); miter != m_ir_list->end(); ++miter) { MethodItemEntry* mentry = &*miter; if (entry_to_addr.find(mentry) == entry_to_addr.end()) { continue; } if (mentry->type == MFLOW_DEX_OPCODE) { auto opcode = mentry->dex_insn->opcode(); if (dex_opcode::is_switch(opcode)) { multi_branches.push_back(mentry); } } if (mentry->type == MFLOW_TARGET) { BranchTarget* bt = mentry->target; if (bt->type == BRANCH_MULTI) { multis[bt->src].push_back(bt); multi_targets[bt] = entry_to_addr.at(mentry); // We can't fix the primary switch opcodes address until we emit // the fopcode, which comes later. } else if (bt->type == BRANCH_SIMPLE && dex_opcode::is_branch(bt->src->dex_insn->opcode())) { MethodItemEntry* branch_op_mie = bt->src; auto branch_addr = entry_to_addr.find(branch_op_mie); always_assert_log(branch_addr != entry_to_addr.end(), "%s refers to nonexistent branch instruction", SHOW(*mentry)); int32_t branch_offset = entry_to_addr.at(mentry) - branch_addr->second; needs_resync |= !encode_offset(m_ir_list, mentry, branch_offset); } } } if (needs_resync) { return false; } size_t num_align_nops{0}; auto& opout = code->reset_instructions(); for (auto& mie : *m_ir_list) { // We are assuming that fill-array-data-payload opcodes are always at // the end of the opcode stream (we enforce that during instruction // lowering). I.e. they are only followed by other fill-array-data-payload // opcodes. So adjusting their addresses here does not require re-running // branch relaxation. entry_to_addr.at(&mie) += num_align_nops; if (mie.type == MFLOW_TARGET && mie.target->src->dex_insn->opcode() == DOPCODE_FILL_ARRAY_DATA) { // This MFLOW_TARGET is right before a fill-array-data-payload opcode, // so we should make sure its address is aligned if (entry_to_addr.at(&mie) & 1) { opout.push_back(new DexInstruction(DOPCODE_NOP)); ++entry_to_addr.at(&mie); ++num_align_nops; } mie.target->src->dex_insn->set_offset(entry_to_addr.at(&mie) - entry_to_addr.at(mie.target->src)); continue; } if (mie.type != MFLOW_DEX_OPCODE) { continue; } TRACE(MTRANS, 6, "Emitting insn %s\n", SHOW(mie.dex_insn)); opout.push_back(mie.dex_insn); } addr += num_align_nops; TRACE(MTRANS, 5, "Emitting multi-branches\n"); // Step 3, generate multi-branch fopcodes for (auto multiopcode : multi_branches) { auto& targets = multis[multiopcode]; auto multi_insn = multiopcode->dex_insn; std::sort(targets.begin(), targets.end(), multi_target_compare_case_key); always_assert_log( !targets.empty(), "need to have targets for %s", SHOW(*multiopcode)); if (sufficiently_sparse(targets)) { // Emit sparse. const size_t count = (targets.size() * 4) + 2; auto sparse_payload = std::make_unique<uint16_t[]>(count); sparse_payload[0] = FOPCODE_SPARSE_SWITCH; sparse_payload[1] = targets.size(); uint32_t* spkeys = (uint32_t*)&sparse_payload[2]; uint32_t* sptargets = (uint32_t*)&sparse_payload[2 + (targets.size() * 2)]; for (BranchTarget* target : targets) { *spkeys++ = target->case_key; *sptargets++ = multi_targets[target] - entry_to_addr.at(multiopcode); } // Emit align nop if (addr & 1) { DexInstruction* nop = new DexInstruction(DOPCODE_NOP); opout.push_back(nop); addr++; } // Insert the new fopcode... DexInstruction* fop = new DexOpcodeData(sparse_payload.get(), (int)(count - 1)); opout.push_back(fop); // re-write the source opcode with the address of the // fopcode, increment the address of the fopcode. multi_insn->set_offset(addr - entry_to_addr.at(multiopcode)); multi_insn->set_opcode(DOPCODE_SPARSE_SWITCH); addr += count; } else { // Emit packed. const uint64_t size = get_packed_switch_size(targets); always_assert(size <= std::numeric_limits<uint16_t>::max()); const size_t count = (size * 2) + 4; auto packed_payload = std::make_unique<uint16_t[]>(count); packed_payload[0] = FOPCODE_PACKED_SWITCH; packed_payload[1] = size; uint32_t* psdata = (uint32_t*)&packed_payload[2]; int32_t next_key = *psdata++ = targets.front()->case_key; for (BranchTarget* target : targets) { // Fill in holes with relative offsets that are falling through to the // instruction after the switch instruction for (; next_key != target->case_key; ++next_key) { *psdata++ = 3; // packed-switch statement is three code units } *psdata++ = multi_targets[target] - entry_to_addr.at(multiopcode); ++next_key; } // Emit align nop if (addr & 1) { DexInstruction* nop = new DexInstruction(DOPCODE_NOP); opout.push_back(nop); addr++; } // Insert the new fopcode... DexInstruction* fop = new DexOpcodeData(packed_payload.get(), (int)(count - 1)); opout.push_back(fop); // re-write the source opcode with the address of the // fopcode, increment the address of the fopcode. multi_insn->set_offset(addr - entry_to_addr.at(multiopcode)); multi_insn->set_opcode(DOPCODE_PACKED_SWITCH); addr += count; } } // Step 4, emit debug entries TRACE(MTRANS, 5, "Emitting debug entries\n"); auto debugitem = code->get_debug_item(); if (debugitem) { gather_debug_entries(m_ir_list, entry_to_addr, &debugitem->get_entries()); } // Step 5, try/catch blocks TRACE(MTRANS, 5, "Emitting try items & catch handlers\n"); auto& tries = code->get_tries(); tries.clear(); MethodItemEntry* active_try = nullptr; for (auto& mentry : *m_ir_list) { if (mentry.type != MFLOW_TRY) { continue; } auto& tentry = mentry.tentry; if (tentry->type == TRY_START) { always_assert(active_try == nullptr); active_try = &mentry; continue; } redex_assert(tentry->type == TRY_END); auto try_end = &mentry; auto try_start = active_try; active_try = nullptr; always_assert_log( try_start != nullptr, "unopened try_end found: %s", SHOW(*try_end)); always_assert_log(try_start->tentry->catch_start == try_end->tentry->catch_start, "mismatched try start (%s) and end (%s)", SHOW(*try_start), SHOW(*try_end)); auto start_addr = entry_to_addr.at(try_start); auto end_addr = entry_to_addr.at(try_end); if (start_addr == end_addr) { continue; } DexCatches catches; for (auto mei = try_end->tentry->catch_start; mei != nullptr; mei = mei->centry->next) { if (mei->centry->next != nullptr) { always_assert(mei->centry->catch_type != nullptr); } catches.emplace_back(mei->centry->catch_type, entry_to_addr.at(mei)); } split_and_insert_try_regions(start_addr, end_addr, catches, &tries); } always_assert_log(active_try == nullptr, "unclosed try_start found"); std::sort(tries.begin(), tries.end(), [](const std::unique_ptr<DexTryItem>& a, const std::unique_ptr<DexTryItem>& b) { return a->m_start_addr < b->m_start_addr; }); return true; }