diff options
author | Nguyễn Gia Phong <cnx@loang.net> | 2024-10-31 14:59:17 +0900 |
---|---|---|
committer | Nguyễn Gia Phong <cnx@loang.net> | 2024-10-31 14:59:17 +0900 |
commit | 95f3fe2b800940f75949b069f50a2da4712435fd (patch) | |
tree | 5d666af0ce716adcbb740f3cb4fd5a4688604c68 | |
parent | 11ab3cc687bfeb64e1bb223e4e690fe423e6a15c (diff) | |
download | taosc-95f3fe2b800940f75949b069f50a2da4712435fd.tar.gz |
Glue things together into a pipeline
-rw-r--r-- | Makefile | 34 | ||||
-rw-r--r-- | collect.c | 47 | ||||
-rw-r--r-- | fix.m4 | 46 | ||||
-rw-r--r-- | patch.c | 102 | ||||
-rw-r--r-- | scout.cc | 256 | ||||
-rw-r--r-- | synth | 55 | ||||
-rw-r--r-- | taosc.cc | 279 |
7 files changed, 535 insertions, 284 deletions
diff --git a/Makefile b/Makefile index d768151..4ee0a88 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,32 @@ -CXXFLAGS += -std=c++20 -LDFLAGS += -lcommon -linstructionAPI -lparseAPI # dyninst +CXXFLAGS += -g -std=c++23 -Wextra -Werror +LDFLAGS += -lcommon -ldyninstAPI -linstructionAPI -lparseAPI # dyninst +PREFIX ?= /usr/local -.PHONY: all clean -all: taosc +BIN := fix scout synth +BIN_PREFIX := $(DESTDIR)$(PREFIX)/bin/taosc- +DATA := collect patch +DATA_DIR := $(DESTDIR)$(PREFIX)/share/taosc + +.PHONY: all clean install + +all: $(BIN) $(DATA) clean: - rm taosc + rm -f $(BIN) $(DATA) + +fix: fix.m4 + m4 -D DATA_DIR=${DATA_DIR} $< > $@ + +collect: collect.c + e9compile $< + +patch: patch.c + e9compile $< + +install: $(addprefix $(BIN_PREFIX),$(BIN)) $(addprefix $(DATA_DIR)/,$(DATA)) + +$(BIN_PREFIX)%: % + install -Dm 755 $< $@ + +$(DATA_DIR)/%: % + install -Dm 644 $< $@ diff --git a/collect.c b/collect.c new file mode 100644 index 0000000..875213a --- /dev/null +++ b/collect.c @@ -0,0 +1,47 @@ +/* + * Live variable collector + * Copyright (C) 2021, 2023 Gregory James Duck + * Copyright (C) 2024 Nguyễn Gia Phong + * + * This file is part of taosc. + * + * Taosc is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Taosc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with taosc. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "stdlib.c" + +char *path; + +void init(int argc, const char *const *argv, const char **envp) +{ + environ = envp; + path = getenv("TAOSC_COLLECT"); /* TODO: fopen */ + setvbuf(stderr, NULL, _IOFBF, 0); +} + + +void log(const struct STATE *state) +{ + static mutex_t mutex = MUTEX_INITIALIZER; + if (mutex_lock(&mutex) < 0) + return; + clearerr(stderr); + fprintf(stderr, "[begin]\n"); + const int64_t *const env = (const int64_t *) state; + for (unsigned char i = 1; i < sizeof(state) / sizeof(int64_t); ++i) + fprintf(stderr, "%hhu %lld\n", i, env[i]); + fprintf(stderr, "[end]\n"); + fflush(stderr); + mutex_unlock(&mutex); +} diff --git a/fix.m4 b/fix.m4 new file mode 100644 index 0000000..cbf3725 --- /dev/null +++ b/fix.m4 @@ -0,0 +1,46 @@ +#!/bin/sh +# Patcher +# Copyright (C) 2024 Nguy?n Gia Phong +# +# This file is part of taosc. +# +# Taosc is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Taosc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with taosc. If not, see <https://www.gnu.org/licenses/>. + +set -e +if test $# -ne 3 +then + echo Usage: taosc-fix binary instruction-address working-directory + exit 1 +fi +binary="$(realpath $1)" +address="$2" +wd="$(realpath $3)" + +pushd DATA_DIR > /dev/null +trap 'popd > /dev/null' EXIT +collect="$wd/$(basename $binary).collect" +e9tool -M addr=$address -P 'log(state)@collect' -o "$collect.orig" "$binary" +afl-dyninst -i "$collect.orig" -o "$collect" +patched="$wd/$(basename $binary).patched" +e9tool -M addr=$address -P 'if dest(state)@patch goto' -o "$patched" "$binary" + +taosc-scout "$binary" "$address" > "$wd/destinations" +#for dest in $(taosc-slice "$binary" "$address") +#do +# for dest in $(taosc-slice "$binary" "$address") +# do +# TAOSC_PREDICATE="<v15p0" TAOSC_DESTINATION=$dest $patched\ +# -d /home/cnx/Sauces/apr/vulnfix/data/binutils/cve_2017_14745/exploit +# done +#done diff --git a/patch.c b/patch.c new file mode 100644 index 0000000..2aa697b --- /dev/null +++ b/patch.c @@ -0,0 +1,102 @@ +/* + * Dynamic patch + * Copyright (C) 2024 Nguyễn Gia Phong + * + * This file is part of taosc. + * + * Taosc is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Taosc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with taosc. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "stdlib.c" + +const char *taosc_predicate; +const void *taosc_destination; + +/* + * Get an environment variable and parse as a number. + * Return 0 on error. + */ +uint64_t getenvull(const char *name) +{ + const char *const s = getenv(name); + if (s == NULL) + return 0ULL; + errno = 0; + const uint64_t u = strtoull(s, NULL, 0); + if (errno) + return 0ULL; + return u; +} + +void init(int argc, const char *const *argv, const char **envp) +{ + environ = envp; + taosc_predicate = getenv("TAOSC_PREDICATE"); + if (taosc_predicate == NULL) + taosc_predicate = "p0"; /* false */ + taosc_destination = (void *) getenvull("TAOSC_DESTINATION"); +} + +/* Parse *p as an integer. */ +int64_t scani(const char **p) +{ + int64_t i = 0; + for (; **p >= '0' && **p <= '9'; ++*p) + i = i * 10 + **p - '0'; + return i; +} + +/* Parse and evaluate *ptr in a prefix Polish notation, recursively. */ +int64_t eval(const char **ptr, const int64_t *env) +{ + const char op = *(*ptr)++; + switch (op) { + case 'n': /* negative integer */ + return -scani(ptr); + case 'p': /* positive integer */ + return scani(ptr); + case 'v': /* variable look up */ + return env[scani(ptr)]; + } + const bool eq = **ptr == '='; + *ptr += eq; + const int64_t a = eval(ptr, env); + const int64_t b = eval(ptr, env); + switch (op) { + case '=': + return a == b; + case '!': + return a != b; + case '>': + return eq ? (a >= b) : (a > b); + case '<': + return eq ? (a <= b) : (a < b); + case '+': + return a + b; + case '-': + return a - b; + case '*': + return a * b; + case '/': + return a / b; + default: + __builtin_unreachable(); + } +} + +const void *dest(const struct STATE *state) +{ + const char *tmp = taosc_predicate; + return eval(&tmp, (const int64_t *) state) ? NULL : taosc_destination; +} diff --git a/scout.cc b/scout.cc new file mode 100644 index 0000000..6c3ddb5 --- /dev/null +++ b/scout.cc @@ -0,0 +1,256 @@ +// Patch's jump destinations searcher +// Copyright (C) 2024 Nguyễn Gia Phong +// +// This file is part of taosc. +// +// Taosc is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Taosc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with taosc. If not, see <https://www.gnu.org/licenses/>. + +// Dyninst headers +#include <CFG.h> +#include <CodeObject.h> +#include <Graph.h> +#include <Instruction.h> +#include <InstructionDecoder.h> +#include <slicing.h> + +using AbstractRegion = Dyninst::AbsRegion; +using Address = Dyninst::Address; +using AssignmentConverter = Dyninst::AssignmentConverter; +using AssignmentPtr = Dyninst::Assignment::Ptr; +using Block = Dyninst::ParseAPI::Block; +using CodeObject = Dyninst::ParseAPI::CodeObject; +using CodeRegion = Dyninst::ParseAPI::CodeRegion; +using CodeSource = Dyninst::ParseAPI::SymtabCodeSource; +using Edge = Dyninst::Edge; +using EdgeIterator = Dyninst::EdgeIterator; +using Function = Dyninst::ParseAPI::Function; +using Graph = Dyninst::Graph; +using Instruction = Dyninst::InstructionAPI::Instruction; +using InstructionCategory = Dyninst::InstructionAPI::InsnCategory; +using InstructionDecoder = Dyninst::InstructionAPI::InstructionDecoder; +using NodeIterator = Dyninst::NodeIterator; +using SliceNode = Dyninst::SliceNode; +using Slicer = Dyninst::Slicer; + +#include <cassert> +#include <filesystem> +#include <functional> +#include <iostream> +#include <map> +#include <queue> + +/// Collect elements from given iterator into a vector +template <class Element, class Iterator> + std::vector <Element*> + range (auto iter) + { + Iterator begin, end; + std::vector <Element*> result; + for (iter (begin, end); begin != end; ++begin) + result.push_back (static_cast <Element*> ((*begin).get ())); + return result; + } + +class SlicerHelper + { + CodeSource& cs; + InstructionDecoder decoder; + Slicer::Predicates predicates; + AssignmentConverter ac {true, true}; + std::set <Address> seen; + std::vector <std::vector <Address>> bfs_slices; + + public: + SlicerHelper (CodeSource& cs) + : cs {cs}, + decoder {(const void*) nullptr, 1, cs.getArch ()}, + ac {true, true} // enable caching and stack analysis + { + this->predicates.setSearchForControlFlowDep(true); + } + + /// Decode instruction at given address + Instruction + decode (Address addr) + { + auto const& insn = this->decoder.decode ((const unsigned char*) + this->cs.getPtrToInstruction (addr)); + assert (insn.size () > 0); + return insn; + } + + /// Collect the interprocedure backward slice at addr in BFS order + void + slice (Instruction const& insn, Address addr, Function* fun, Block* blk) + { + std::vector <AssignmentPtr> assignments; + this->ac.convert (insn, addr, fun, blk, assignments); + if (assignments.empty ()) + return; + for (auto const& asgn : assignments) + { + this->bfs_slices.emplace_back (); + Slicer s {asgn, blk, fun}; + auto const& slice = s.backwardSlice (this->predicates); +#define ITER(i, E, I, x, f) \ + for (auto const& i : range <E, I> ([x] (auto& b, auto& e) { x->f (b, e); })) + ITER (node, SliceNode, NodeIterator, slice, exitNodes) + { + this->seen.insert (node->addr ()); + std::queue <SliceNode*> q; // breadth-first traversal + q.push (node); + while (!q.empty ()) + { + auto const& parent = q.front (); + q.pop (); + this->bfs_slices.back ().push_back (parent->addr ()); + ITER (edge, Edge, EdgeIterator, parent, ins) +#undef ITER + { + auto const& child = edge->source (); + if (this->seen.count (child->addr ()) > 0) + continue; + this->seen.insert (child->addr ()); + q.push (static_cast <SliceNode*> (child.get ())); + } + } + } + } + } + + /// Flatten stored BFS slices in round-robin order + std::vector <Address> const + slice_zip () + { + std::vector <Address> result; + size_t n = 0; + for (auto const& v : this->bfs_slices) + { + if (result.empty () || v[0] != result.back ()) + result.push_back (v[0]); + n = std::max (n, v.size ()); + } + for (size_t i = 1; i < n; ++i) + for (auto const& v : this->bfs_slices) + if (i < v.size ()) + result.push_back (v[i]); + return result; + } + }; + +/// Find next basic block's entry after given address, reparsing if necessary +Block* +next_block (CodeObject& co, CodeRegion* region, Address address) +{ + auto blk = co.findBlockByEntry (region, address); + if (blk != nullptr) + return blk; + co.parse (address, true); + blk = co.findBlockByEntry (region, address); + return (blk != nullptr) ? blk : co.findNextBlock (region, address); +} + +/// Find block containing given address +Block* +find_block (CodeSource& cs, CodeObject& co, Address target_addr) +{ + if (!cs.isCode (target_addr)) + { + std::cerr << std::hex << target_addr + << " does not point to an instruction\n"; + return nullptr; + } + std::set <CodeRegion*> regions; + if (cs.findRegions (target_addr, regions) != 1) + { + std::cerr << "expected 1 region containing instruction, found " + << regions.size () << '\n'; + return nullptr; + } + for (auto const& region : regions) + { + std::set <Block*> blocks; + if (co.findBlocks (region, target_addr, blocks) > 0) + for (auto const& blk : blocks) // TODO: choose the best block + return blk; + + auto* blk = next_block (co, region, region->low ()); + while (blk != nullptr && target_addr > blk->last ()) + blk = next_block (co, region, blk->end ()); + if (blk == nullptr) + return nullptr; + assert (target_addr >= blk->start () && target_addr < blk->end ()); + return blk; + } +#if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) + std::unreachable (); +#else + __builtin_unreachable(); // GCC or Clang +#endif +} + +/// Slice backward from return instructions +std::vector <Address> const +returns_slice (CodeSource& cs, Function* fun, Address target_addr) +{ + SlicerHelper helper {cs}; + for (auto const& blk : fun->blocks ()) + for (auto [addr, step] = std::tuple {blk->start (), 0ul}; + addr < blk->end (); + addr += step) + { + auto const& insn = helper.decode (addr); + step = insn.size (); + if (insn.getCategory () == InstructionCategory::c_ReturnInsn) + helper.slice (insn, addr, fun, blk); + } + return helper.slice_zip (); +} + +int +main (int argc, char** argv) +{ + if (argc != 3) + { + std::cerr << "Usage: " << std::filesystem::path (argv[0]).filename () + << " binary instruction-address\n"; + return -1; + } + CodeSource cs {argv[1]}; + Address target_addr; + { + std::stringstream ss; + ss << std::hex << argv[2]; + ss >> target_addr; + } + CodeObject co {&cs}; + co.parse (); // parsed functions have same lifetime as co + auto const& block = find_block (cs, co, target_addr); + if (block == nullptr) + { + std::cerr << "block containing instruction not found\n"; + return -1; + } + std::vector<Function*> functions; + block->getFuncs (functions); + if (functions.size () != 1) + { + std::cerr << "0 or multiple functions containing instruction found\n"; + return -1; + } + for (auto* fun : functions) + for (auto const& addr : returns_slice (cs, fun, target_addr)) + std::cout << std::hex << addr << '\n'; + return 0; +} diff --git a/synth b/synth new file mode 100644 index 0000000..4594d0a --- /dev/null +++ b/synth @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Patch's predicate synthesizer +# Copyright (C) 2024 Nguyễn Gia Phong +# +# This file is part of taosc. +# +# Taosc is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Taosc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with taosc. If not, see <https://www.gnu.org/licenses/>. + +from contextlib import redirect_stderr +from os import devnull +from os.path import basename +from pathlib import Path +from sys import argv, stdout + +from pacfix import learn +from pacfix.invariant import INVARIANT_MAP, InvariantType +from pacfix.utils import get_live_vars, get_valuations, parse_valuation + + +def encode(inv, file): + if inv.inv_type == InvariantType.VAR: + file.write(f"v{inv.data}") + elif inv.inv_type == InvariantType.CONST: + file.write(f"{'n' if inv.data < 0 else 'p'}{abs(inv.data)}") + else: + file.write(INVARIANT_MAP[inv.inv_type]) + encode(inv.left, file) + encode(inv.right, file) + + +if len(argv) != 3: + print(f'usage: taosc-synth input-dir delta') + exit(1) +input_dir = Path(argv[1]) +delta = float(argv[2]) + +with open(input_dir / 'vars') as f: live_vars = get_live_vars(f) +vals_neg, vals_pos = parse_valuation(get_valuations(input_dir / "neg"), + get_valuations(input_dir / "pos")) +with open(devnull, 'w') as f, redirect_stderr(f): + result = learn(live_vars, vals_neg, vals_pos, delta) +for i in result.inv_mgr.invs: + encode(i, stdout) + print() diff --git a/taosc.cc b/taosc.cc deleted file mode 100644 index 857fcf5..0000000 --- a/taosc.cc +++ /dev/null @@ -1,279 +0,0 @@ -// Slicing-based binary fault localizer and patch searcher -// Copyright (C) 2024 Nguyễn Gia Phong -// -// This file is part of taosc. -// -// Taosc is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Taosc is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with taosc. If not, see <https://www.gnu.org/licenses/>. - -// Dyninst headers -#include <CFG.h> -#include <CodeObject.h> -#include <Graph.h> -#include <Instruction.h> -#include <InstructionDecoder.h> -#include <slicing.h> - -using AbstractRegion = Dyninst::AbsRegion; -using Address = Dyninst::Address; -using AssignmentConverter = Dyninst::AssignmentConverter; -using AssignmentPtr = Dyninst::Assignment::Ptr; -using Block = Dyninst::ParseAPI::Block; -using CodeObject = Dyninst::ParseAPI::CodeObject; -using CodeRegion = Dyninst::ParseAPI::CodeRegion; -using CodeSource = Dyninst::ParseAPI::SymtabCodeSource; -using Edge = Dyninst::Edge; -using EdgeIterator = Dyninst::EdgeIterator; -using Function = Dyninst::ParseAPI::Function; -using Graph = Dyninst::Graph; -using Instruction = Dyninst::InstructionAPI::Instruction; -using InstructionDecoder = Dyninst::InstructionAPI::InstructionDecoder; -using InstructionCategory = Dyninst::InstructionAPI::InsnCategory; -using NodeIterator = Dyninst::NodeIterator; -using SliceNode = Dyninst::SliceNode; -using Slicer = Dyninst::Slicer; - -#include <cassert> -#include <functional> -#include <iostream> -#include <map> -#include <queue> - -class SearchingAll : public Slicer::Predicates - { - public: - std::vector <Function*> - followCallBackward (Block* caller, - CallStack_t& call_stack, - AbstractRegion argument) override - { - std::vector <Function*> callers; - caller->getFuncs (callers); - return callers; - } - }; - -template <class Element, class Iterator> - std::vector <Element*> - sorted (auto iter) - { - Iterator begin, end; - std::vector <Element*> result; - for (iter (begin, end); begin != end; ++begin) - result.push_back (static_cast <Element*> ((*begin).get ())); - return result; - } - -template <class SlicerPredicates> - class SlicerHelper - { - CodeSource& cs; - InstructionDecoder decoder; - SlicerPredicates predicates; - AssignmentConverter ac {true, true}; - std::set <Address> seen; - std::vector <std::vector <Address>> bfs_slices; - - public: - SlicerHelper (CodeSource& cs) - : cs {cs}, - decoder {(const void*) nullptr, 1, cs.getArch ()}, - ac {true, true} // enable caching and stack analysis - { - this->predicates.setSearchForControlFlowDep(true); - } - - Instruction - decode (Address addr) - { - auto const& insn = this->decoder.decode ((const unsigned char *) - this->cs.getPtrToInstruction (addr)); - assert (insn.size () > 0); - return insn; - } - -#define ITER(i, E, I, x, f) \ - for (auto const& i : sorted <E, I> ([x] (auto& b, auto& e) { x->f (b, e); })) - - void - slice (Instruction const& insn, Address addr, Function* fun, Block* blk) - { - std::vector <AssignmentPtr> assignments; - this->ac.convert (insn, addr, fun, blk, assignments); - if (assignments.empty ()) - return; - for (auto const& asgn : assignments) - { - this->bfs_slices.emplace_back (); - Slicer s {asgn, blk, fun}; - auto const& slice = s.backwardSlice (this->predicates); - ITER (node, SliceNode, NodeIterator, slice, exitNodes) - { - this->seen.insert (node->addr ()); - std::queue <SliceNode*> q; // breadth-first traversal - q.push (node); - while (!q.empty ()) - { - auto const& parent = q.front (); - q.pop (); - this->bfs_slices.back ().push_back (parent->addr ()); - ITER (edge, Edge, EdgeIterator, parent, ins) - { - auto const& child = edge->source (); - if (this->seen.count (child->addr ()) > 0) - continue; - this->seen.insert (child->addr ()); - q.push (static_cast <SliceNode*> (child.get ())); - } - } - } - } - } - -#undef ITER - - std::vector <Address> - slice_zip () - { - std::vector <Address> result; - size_t n = 0; - for (auto const& v : this->bfs_slices) - { - if (result.empty () || v[0] != result.back ()) - result.push_back (v[0]); - n = std::max (n, v.size ()); - } - for (size_t i = 1; i < n; ++i) - for (auto const& v : this->bfs_slices) - if (i < v.size ()) - result.push_back (v[i]); - return result; - } - }; - -Block* -next_block (CodeObject& co, CodeRegion* region, Address address) -{ - auto blk = co.findBlockByEntry (region, address); - if (blk != nullptr) - return blk; - co.parse (address, true); - blk = co.findBlockByEntry (region, address); - return (blk != nullptr) ? blk : co.findNextBlock (region, address); -} - -Block* -find_block (CodeSource& cs, CodeObject& co, Address target_addr) -{ - if (!cs.isCode (target_addr)) - { - std::cerr << std::hex << target_addr - << " does not point to an instruction\n"; - return nullptr; - } - std::set <CodeRegion*> regions; - cs.findRegions (target_addr, regions); - if (regions.size () != 1) - { - std::cerr << "expected 1 region containing instruction, found " - << regions.size () << '\n'; - return nullptr; - } - for (auto const& region : regions) - { - std::set <Block*> blocks; - co.findBlocks (region, target_addr, blocks); - if (blocks.size () == 1) - for (auto const& blk : blocks) - return blk; - - auto blk = next_block (co, region, region->low ()); - while (blk != nullptr && target_addr > blk->last ()) - blk = next_block (co, region, blk->end ()); - if (blk == nullptr) - return nullptr; - assert (target_addr >= blk->start () && target_addr < blk->end ()); - return blk; - } - // std::unreachable(); - return nullptr; -} - -std::vector <Address> const -patch_locations (CodeSource& cs, - std::vector<Function*> const& functions, - Block* block, - Address target_addr) -{ - SlicerHelper <SearchingAll> helper {cs}; - std::map <Address, std::string> locations; - for (auto const& fun : functions) - helper.slice (helper.decode (target_addr), target_addr, fun, block); - return helper.slice_zip (); -} - -std::vector <Address> const -return_values (CodeSource& cs, - std::vector<Function*> const& functions, - Block* block, - Address target_addr) -{ - SlicerHelper <Slicer::Predicates> helper {cs}; - for (auto const& fun : functions) - for (auto const& blk : fun->blocks ()) - for (auto [addr, step] = std::tuple {blk->start (), 0ul}; - addr < blk->end (); - addr += step) - { - auto const& insn = helper.decode (addr); - step = insn.size (); - if (insn.getCategory () == InstructionCategory::c_ReturnInsn) - helper.slice (insn, addr, fun, blk); - } - return helper.slice_zip (); -} - -int -main (int argc, char** argv) -{ - if (argc != 3) - { - std::cerr << "Usage: " << argv[0] - << " <binary path> <instruction address>\n"; - return -1; - } - CodeSource cs {argv[1]}; - Address target_addr; - { - std::stringstream ss; - ss << std::hex << argv[2]; - ss >> target_addr; - } - CodeObject co {&cs}; - co.parse (); // parsed functions have same lifetime as co - auto const& block = find_block (cs, co, target_addr); - if (block == nullptr) - { - std::cerr << "block containing instruction not found\n"; - return -1; - } - std::vector<Function*> functions; - block->getFuncs (functions); - - for (auto const& addr : patch_locations (cs, functions, block, target_addr)) - std::cout << std::hex << addr << '\n'; - std::cout << "------\n"; - for (auto const& addr : return_values (cs, functions, block, target_addr)) - std::cout << std::hex << addr << '\n'; - return 0; -} |