--- title: angrメモ description: シンボリック実行エンジンangrをPythonでスクリプティングするときに便利な使い方メモ。angrに慣れている人には便利なはず~ --- angr メモ ==== 筆者:[友利奈緒(@K_atc)](https://twitter.com/K_atc) インストール ---- angr 7を前提とする。 ### apt install 以下パッケージをインストールしないとだめかも。ptpythonインストール前提。 ```bash ### basic commands sudo apt install asciinema vim curl wget tmux zsh ### install build requirements sudo apt install build-essential libpcap-dev libpcap0.8 ### python & pip requrements sudo apt install python python-pip libffi-dev libncurses5-dev sudo -H pip install --upgrade pip ``` ### install on vanilla python2 angr 6を使っている人は更新した方がいいかも。 ```bash sudo -H pip install angr ipdb ### to update angr sudo -H pip install -U angr ``` pipインストール推奨パッケージ: * ptpython:pythonよりも扱いが楽な対話的インタプリタ− ### install on pypy (Optional) ```bash ### instal pypy sudo apt install pypy pypy-dev ### install pip cd /tmp curl -O https://bootstrap.pypa.io/get-pip.py sudo -H pypy get-pip.py ### install angr, ipdb sudo -H pypy -m pip install angr ipdb ``` ### 実行エラー #### ImportError: cannot import name arm Ubuntu 16.04だと必ず起こるエラー。以下対処方法。 see https://github.com/angr/angr/issues/52 ```bash ### for python2 sudo -H pip install -I --no-use-wheel capstone ### for pypy sudo -H pypy -m pip install -I --no-use-wheel capstone ``` ### 参考情報:angr-dev 公式のインストールスクリプト https://github.com/angr/angr-dev ### angr 7に移行するための情報 * [Migrating to angr 7](https://github.com/angr/angr-doc/blob/master/MIGRATION.md) よく参照するangr-doc ---- * Options: https://github.com/angr/angr-doc/blob/master/docs/appendices/options.md * Cheatsheet: https://github.com/angr/angr-doc/blob/master/CHEATSHEET.md * Speed: https://github.com/angr/angr-doc/blob/master/docs/speed.md * claripy: https://github.com/angr/angr-doc/blob/master/docs/claripy.md 変数名 --- これらの頻出の変数名は覚えておくとよい。 ### proj project。`angr.Project`したインスタンスの変数に付ける名前。 ### simgr Simulation Manager。シンボリック実行における実行ループを管理。 ### lpg local path group。各stashを保持。 ### tpg thread path group。ThreadingのExplore techniqueで出てくる。 FAQ ---- ### すぐに(Round 0で)探索が終了する 事前に与えた制約に矛盾があるせいかも。 angr.manager --- ### angr.manager.l `ipdb> filter(lambda x: not x.startswith('_'), dir(angr.manager.l))` `['addFilter', 'addHandler', 'callHandlers', 'critical', 'debug', 'disabled', 'error', 'exception', 'fatal', 'filter', 'filters', 'findCaller', 'getChild', 'getEffectiveLevel', 'handle', 'handlers', 'info', 'isEnabledFor', 'level', 'log', 'makeRecord', 'manager', 'name', 'parent', 'propagate', 'removeFilter', 'removeHandler', 'root', 'setLevel', 'warn', 'warning']` ### angr.manager.logging `ipdb> filter(lambda x: not x.startswith('_'), dir(angr.manager.logging))` `['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR', 'FATAL', 'FileHandler', 'Filter', 'Filterer', 'Formatter', 'Handler', 'INFO', 'LogRecord', 'Logger', 'LoggerAdapter', 'Manager', 'NOTSET', 'NullHandler', 'PlaceHolder', 'RootLogger', 'StreamHandler', 'WARN', 'WARNING', 'addLevelName', 'atexit', 'basicConfig', 'cStringIO', 'captureWarnings', 'codecs', 'collections', 'critical', 'currentframe', 'debug', 'disable', 'error', 'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass', 'info', 'log', 'logMultiprocessing', 'logProcesses', 'logThreads', 'makeLogRecord', 'os', 'raiseExceptions', 'root', 'setLoggerClass', 'shutdown', 'sys', 'thread', 'threading', 'time', 'traceback', 'warn', 'warning', 'warnings', 'weakref']` ### デバッグログをファイルに保存する 以下のコードを冒頭に加えると、angr.logというファイルに、上書き('w')でログが書き込まれるようになる。追記がよかったらmodeを'a'にすること。 ```python angr.manager.l.setLevel("DEBUG") logging = angr.manager.logging log_handler = logging.FileHandler("angr.log", mode='w') log_handler.setLevel(logging.DEBUG) log_handler.setFormatter(logging.Formatter('%(levelname)-7s | %(asctime)-23s | %(name)-8s | %(message)s')) angr.manager.l.addHandler(log_handler) ``` #### フォーマット https://github.com/angr/angr/blob/5c53804fa972f93418fea3f21877854ca18df6ad/angr/misc/loggers.py#L11 ```python self.handler.setFormatter(logging.Formatter('%(levelname)-7s | %(asctime)-23s | %(name)-8s | %(message)s')) ``` SimProcedure ---- ``` ipdb> angr.SIM_PROCEDURES.keys() ['msvcr', 'ntdll', 'uclibc', 'libc', 'stubs', 'testing', 'win32', 'win_user32', 'linux_loader', 'cgc', 'posix', 'linux_kernel', 'glibc'] ipdb> angr.SIM_PROCEDURES['libc'].keys() ['strncmp', 'rand', 'malloc', 'sscanf', 'snprintf', 'vsnprintf', 'fgetc', 'realloc', 'fread', 'fclose', 'strcat', 'abort', 'fprintf', 'printf', 'fgets', 'fflush', 'fopen', 'feof', 'strncpy', 'getchar', 'strchr', 'fputc', 'puts', 'fwrite', 'scanf', 'fseek', 'system', 'fputs', 'ftell', 'exit', 'sprintf', 'putc', 'memcmp', 'srand', 'strtol', 'memset', 'FormatParser', 'rewind', 'free', 'atoi', 'calloc', 'putchar', 'strlen', 'memcpy', 'perror', 'strstr', 'strcpy', 'tmpnam', 'ungetc', 'strcmp', 'setvbuf'] ``` proj.loader ---- ### proj.loader.find_symbol 属性一覧 `dir(proj.loader.find_symbol('main'))` `['TYPE_FUNCTION', 'TYPE_NONE', 'TYPE_OBJECT', 'TYPE_OTHER', 'TYPE_SECTION', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__subclasshook__', '__weakref__', 'addr', 'binding', 'demangled_name', 'elftype', 'is_common', 'is_export', 'is_extern', 'is_forward', 'is_function', 'is_import', 'is_static', 'is_weak', 'linked_addr', 'name', 'owner_obj', 'rebased_addr', 'relative_addr', 'resolve', 'resolve_forwarder', 'resolved', 'resolvedby', 'section', 'size', 'type', 'warned_addr']` #### アドレス解決された値を得る find, avoidにはこちらのアドレスを与える。ラッパー関数を用意すると楽。 ```python rebased_addr = lambda x: proj.loader.find_symbol(x).rebased_addr ``` 使用例) ```python find, avoid = [], [] find += [rebased_addr('check_passed')] ``` #### 相対アドレスを得る ディスアセンブラで表示される方のアドレスを得るならこっち。ラッパー関数を用意すると楽。 ```python relative_addr = lambda x: proj.loader.find_symbol(x).relative_addr ``` ### proj.loader.memory ` ipdb> dir(proj.loader.memory)` `['__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__getstate__', '__hash__', '__init__', '__iter__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__setstate__', '__str__', '__subclasshook__', '__weakref__', '_arch', '_backers', '_cbackers', '_flatten_to_c', '_needs_flattening', '_needs_flattening_personal', '_pointer', '_root', '_stride_repr', '_updates', 'add_backer', 'cbackers', 'get_byte', 'read', 'read_addr_at', 'read_bytes', 'read_bytes_c', 'remove_backer', 'seek', 'stride_repr', 'tell', 'update_backer', 'write_addr_at', 'write_bytes', 'write_bytes_to_backer']` Simulation Manager ---- https://github.com/angr/angr/blob/master/angr/manager.py `angr/manager.py` → Anger Managerのつもり? シンボリック実行エンジンのコア。Simulation Manager(simgr)は実行状態を管理する。下図はangrの実行ループを表している。 ![](https://i.imgur.com/YvhvPZJ.png) ### stashについて Simulation Managerにおいて各探索パスの実行状態を状態別に管理するための配列。 simgr に与えられた初期状態は、 active stash に格納される。active stash は実行可能な状態が格納されるデータ構造である。初期化が終わると、ラウンドと呼ばれる、 active stash から状態を取り出し、個々の状態が指す命令を実行し、状態を更新し、 stash に戻す処理を行う。更新された状態は、状態に応じて active stash の他に、探索成功を意味づけられた状態が入る found stash、探索を中断させた状態が入る avoid stash、実行時エラーが起きた状態が入る errored stash にふるい分けられる。 ### simgr.explore find, avoidをベースにした探索戦略。 ```python TODO ``` #### find/avoid TODO #### filter_func ```python simgr.explore(find=[], step_func=lambda x: x.stash(from_stash='errored', to_stash='found', filter_func=explore_filter)) ``` ログから判断すると state を errored stash から found stash に移動できないように見える。 ### simger.step 痒いところに手が届く良い探索戦略。angrを使い倒すときはこちらがよい。 ```python simgr.step(step_func=your_step_func, until=lambda lpg: len(lpg.errored) > 0) ``` #### until __注意__:ラムダ式を与える!間違えてbool値に評価されるものを入れないようにする!TypeErrorで落ちて原因をつかむのに時間がかかる。 #### find/avoid simgr.stepを使うとexploreのように引数でfind/avoidを与えることができない。以下のようなstep_funcを用意するとよい。 ```python def step_func(lpg): global find, avoid if find is not []: lpg.stash(filter_func=lambda path: path.addr in find, from_stash='active', to_stash='found') if avoid is not []: lpg.stash(filter_func=lambda path: path.addr in avoid, from_stash='active', to_stash='avoid') # lpg.drop(stash='avoid') # memory usage optimization return lpg simgr.step(step_func=step_func, until=lambda x: len(x.found) > 0) ``` Claripy ---- z3ベースの制約ソルバー。 ### API https://github.com/angr/angr-doc/blob/master/docs/claripy.md ### 例 ### BVSで遊ぶ 0バイト目は下位1バイトの位置。 ```python >>> x = claripy.BVS('x', 32) >>> x <BV32 x_0_32> >>> s = claripy.Solver() >>> s.add(claripy.Extract(7, 0, x) == 0x41) (<Bool x_0_32[7:0] == 65>,) >>> s.eval(x, 4) # 与制約を満たす具体値を4つ得る (3393L, 1345L, 321L, 65L) ``` state ---- ### state.regs 実際にはユーザーが使っても効果ないAPIがある。annotateとか。 `print filter(lambda x: not x.startswith('_'), dir(state.regs.r0))` ` ['Concat', 'Extract', 'FULL_SIMPLIFY', 'LITE_SIMPLIFY', 'LShR', 'SDiv', 'SGE', 'SGT', 'SLE', 'SLT', 'SMod', 'UGE', 'UGT', 'ULE', 'ULT', 'UNSIMPLIFIED', 'ana_load', 'ana_store', 'ana_uuid', 'annotate', 'annotations', 'append_annotation', 'append_annotations', 'args', 'cache_key', 'canonicalize', 'cardinality', 'chop', 'concat', 'concrete', 'dbg_is_looped', 'dbg_repr', 'depth', 'get_byte', 'get_bytes', 'insert_annotation', 'insert_annotations', 'intersection', 'ite_burrowed', 'ite_excavated', 'length', 'make_like', 'make_uuid', 'model', 'multivalued', 'op', 'raw_to_bv', 'raw_to_fp', 'recursive_children_asts', 'recursive_leaf_asts', 'remove_annotation', 'remove_annotations', 'replace', 'replace_annotations', 'replace_dict', 'reversed', 'shallow_repr', 'sign_extend', 'simplifiable', 'singlevalued', 'size', 'split', 'structurally_match', 'swap_args', 'symbolic', 'to_bv', 'to_literal', 'uc_alloc_depth', 'uninitialized', 'union', 'uuid', 'val_to_fp', 'variables', 'widen', 'zero_extend'] ` ```python ### シンボリックかを判定 ipdb> state.regs.r0.symbolic True ``` ### state.arch ``` ### arm 32bits ipdb> state.arch.name 'ARMEL' ### intel 64bits ipdb> state.arch <Arch AMD64 (LE)> ipdb> state.arch.name 'AMD64' ``` ### dir(state) stateの属性一覧。 `print filter(lambda x: not x.startswith('_'), dir(state))` ` ['actions', 'add_constraints', 'addr', 'addr_trace', 'ana_load', 'ana_store', 'ana_uuid', 'arch', 'block', 'cgc', 'copy', 'dbg_print_stack', 'downsize', 'events', 'gdb', 'get_plugin', 'globals', 'guards', 'has_plugin', 'history', 'history_iterator', 'inspect', 'ip', 'ip_constraints', 'jumpkind', 'jumpkinds', 'last_actions', 'length', 'libc', 'log', 'make_concrete_int', 'make_uuid', 'mem', 'mem_concrete', 'memory', 'merge', 'mode', 'options', 'os_name', 'plugins', 'posix', 'prepare_callsite', 'project', 'reachable', 'reg_concrete', 'register_plugin', 'registers', 'regs', 'release_plugin', 'satisfiable', 'scratch', 'se', 'set_mode', 'simplify', 'solver', 'stack_pop', 'stack_push', 'stack_read', 'state', 'step', 'targets', 'thumb', 'to_literal', 'trace', 'trim_history', 'uc_manager', 'unicorn', 'uninitialized_access_handler', 'widen', 'with_condition']` ### simgr.found[0] 属性一覧 `['actions', 'add_constraints', 'addr', 'addr_trace', 'ana_load', 'ana_store', 'ana_uuid', 'arch', 'block', 'cgc', 'copy', 'dbg_print_stack', 'downsize', 'events', 'gdb', 'get_plugin', 'globals', 'guards', 'has_plugin', 'history', 'history_iterator', 'inspect', 'ip', 'ip_constraints', 'jumpkind', 'jumpkinds', 'last_actions', 'length', 'libc', 'log', 'make_concrete_int', 'make_uuid', 'mem', 'mem_concrete', 'memory', 'merge', 'mode', 'options', 'os_name', 'plugins', 'posix', 'prepare_callsite', 'project', 'reachable', 'reg_concrete', 'register_plugin', 'registers', 'regs', 'release_plugin', 'satisfiable', 'scratch', 'se', 'set_mode', 'simplify', 'solver', 'stack_pop', 'stack_push', 'stack_read', 'state', 'step', 'targets', 'thumb', 'to_literal', 'trace', 'trim_history', 'uc_manager', 'unicorn', 'uninitialized_access_handler', 'widen', 'with_condition']` ### simgr.deadended[0] 属性一覧 `['actions', 'add_constraints', 'addr', 'addr_trace', 'ana_load', 'ana_store', 'ana_uuid', 'arch', 'block', 'cgc', 'copy', 'dbg_print_stack', 'downsize', 'events', 'gdb', 'get_plugin', 'globals', 'guards', 'has_plugin', 'history', 'history_iterator', 'inspect', 'ip', 'ip_constraints', 'jumpkind', 'jumpkinds', 'last_actions', 'length', 'libc', 'log', 'make_concrete_int', 'make_uuid', 'mem', 'mem_concrete', 'memory', 'merge', 'mode', 'options', 'os_name', 'plugins', 'posix', 'prepare_callsite', 'project', 'reachable', 'reg_concrete', 'register_plugin', 'registers', 'regs', 'release_plugin', 'satisfiable', 'scratch', 'se', 'set_mode', 'simplify', 'solver', 'stack_pop', 'stack_push', 'stack_read', 'state', 'step', 'targets', 'thumb', 'to_literal', 'trace', 'trim_history', 'uc_manager', 'unicorn', 'uninitialized_access_handler', 'widen', 'with_condition']` ### simgr.errored[0] 属性一覧 `['debug', 'error', 'state', 'traceback']` #### errored.error ```python print "error: %r" % (errored.error) for k, v in vars(errored.error).items(): print "\t%s: %r" % (k, v) ``` ``` error: SimSegfaultException(0x609060 (read-miss, original <BV64 0x609060>) addr: 6328416L bbl_addr: 4196067L original_addr: <BV64 0x609060> reason: 'read-miss' ins_addr: 4196090L executed_instruction_count: 5 guard: <Bool True> stmt_idx: 28 ``` #### errored.state ```python print "state: %s" % (errored.state) for k, v in vars(errored.state).items(): if k == "plugins": print "\t%s:" % (k) for k2, v2 in v.items(): # type of errored.state.plugins is dictinary print "\t\t%s: %r" % (k2, v2) else: print "\t%s: %r" % (k, v) ``` ``` state: <SimState @ 0x4006e3> _special_memory_filler: None os_name: 'Linux' ip_constraints: [] _global_condition: None _satisfiable: True project: <Project ./segv> mode: 'symbolic' plugins: callstack: <CallStack (depth 3)> solver_engine: <angr.state_plugins.solver.SimSolver object at 0x7f870c75f7d0> mem: <SimMemView> scratch: <angr.state_plugins.scratch.SimStateScratch object at 0x7f870c75f850> regs: <angr.state_plugins.view.SimRegNameView object at 0x7f870c75f890> libc: <angr.state_plugins.libc.SimStateLibc object at 0x7f870c75f8d0> registers: <angr.state_plugins.symbolic_memory.SimSymbolicMemory object at 0x7f870c75f950> posix: <angr.state_plugins.posix.SimStateSystem object at 0x7f870c75fbd0> memory: <angr.state_plugins.symbolic_memory.SimSymbolicMemory object at 0x7f870c75fc50> inspector: <angr.state_plugins.inspect.SimInspector object at 0x7f870c75fc90> history: <StateHistory @ 0x4006e3> arch: <Arch AMD64 (LE)> options: set(['TRACK_CONSTRAINT_ACTIONS', 'SIMPLIFY_REGISTER_WRITES', 'DO_STORES', 'DO_GETS', 'SYMBOLIC', 'TRACK_CONSTRAINTS', 'EXTENDED_IROP_SUPPORT', 'COMPOSITE_SOLVER', 'OPTIMIZE_IR', 'DO_PUTS', 'SIMPLIFY_MEMORY_WRITES', 'COW_STATES', 'SYMBOLIC_INITIAL_VALUES', 'SUPPORT_FLOATING_POINT', 'DO_OPS', 'DO_LOADS', 'DO_CCALLS', 'TRACK_MEMORY_MAPPING', 'STRICT_PAGE_ACCESS']) uninitialized_access_handler: None ``` ##### error.state.plugins dict型。 ```python errored.state.plugins['solver_engine'].eval(argv1, cast_to=str) ``` #### errored.traceback ```python ``` ``` ipdb> dir(errored.traceback) ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'tb_frame', 'tb_lasti', 'tb_lineno', 'tb_next'] ``` state.se ---- claripyと同等。なのでmainの引数をシンボル化しないときは`import claripy`しなくても事足る。 https://github.com/angr/angr-doc/blob/master/MIGRATION.md#changes-to-the-solver-interface > We cleaned up the menagerie of functions present on state.solver (if you're still referring to it as state.se you should stop) and simplified it into a cleaner interface seはstate engineの略? ### state.se.BVS, state.se.BVV シンボル変数の作成。BVはZ3のBit Vectorと同じ。なので第二引数はビット単位。 ```python symvar_x = state.se.BVS('x', 2 * 8) ``` #### BVの属性 `dir(symvar_BV)` `['Concat', 'Extract', 'FULL_SIMPLIFY', 'LITE_SIMPLIFY', 'LShR', 'SDiv', 'SGE', 'SGT', 'SLE', 'SLT', 'SMod', 'UGE', 'UGT', 'ULE', 'ULT', 'UNSIMPLIFIED', '__a_init__', '__abs__', '__add__', '__and__', '__class__', '__delattr__', '__dict__', '__div__', '__divmod__', '__doc__', '__eq__', '__floordiv__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__invert__', '__iter__', '__le__', '__len__', '__lshift__', '__lt__', '__mod__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__slots__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__weakref__', '__xor__', '_all_slots', '_ana_getliteral', '_ana_getstate', '_ana_setstate', '_ana_uuid', '_any_to_literal', '_apply_to_annotations', '_burrow_ite', '_burrowed', '_cache_key', '_calc_hash', '_check_replaceability', '_depth', '_eager_backends', '_errored', '_excavate_ite', '_excavated', '_first_backend', '_from_BVV', '_from_int', '_from_long', '_from_str', '_get_hashables', '_hash', '_hash_cache', '_identify_vars', '_recursive_leaf_asts', '_relocatable_annotations', '_rename', '_replace', '_self_to_literal', '_simplified', '_stored', '_type_name', '_uc_alloc_depth', '_uneliminatable_annotations', '_uninitialized', 'ana_load', 'ana_store', 'ana_uuid', 'annotate', 'annotations', 'append_annotation', 'append_annotations', 'args', 'cache_key', 'canonicalize', 'cardinality', 'chop', 'concat', 'concrete', 'dbg_is_looped', 'dbg_repr', 'depth', 'get_byte', 'get_bytes', 'insert_annotation', 'insert_annotations', 'intersection', 'ite_burrowed', 'ite_excavated', 'length', 'make_like', 'make_uuid', 'model', 'multivalued', 'op', 'raw_to_bv', 'raw_to_fp', 'recursive_children_asts', 'recursive_leaf_asts', 'remove_annotation', 'remove_annotations', 'replace', 'replace_annotations', 'replace_dict', 'reversed', 'shallow_repr', 'sign_extend', 'simplifiable', 'singlevalued', 'size', 'split', 'structurally_match', 'swap_args', 'symbolic', 'to_bv', 'to_literal', 'uc_alloc_depth', 'uninitialized', 'union', 'uuid', 'val_to_fp', 'variables', 'widen', 'zero_extend']` #### シンボル/コンクリートかの判定 ```python symvar_BV.symbolic # => bool symvar_BV.concrete # => bool ``` #### コンクリート化 デフォルトの返り値はlong型。 ``` ipdb> state.se.any_int(symvar_BV) Deprecation warning: Use eval() instead of any_int 128L ipdb> state.se.eval(symvar_BV) 128L ipdb> state.se.any_str(symvar_BV) Deprecation warning: Use eval(expr, cast_to=str) instead of any_str '\x00\x80' ipdb> state.se.eval(symvar_BV, cast_to=str) '\x00\x80' ipdb> state.solver.eval(symvar_BV, cast_to=str) '\x00\x80' ``` ### state.se.Extract angr-doc非掲載API。 ```python ### Extract(end, start, BV) ### extracts {x | start <= x <= end} state.se.Extract(32 * 2 + 15, 32 * 2 + 8, payload) ``` #### エンディアン変換 Extractで出てくるバイト列はビッグエンディアンなので、エンディアンの変換関数があると混乱しない。 ```python Extract = lambda x: state.se.Extract(x) BigEndian = lambda x: state.se.Reverse(x) LittleEndian = lambda x: x ``` 使用例: ```python state.add_constraints(LittleEndian(Extract(8 * 4 - 1, 8 * 0, symvar_pbuf_payload)) == 0x80810000) ``` ### state.se.Reverse angr-doc非掲載API。 バイトオーダーをひっくり返す。state.memory.store前のエンディアンの変換に便利。 ### state.se.constraints 配列。なので引数不要。 使用例: ```python constraint_expr = simgr.errored[0].state.plugins['solver_engine'].constraints for s in simgr.active: s.add_constraints(s.se.Or(*[s.se.Not(x) for x in constraint_expr])) ``` state.solver ---- state.solverに同じ。state.seを使うのをやめよう! https://github.com/angr/angr/blob/master/angr/state_plugins/solver.py ```python ### 充足可能性の判定 ### @return bool def satisfiable(self, extra_constraints=(), exact=None): ``` ### state.solver.add 制約を追加 https://github.com/angr/angr-doc/blob/master/docs/claripy.md ``` ipdb> state.solver.satisfiable([state.regs.r0 == 0]) False ipdb> state.solver.satisfiable([state.regs.r0 != 0]) True ``` state.se.Andとか使えばいいかな? #### state.solver.add vs. state.add_constraints 機能は同じだと思う。 ### state.solver.eval BV変数をコンクリート化する。 ```python ret_long_int = state.solver.eval(state.memory.load(addr, 0x8)) ret_bytes = state.solver.eval(state.memory.load(addr, 0x100), case_to=bytes) ``` case_toで文字列に変換するときにodd-lengthで例外吐くことがあるのでラッパー関数を用意するとよい。 ```python import hexdump ### needs `pip install hexdump` def memory_dump(begin, length): ret = "" for i in range(0, length): val = found.solver.eval(state.memory.load(begin + i, 1)) ret += chr(val) return ret hexdump.hexdump(v) ``` state.memory ---- `ipdb> dir(state.memory)` `['STRONGREF_STATE', '_CONCRETIZATION_STRATEGIES', '_SAFE_CONCRETIZATION_STRATEGIES', '__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getstate__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__subclasshook__', '__weakref__', '_abstract_backer', '_apply_concretization_strategies', '_changes_to_merge', '_constrain_underconstrained_index', '_convert_to_ast', '_copy_contents', '_create_default_read_strategies', '_create_default_write_strategies', '_fill_missing', '_find', '_generic_region_map', '_is_uninitialized', '_load', '_maximum_concrete_size', '_maximum_symbolic_size', '_maximum_symbolic_size_approx', '_merge', '_merge_strategies', '_merge_values', '_read_address_range', '_read_address_range_approx', '_read_from', '_resolve_location_name', '_resolve_size_range', '_stack_region_map', '_store', '_store_cases', '_store_with_merge', '_temp_generic_region_map', '_temp_stack_region_map', '_write_address_range', '_write_address_range_approx', 'addrs_for_hash', 'addrs_for_name', 'category', 'changed_bytes', 'concrete_parts', 'concretize_read_addr', 'concretize_write_addr', 'copy', 'copy_contents', 'dbg_print', 'endness', 'find', 'get_unconstrained_bytes', 'id', 'init_state', 'load', 'make_symbolic', 'map_region', 'mem', 'memory_objects_for_hash', 'memory_objects_for_name', 'merge', 'normalize_address', 'permissions', 'read_strategies', 'register_default', 'replace_all', 'replace_memory_object', 'set_stack_address_mapping', 'set_state', 'set_strongref_state', 'stack_id', 'state', 'store', 'store_cases', 'unconstrain_byte', 'unconstrain_differences', 'unmap_region', 'unset_stack_address_mapping', 'was_written_to', 'widen', 'write_strategies'] ` ### state.memory.load 第一引数は読み込み元のアドレス。第二引数は読み込むバイト数。 ### state.memory.store 第一引数がBVのときは第二引数で長さを指定しなくてもよい。 勝手にリトルエンディアンで格納するので要注意。 定数値を入れる時はstate.memの方が安全。エンディアンを気にしたい時は、 ```python state.memory.store(addr, state.se.BVV(0x114514, 64)) # store 0x114514 with Big endian state.memory.store(addr, state.se.Reverse(state.se.BVV(0x114514, 64))) # store 0x114514 with Little endian ``` ### エンディアンの注意点 state.se.BVVとstate.se.BVSの内部表現はビッグエンディアン。`memory.store`は対象番地にリトルエンディアンで書き込む。storeの第二引数を数値で直書きしても、内部ではビッグエンディアンで、それをリトルエンディアンで書き込む挙動をするので注意。 ```python state.memory.store(buf_ptr, buf, 8) # store memory with BIG endian ``` リトルエンディアンで確実に数値を書き込みたかったら、`state.mem`を使うとよい。64ビットでの挙動が怪しい場面に直面した。怖い。 ```python buf_ptr = 0x200000 state.mem[buf_ptr].qword = 0x114514 # 無言の死を遂げる? state.mem[buf_ptr].uint64_t = 0x114514 # 無言の死を遂げる? state.mem[buf_ptr].uint64_t = 0x114514 state.mem[buf_ptr].dword = 0x114514 ``` または、 ```python state.memory.store(buf_ptr, state.se.Reverse(buf), 0x100) ``` state.memory.mem ---- #### state.memory.mem.map_region ```python state.memory.mem.map_region(MY_SYMVAR_REGION_BEGIN, MY_SYMVAR_REGION_LENGTH, 0b111) # begin, len, permissions (exec, read, write) ``` http://angr.io/api-doc/angr.html#angr.storage.paged_memory.BasePage > permissions (claripy.AST) – A 3-bit bitvector setting specific permissions for EXEC, READ, and WRITE state.inspect ---- ### state.inspect.b メモリのリードアクセス前にフックを追加。 ```python new_state.inspect.b('mem_read', when=angr.BP_AFTER, action=debug_funcRead) def debug_funcRead(state): print 'Read', state.inspect.mem_read_expr, 'from', state.inspect.mem_read_address ``` ### dir(state.inspect) `ipdb> filter(lambda x: not x.startswith('_'), dir(state.inspect))` `['BP_AFTER', 'BP_BEFORE', 'BP_BOTH', 'STRONGREF_STATE', 'action', 'add', 'add_breakpoint', 'added_constraints', 'address', 'address_concretization_action', 'address_concretization_add_constraints', 'address_concretization_expr', 'address_concretization_memory', 'address_concretization_result', 'address_concretization_strategy', 'b', 'backtrace', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'downsize', 'exit_guard', 'exit_jumpkind', 'exit_target', 'expr', 'function_address', 'init_state', 'instruction', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'make_breakpoint', 'mem_read_address', 'mem_read_expr', 'mem_read_length', 'mem_write_address', 'mem_write_expr', 'mem_write_length', 'merge', 'pop', 'reg_read_expr', 'reg_read_length', 'reg_read_offset', 'reg_write_expr', 'reg_write_length', 'reg_write_offset', 'register_default', 'remove', 'remove_breakpoint', 'set_state', 'set_strongref_state', 'sim_engine', 'sim_successors', 'simprocedure', 'simprocedure_addr', 'simprocedure_name', 'state', 'statement', 'symbolic_expr', 'symbolic_name', 'symbolic_size', 'symmetric_difference', 'symmetric_difference_update', 'syscall_name', 'tmp_read_expr', 'tmp_read_num', 'tmp_write_expr', 'tmp_write_num', 'union', 'update', 'widen']` state.mem ---- https://github.com/angr/angr/blob/6d88622e3e8825083d55ac0e15a4ae304fc2b0e2/angr/state_plugins/view.py#L82 数値でのアクセスはこれを使うのが楽。返り値を比較できない(state.add_constraintの文脈でも不可っぽい)謎仕様なので使いみちがあまりない。 ```python state.mem[addr].uint64_t # => <BV ...> state.mem[addr].uint64_t.concrete # => long int ``` > **NOTE**: uint, intの打ち間違いに注意! angr.exploration_techniques ---- 探索アルゴリズムをカスタマイズすることができる。標準で用意されているテクニックを使うのが楽。 ### DFS https://github.com/angr/angr/blob/master/angr/exploration_techniques/dfs.py ```python simgr.use_technique(angr.exploration_techniques.DFS()) ``` 深さ優先探索(Depth First Search?)を行う。デフォルトは幅優先探索。 activeが空になったときに探索を終了してしまう不都合な挙動をするので、以下のようにstep_funcをうまく実装しないとだめ。 ```python def step_func(lpg): global find, avoid if find is not []: lpg.stash(filter_func=lambda path: path.addr in find, from_stash='active', to_stash='found') lpg.stash(filter_func=lambda path: path.addr in avoid, from_stash='active', to_stash='avoid') lpg.drop(stash='avoid') # memory usage optimization # print "[*] len(lpg.active) = %d, len(lpg.deferred) = %d" % (len(lpg.active), len(lpg.deferred)) if len(lpg.active) == 0 and len(lpg.deferred) > 0: lpg.active.append(lpg.deferred.pop()) return lpg ``` ### Threading https://github.com/angr/angr/blob/master/angr/exploration_techniques/threading.py ```python simgr.use_technique(angr.exploration_techniques.Threading()) ``` 冒頭で import している current.features は並列実行用のPython標準モジュール。 非並列で実行したときに解が求まる事前制約を与えると、Threadingのときは解が求まらないっぽい?