├── .gitbook.yaml ├── docs ├── README.md ├── SUMMARY.md ├── bitvectors.md ├── challenges │ ├── 00_angr_find │ ├── 01_angr_avoid │ ├── 02_angr_find_condition │ ├── 03_angr_symbolic_registers │ ├── ais3_crackme │ ├── basic │ ├── basic32 │ ├── cmu_bomb │ ├── r100 │ ├── r200 │ └── vuln ├── contributing.md ├── emulation.md ├── exploitation.md ├── exploration.md ├── features.md ├── hooks.md ├── initialization.md ├── installation.md ├── logo.png ├── overview.md ├── preview.gif ├── scripting.md ├── states.md ├── tutorial1.md ├── tutorial2.md ├── tutorial3.md ├── tutorial4.md ├── visualization.md └── watchpoints.md ├── plugin.py ├── readme.md └── src ├── bitvectors.py ├── bvs.py ├── concrete ├── __pycache__ │ ├── concrete.cpython-36.pyc │ ├── memory_map.cpython-36.pyc │ └── target.cpython-36.pyc ├── concrete.py ├── memory_map.py └── target.py ├── debug.py ├── disass.py ├── exploit.py ├── hooks.py ├── initializer.py ├── printer.py ├── project.py ├── r2angr.py ├── r2angrdbg.py ├── stash.py ├── util.py └── watcher.py /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs/ 2 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | A radare2 plugin to quickly perform symbolic execution inside radare2 with angr, a platform-agnostic binary analysis framework by the Computer Security Lab at UC Santa Barbara and SEFCOM at Arizona State University. This plugin is intended to integrate angr in a way that's (relativley) consistent with the r2cli conventions 4 | 5 | ## Goals 6 | 7 | This project intends to 8 | - Better integrate symbolic execution with the rest of the reverse engineering process 9 | - Provide a faster alternative to using angr than the python bindings 10 | - Allow for switching between concrete and symbolic execution (this feature is coming soon) 11 | - Provide useful visualizations of the angr backend 12 | - Include a suite of features for vulnerability detection, exploit generation, etc (coming soon) 13 | 14 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | ## Getting started 4 | * [Installation](installation.md) 5 | * [Contributing](contributing.md) 6 | 7 | ## References 8 | * [Features List](features.md) 9 | * [Command List](list.md) 10 | 11 | ## Features 12 | * [Overview](overview.md) 13 | * [Exploration](exploration.md) 14 | * [Emulation](emulation.md) 15 | * [Initialization](initialization.md) 16 | * [States](states.md) 17 | * [Hooks](hooks.md) 18 | * [Bitvectors](bitvectors.md) 19 | * [Watchpoints](watchpoints.md) 20 | * [Exploitation](exploitation.md) 21 | * [Scripting](scripting.md) 22 | * [Visualization](visualization.md) 23 | 24 | ## Tutorials 25 | * [Basic Exploration](tutorial1.md) 26 | * [Finding and Avoiding](tutorial2.md) 27 | * [Finding Output](tutorial3.md) 28 | * [Working with bitvectors](tutorial4.md) 29 | 30 | -------------------------------------------------------------------------------- /docs/bitvectors.md: -------------------------------------------------------------------------------- 1 | # Bitvectors 2 | 3 | Coming soon. 4 | -------------------------------------------------------------------------------- /docs/challenges/00_angr_find: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/00_angr_find -------------------------------------------------------------------------------- /docs/challenges/01_angr_avoid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/01_angr_avoid -------------------------------------------------------------------------------- /docs/challenges/02_angr_find_condition: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/02_angr_find_condition -------------------------------------------------------------------------------- /docs/challenges/03_angr_symbolic_registers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/03_angr_symbolic_registers -------------------------------------------------------------------------------- /docs/challenges/ais3_crackme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/ais3_crackme -------------------------------------------------------------------------------- /docs/challenges/basic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/basic -------------------------------------------------------------------------------- /docs/challenges/basic32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/basic32 -------------------------------------------------------------------------------- /docs/challenges/cmu_bomb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/cmu_bomb -------------------------------------------------------------------------------- /docs/challenges/r100: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/r100 -------------------------------------------------------------------------------- /docs/challenges/r200: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/r200 -------------------------------------------------------------------------------- /docs/challenges/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/challenges/vuln -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project is in it's early stages so bugs are expected. Feel free to create a git issue for any you find, or better yet submit a pull request with the fix. 4 | 5 | If you're looking to contribute new features, feel free to contact me for ideas. 6 | -------------------------------------------------------------------------------- /docs/emulation.md: -------------------------------------------------------------------------------- 1 | # Emulation 2 | 3 | Most of the time you'll want to use the exploration commands instead of the emulation ones, but a few emulation commands are included so angr can be used like a debugger. Most of these are variations of the `Mc` (continue) command. The commands can be listed as shown below. 4 | 5 | ``` 6 | [0x08048687]> Mc? 7 | Getting help 8 | | Mc[?] Continue emulation 9 | | Mcs Continue emulation one step 10 | | Mcu Continue emulation until address 11 | | Mcb Continue emulation until branch 12 | | Mco Continue emulation until output 13 | [0x08048687]> 14 | ``` 15 | 16 | ## Continuing 17 | 18 | The `Mc` command will continue emulation until all states have deadended. 19 | 20 | ``` 21 | [0x08048687]> Mc 22 | [DEBUG] Continuing emulation 23 | [0x08048687]> Msl 24 | Deadended states: 25 | 0 0x90303d0 26 | 1 0x90303d0 27 | 2 0x90303d0 28 | 3 0x90303d0 29 | 4 0x90303d0 30 | 5 0x90303d0 31 | 6 0x90303d0 32 | 7 0x90303d0 33 | 8 0x90303d0 34 | 9 0x90303d0 35 | 10 0x90303d0 36 | 11 0x90303d0 37 | 12 0x90303d0 38 | 13 0x90303d0 39 | 14 0x90303d0 40 | 15 0x90303d0 41 | 16 0xc000048 42 | 17 0xc000048 43 | ``` 44 | 45 | As you can see, continuing and then listing the states leaves us with 17 deadended states. 46 | 47 | ## Stepping 48 | 49 | The `Ms` command can be use to step all the states. 50 | 51 | ``` 52 | [0x08048687]> Mcs 53 | [DEBUG] Continuing emulation one step 54 | ``` 55 | 56 | ## Continuing to address 57 | 58 | The `Mcu` command can be used to continue emulation until a state hits the specified address or function. 59 | 60 | ``` 61 | [0x08048687]> Mcu main 62 | [DEBUG] Continuing emulation until main 63 | ``` 64 | 65 | ## Continuing to branch 66 | 67 | The `Mcb` command can be used to continue emulation until a state hits a branch (where the state will split). 68 | 69 | ``` 70 | [0x08048687]> Mcb 71 | [DEBUG] Continuing emulation until branch 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/exploitation.md: -------------------------------------------------------------------------------- 1 | # Exploitation 2 | 3 | Coming soon. 4 | -------------------------------------------------------------------------------- /docs/exploration.md: -------------------------------------------------------------------------------- 1 | # Exploration 2 | 3 | Modality contains a number of exploration commands. These can be listed with the `Me?` command. 4 | 5 | ``` 6 | [0x08048450]> Me? 7 | Getting help 8 | | Me[?] Explore using find/avoid comments 9 | | Meu Explore until address 10 | | Meo Explore until string is in stdout 11 | ``` 12 | 13 | Currently there are only three options, but eventually all of the angr exploration methods and some variations will be included. 14 | 15 | ## Exploring to a location 16 | 17 | The simplest command to use is probably the `Meu ` command. This command explores until a state reaches a specified address or function name. An example of exploring to the main address is shown below. 18 | 19 | ``` 20 | [0x08048450]> Meu main 21 | [DEBUG] Starting exploration. Find: [0x80485c7] 22 | WARNING | 2020-06-15 15:22:15,566 | angr.state_plugins.symbolic_memory | Filling register edi with 4 unconstrained bytes referenced from 0x80486b1 (__libc_csu_init+0x1 in 00_angr_find (0x80486b1)) 23 | WARNING | 2020-06-15 15:22:15,568 | angr.state_plugins.symbolic_memory | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in 00_angr_find (0x80486b3)) 24 | [DEBUG] Found 1 solutions 25 | [0x080485c7]> 26 | ``` 27 | 28 | ## Exploring using find/avoid comments 29 | 30 | The `Me` command explores using the addresses marked by the radare2 comments "find" or "avoid". An example is shown below. 31 | 32 | ``` 33 | [0x08048450]> CC+find @ main 34 | [0x08048450]> Me 35 | [DEBUG] Starting exploration. 36 | Find: [0x80485c7]. Avoid: []. 37 | WARNING | 2020-06-15 15:28:03,910 | angr.state_plugins.symbolic_memory | Filling register edi with 4 unconstrained bytes referenced from 0x80486b1 (__libc_csu_init+0x1 in 00_angr_find (0x80486b1)) 38 | WARNING | 2020-06-15 15:28:03,912 | angr.state_plugins.symbolic_memory | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in 00_angr_find (0x80486b3)) 39 | [DEBUG] Found 1 solutions 40 | [0x080485c7]> 41 | ``` 42 | 43 | The `CC+` command adds a comment at that address, and the `Me` command is used to explore to it. Most of the time you'll use find/avoid addresses because you want to set multiple throughout the binary. It's often convienient to add these comments by pressing `;` in the graph view. 44 | 45 | ## Exploring using stdout 46 | 47 | The `Meo` command explores until a state has a specified value in stdout. This is useful, for example, if you want to find the input for some CTF challenge that gets the binary to print "Sucess". 48 | 49 | ``` 50 | [0x080485c7]> Meo Good Job 51 | [DEBUG] Starting exploration. Find: [Good Job] 52 | WARNING | 2020-06-15 15:31:48,211 | angr.state_plugins.symbolic_memory | Filling memory at 0x804a040 with 240 unconstrained bytes referenced from 0x90512d0 (printf+0x0 in libc.so.6 (0x512d0)) 53 | WARNING | 2020-06-15 15:31:49,370 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffefffc with 103 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 54 | WARNING | 2020-06-15 15:31:49,371 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff70 with 4 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 55 | [DEBUG] Found 1 solutions 56 | [0x08048687]> 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | ### Documentation 4 | 5 | * [ ] Document installation 6 | * [ ] Wiki and tutorial 7 | * [ ] Short tool overview 8 | * [ ] Long tutorial video 9 | 10 | ### Emulation 11 | 12 | * [x] Basic initialization: `mi` 13 | * [x] Initialize at arbitrary memory location 14 | * [ ] Add all the exploration methods 15 | * [ ] Symbolize arbitrary number of arguments \(use -d arguments to set them initially\) 16 | * [ ] Initialize on function with current debugger args, specific args, or symbolic args 17 | * [x] Basic emulation: `mc`, `mcs`, `mcb`, `mcu`, `mco` 18 | * [x] Basic exploration: `me`, `meu` 19 | * [ ] Explore for certain output 20 | * [ ] Implement staged exploration 21 | * [ ] Add avoid/find annotation commands 22 | * [x] Basic watchpoint commands: `mw`, `mwl` 23 | * [ ] More watchpoint commands \(remove watchpoints, run command at watchpoints\) 24 | * [ ] Switch between concrete and symbolic execution 25 | 26 | ### Stash manipulation 27 | 28 | * [x] Basic state manipulation: `ms`, `msl`, `msk`, `msr`, `mse`, `mss` 29 | * [x] Group state operations: `mska`, `msra` 30 | * [x] Print more detailed state information 31 | * [ ] Print info while killing/reviving/extracting states 32 | * [ ] Kill/revive based on output 33 | * [ ] State seeking by index 34 | * [x] States outputs and inputs printing 35 | * [ ] Print even more detailed state information \(current function, ...\) 36 | 37 | ### Symbolize commands 38 | 39 | * [ ] Commands to symbolize registers or stack values with different data types 40 | * [ ] Commands to symbolize variables 41 | 42 | ### Visualization 43 | 44 | * [ ] PEDA like view option 45 | * [ ] Finalize found/active highlighting and stashing 46 | * [ ] Indent all printing according to call/loop hierarchy 47 | * [ ] Implement custom radare2 panels view for exploration 48 | * [ ] Print log of a state history 49 | * [ ] If enabled, replace commands like `dr` with symbolic information 50 | * [ ] Graph an emulation history, with branches at state splitting and annotations for loops, branches, etc 51 | * [ ] Standardize print messages with formats like \[DEBUG\] \[PRINT\] \[HOOK\] etc 52 | * [ ] Command to print detailed info about state at current address. Can be used with visual panels mode. 53 | * [ ] Annotate graph with state split locations 54 | 55 | ### Exploitation 56 | 57 | * [ ] Brainstorm list of features 58 | * [ ] Integrate or reimplement functionality of rex 59 | 60 | ### Hooks 61 | 62 | * [x] Analysis commands for loops, functions, etc 63 | * [x] Move analysis commands to the hook clasification 64 | * [ ] List hooks 65 | * [ ] When hooking function calls, print args 66 | * [ ] Print function return values 67 | * [ ] Command to add hooks at locations, run some r2 command there 68 | * [ ] When state splits, print \[1\|2\] => \[1\|3\] with split address 69 | * [ ] Add custom hooks for strlen, etc that ask for length or arbitrary length. Can also set this in the config. 70 | 71 | ### Other 72 | 73 | * [ ] Deal with offets for PIE 74 | * [ ] Commands to edit config.txt file 75 | * [ ] Integrate ghidra with the disassembler 76 | * [ ] Watchpoint comment hit count doesn't work 77 | * [ ] Watchpoint hits should work per state 78 | * [ ] Remove watchpoints unimplemented 79 | * [ ] der command broken 80 | * [ ] Tools for dealing with path explosion 81 | * [ ] Get working as scripting engine 82 | * [ ] Easy way to edit script inside radare2 that runs at the beginning. Can add custom hooks this way. 83 | * [ ] Make commands robust, go through and check for bugs 84 | * [ ] Write wiki for this project. Write tutorial for this project 85 | 86 | -------------------------------------------------------------------------------- /docs/hooks.md: -------------------------------------------------------------------------------- 1 | # Hooks 2 | 3 | These command can be used to hook different parts of the code and run commands or print debugging information when a state hits those locations. This code is mostly proof-of-concept so a lot of features are currently missing. 4 | 5 | The relevant commands can be listed using `Mh?`. 6 | 7 | ``` 8 | [0x00400610]> Mh? 9 | Getting help 10 | | Mh[?] Hooks help 11 | | Mhf Hook all functions 12 | | Mhl Hook all loops 13 | [0x00400610]> 14 | ``` 15 | 16 | ## Hooking functions 17 | 18 | An example of the `Mhf` command for hooking all functions is shown below. 19 | 20 | ``` 21 | [0x00400610]> Mhf 22 | [HOOKS] Hooking function: entry0 at 0x400610 23 | [HOOKS] Hooking import: sym.imp.__libc_start_main at 0x4005d0 24 | [HOOKS] Hooking import: sym.imp.getenv at 0x400590 25 | [HOOKS] Hooking import: sym.imp.puts at 0x4005a0 26 | [HOOKS] Hooking import: sym.imp.__stack_chk_fail at 0x4005b0 27 | [HOOKS] Hooking import: sym.imp.printf at 0x4005c0 28 | [HOOKS] Hooking import: sym.imp.fgets at 0x4005e0 29 | [HOOKS] Hooking import: sym.imp.ptrace at 0x400600 30 | [HOOKS] Hooking function: main at 0x4007e8 31 | [HOOKS] Hooking function: entry.init0 at 0x4006d0 32 | [HOOKS] Hooking function: entry.init1 at 0x4007a8 33 | [HOOKS] Hooking function: entry.fini0 at 0x4006b0 34 | [HOOKS] Hooking function: fcn.00400640 at 0x400640 35 | [0x00400610]> Meu 0x400844 36 | [DEBUG] Starting exploration. Find: [0x400844] 37 | [HOOKS] Called entry0 (int64_t arg3); 38 | [HOOKS] Called int sym.imp.__libc_start_main (func main, int argc, char **ubp_av, func init, func fini, func rtld_fini, void *stack_end); 39 | [HOOKS] Called entry.init0 (); 40 | [HOOKS] Called entry.init1 (); 41 | [HOOKS] Called char *sym.imp.getenv (const char *name); 42 | [HOOKS] Called long sym.imp.ptrace (__ptrace_request request, pid_t pid, void*addr, void*data); 43 | [HOOKS] Called int main (int argc, char **argv, char **envp); 44 | [HOOKS] Called int sym.imp.printf (const char *format); 45 | [HOOKS] Called char *sym.imp.fgets (char *s, int size, FILE *stream); 46 | [HOOKS] Called int sym.imp.puts (const char *s); 47 | [HOOKS] Called int sym.imp.puts (const char *s); 48 | [HOOKS] Called int sym.imp.puts (const char *s); 49 | [HOOKS] Called int sym.imp.puts (const char *s); 50 | [HOOKS] Called int sym.imp.puts (const char *s); 51 | [HOOKS] Called int sym.imp.puts (const char *s); 52 | [HOOKS] Called int sym.imp.puts (const char *s); 53 | [HOOKS] Called int sym.imp.puts (const char *s); 54 | [HOOKS] Called int sym.imp.puts (const char *s); 55 | [HOOKS] Called int sym.imp.puts (const char *s); 56 | [HOOKS] Called int sym.imp.puts (const char *s); 57 | [DEBUG] Found 1 solutions 58 | [0x00400844]> 59 | ``` 60 | 61 | As you can see, there is a lot more debugging information printed during exploration because these hooks have been installed. These hooks are useful for tracking what angr is doing during an exploration. 62 | 63 | ## Hooking loops 64 | 65 | Loops famously pose a challenge for symbolic execution due to the problem of path explosion. The `Mhl` command hooks the start of a loop and prints debugging information each iteration to make debugging these kinds of issues simpler. 66 | 67 | ``` 68 | [0x00400610]> Mhl 69 | WARNING | 2020-06-15 16:34:02,911 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. 70 | WARNING | 2020-06-15 16:34:02,911 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: 71 | WARNING | 2020-06-15 16:34:02,911 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state 72 | WARNING | 2020-06-15 16:34:02,911 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null 73 | WARNING | 2020-06-15 16:34:02,911 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages. 74 | WARNING | 2020-06-15 16:34:02,911 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffff with 8 unconstrained bytes referenced from 0x400615 (PLT.ptrace+0x15 in r100 (0x400615)) 75 | [HOOKS] Found 4 loops 76 | [0x00400610]> Meu main 77 | [DEBUG] Starting exploration. Find: [0x4007e8] 78 | [HOOKS] Starting loop at 0x4008d0 79 | [HOOKS] [1|0] {Loop count: 1} Looping at 0x4008d0 80 | [HOOKS] Starting loop at 0x4007e4 81 | [HOOKS] [3|0] {Loop count: 1} Looping at 0x4007e4 82 | [HOOKS] [3|0] {Loop count: 2} Looping at 0x4007e4 83 | [HOOKS] [3|0] {Loop count: 3} Looping at 0x4007e4 84 | [HOOKS] [3|0] {Loop count: 4} Looping at 0x4007e4 85 | [HOOKS] [3|0] {Loop count: 5} Looping at 0x4007e4 86 | [DEBUG] Found 1 solutions 87 | ``` 88 | -------------------------------------------------------------------------------- /docs/initialization.md: -------------------------------------------------------------------------------- 1 | # Initialization 2 | 3 | Modality automatically initializes a state at the entry point when the plugin is loaded. However, under some circumstances it is useful to reinitialize this state or initialize it at a different location, which is the purpose of the commands under this classification. 4 | 5 | The avaliable commands can be listed as shown below. 6 | 7 | ``` 8 | [0x08048687]> Mi? 9 | Getting help 10 | | Mi[?] Initialize at entry point 11 | | Mie Initialize at entry point 12 | | Mib Initialize blank state at current address 13 | ``` 14 | 15 | ## Initializing at entry0 16 | 17 | The `Mie` command will initialize the simgr at the entry point detected by radare2. 18 | 19 | ``` 20 | [0x08048687]> Mie 21 | [R2ANGR] Initialized r2angr at entry point 22 | ``` 23 | 24 | ## Blank states 25 | 26 | Blank states can be initialized at the current address using the `Mib` command. 27 | 28 | ``` 29 | [0x08048450]> Mib 30 | WARNING | 2020-06-15 16:11:42,459 | angr.sim_state | Unused keyword arguments passed to SimState: args 31 | [R2ANGR] Initialized r2angr blank state at current address 32 | [0x08048450]> 33 | ``` 34 | 35 | They can also be combined with the radare2 `@` symbol to initialize at any other address or function name, as shown below. 36 | 37 | ``` 38 | [0x08048450]> Mib @ main 39 | WARNING | 2020-06-15 16:13:04,559 | angr.sim_state | Unused keyword arguments passed to SimState: args 40 | [R2ANGR] Initialized r2angr blank state 41 | [0x08048450]> 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Coming soon. 4 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/logo.png -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This plugin is built on top of angr, and so most of it's functionality revolves around angr conventions. It would be useful for anyone who uses this tool to read the angr docs first. 4 | 5 | ## Introduction 6 | 7 | Radare2's design is ideal for integration with angr because it allows for complicated command structures. All Modality commands are prefixed with `M`, and the top level commands can be listed with `M?`. 8 | 9 | ``` 10 | [0x08048450]> M? 11 | [R2ANGR] Importing angr 12 | [R2ANGR] Loading r2angr 13 | [R2ANGR] Initialized r2angr at entry point 14 | Getting help 15 | | Mc[?] Continue emulation 16 | | Me[?] Explore using find/avoid comments 17 | | Mi[?] Initialize at entry point 18 | | Ms[?] States list 19 | | Mh[?] Hooks 20 | | Mw[?] Add a watchpoint 21 | [0x08048450]> 22 | ``` 23 | 24 | This first time you run a Modality command it imports angr and loads the rest of the plugin which normally takes a few seconds. This is only required once per radare2 session. 25 | 26 | We can see the command above listed the top level commands for exploration, state manipulation, etc. We can list the sub-commands for any top level command as shown below. 27 | 28 | ``` 29 | [0x08048450]> Me? 30 | Getting help 31 | | Me[?] Explore using find/avoid comments 32 | | Meu Explore until address 33 | | Meo Explore until string is in stdout 34 | [0x08048450]> 35 | ``` 36 | 37 | This should all be mostly consistent with the r2cli conventions. 38 | 39 | ## Example 40 | 41 | Here's an example using the r100 challenge in the challenges/ folder. First we'll open one of the example binaries in radare2 and display the help with the `M?` command. 42 | 43 | ``` 44 | shell@shell:~/$ r2 challenges/r100 45 | [0x00400610]> aa 46 | [x] Analyze all flags starting with sym. and entry0 (aa) 47 | [0x00400610]> M? 48 | [R2ANGR] Importing angr 49 | [R2ANGR] Loading r2angr 50 | [R2ANGR] Initialized r2angr at entry point 51 | Getting help 52 | | Mc[?] Continue emulation 53 | | Me[?] Explore using find/avoid comments 54 | | Mi[?] Initialize at entry point 55 | | Ms[?] States list 56 | | Mh[?] Hooks 57 | | Mw[?] Add a watchpoint 58 | [0x00400610]> 59 | ``` 60 | 61 | It takes a second to load angr and the rest of the plugin, then displays our top level help menu. During this load, it automatically initializes a state at the entry point, so our next step is to explore to the desired address. We can list the exploration commands as shown below. 62 | 63 | ``` 64 | [0x00400610]> Me? 65 | Getting help 66 | | Me[?] Explore using find/avoid comments 67 | | Meu Explore until address 68 | | Meo Explore until string is in stdout 69 | [0x00400610]> 70 | ``` 71 | 72 | We'll use the `Meu` command to *explore until* one of the simulation manager states hits the main function. 73 | 74 | ``` 75 | [0x00400610]> aa 76 | [x] Analyze all flags starting with sym. and entry0 (aa) 77 | [0x00400610]> Meu main 78 | [DEBUG] Starting exploration. Find: [0x4007e8] 79 | [DEBUG] Found 1 solutions 80 | [0x004007e8]> 81 | ``` 82 | 83 | As we can see, one of our states found the main function. We can list all the active states with `Msl`. 84 | 85 | ``` 86 | [0x004007e8]> Msl 87 | Active states: 88 | 0 0x4007e4 89 | 1 0x4007e4 90 | 2 0x4007e8 91 | 92 | [0x004007e8]> 93 | ``` 94 | 95 | Since we only care about the state at `0x4007e8` (the others are stuck at an anti-debugging trick), we can kill the states at `0x4007e4` with the `Msk` command (to *kill* a single state) or the `Mse` command (to *extract* a single state and kill all the others). 96 | 97 | ``` 98 | [0x004007e8]> Mse 2 99 | [0x004007e8]> Msl 100 | Active states: 101 | 0 0x4007e8 102 | 103 | Deadended states: 104 | 0 0x4007e4 105 | 1 0x4007e4 106 | ``` 107 | 108 | This binary contains a success/fail condition. We want to know the input that produces the success condition. We can explore to this branch as shown below. 109 | 110 | ``` 111 | [0x004007e8]> Meu 0x00400844 112 | [DEBUG] Starting exploration. Find: [0x400844] 113 | [DEBUG] Found 1 solutions 114 | [0x00400844]> 115 | ``` 116 | 117 | We can then list the stdin for all active states with `Msi`. 118 | 119 | ``` 120 | [0x00400844]> Msi 121 | Active state 0 at 0x4005a0: 122 | b'Code_Talker\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 123 | Active state 1 at 0x40085f: 124 | b'Code_Talke\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 125 | Active state 2 at 0x40087f: 126 | b'Code_Talk\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 127 | Active state 3 at 0x4000050: 128 | b'Code_Tal\xf5\xe5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 129 | Active state 4 at 0x400844: 130 | b'Code_Talkers\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 131 | ``` 132 | 133 | Since the state we acare about is at `0x400844`, we know our password is "Code_Talkers". 134 | 135 | ### Quicker solution 136 | 137 | To explain a variety of features we took a long way to solve this challenge, it could be solved quicker by opening r2 and using the one liner below. 138 | 139 | ``` 140 | [0x00400610]> aa;Meu 0x400844;Mse 0x400844;Msi 141 | [x] Analyze all flags starting with sym. and entry0 (aa) 142 | [R2ANGR] Importing angr 143 | [R2ANGR] Loading r2angr 144 | [R2ANGR] Initialized r2angr at entry point 145 | [DEBUG] Starting exploration. Find: [0x400844] 146 | [DEBUG] Found 1 solutions 147 | Active state 0 at 0x400844: 148 | b'Code_Talkers\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 149 | [0x00400844]> 150 | ``` 151 | -------------------------------------------------------------------------------- /docs/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/docs/preview.gif -------------------------------------------------------------------------------- /docs/scripting.md: -------------------------------------------------------------------------------- 1 | # Scripting 2 | 3 | Coming soon. 4 | -------------------------------------------------------------------------------- /docs/states.md: -------------------------------------------------------------------------------- 1 | # States 2 | 3 | The states commands are used for manipulating and reading data from the various simulation manager stashes. The relevant commands can be listed as shown below. 4 | 5 | ``` 6 | [0x08048450]> Ms? 7 | Getting help 8 | | Ms[?] States list 9 | | Msl List states 10 | | Msi Print state stdin 11 | | Mso Print state stdout 12 | | Msk[?] Kill state by index or address 13 | | Msr[?] Revive state by index or address 14 | | Mss[?] 15 | | Mse Extract single state and kill all others 16 | ``` 17 | 18 | ## Listing states 19 | 20 | States can be listed with the `Msl` command. 21 | 22 | ``` 23 | [0x00400844]> Msl 24 | Active states: 25 | 0 0x4007e4 26 | 1 0x4007e4 27 | 2 0x4005a0 28 | 3 0x40085f 29 | 4 0x40087f 30 | 5 0x4000050 31 | 6 0x400844 32 | 33 | Deadended states: 34 | 0 0x4000050 35 | 1 0x4000050 36 | 2 0x4000050 37 | 3 0x4000050 38 | 4 0x4000050 39 | 5 0x4000050 40 | 6 0x4000050 41 | 7 0x4000050 42 | 43 | [0x00400844]> 44 | 45 | ``` 46 | 47 | States may be in either the *active* or *deadended* stashes. 48 | 49 | ## Manipulating states 50 | 51 | To optimize the exploration process it is useful to be able to kill in unwanted parts of the CFG. Some commands relevant to this include `Msk` (for killing states), `Msr` (for reviving states), `Mse` (for extracting states, or killing all states except for one), `Mska` (kill all states), and `Msra` revive all states. These commands can be used with indexes or addresses. 52 | 53 | Here is an example of killing a state. 54 | 55 | ``` 56 | [0x004007e8]> Msl 57 | Active states: 58 | 0 0x4007e4 59 | 1 0x4007e4 60 | 2 0x4007e8 61 | 62 | [0x004007e8]> Msk 2 63 | [0x004007e4]> Msl 64 | Active states: 65 | 0 0x4007e4 66 | 1 0x4007e4 67 | 68 | Deadended states: 69 | 0 0x4007e8 70 | 71 | [0x004007e4]> 72 | ``` 73 | 74 | Reviving a state. 75 | 76 | ``` 77 | [0x004007e4]> Msl 78 | Active states: 79 | 0 0x4007e4 80 | 1 0x4007e4 81 | 82 | Deadended states: 83 | 0 0x4007e8 84 | 85 | [0x004007e4]> Msr 0x4007e8 86 | [0x004007e8]> Msl 87 | Active states: 88 | 0 0x4007e4 89 | 1 0x4007e4 90 | 2 0x4007e8 91 | ``` 92 | 93 | Extracting a state (this is the state command I use the most often). 94 | 95 | ``` 96 | [0x004007e8]> Msl 97 | Active states: 98 | 0 0x4007e4 99 | 1 0x4007e4 100 | 2 0x4007e8 101 | 102 | [0x004007e8]> Mse 2 103 | [0x004007e8]> Msl 104 | Active states: 105 | 0 0x4007e8 106 | 107 | Deadended states: 108 | 0 0x4007e4 109 | 1 0x4007e4 110 | ``` 111 | 112 | Reviving all states 113 | 114 | ``` 115 | [0x004007e8]> Msl 116 | Active states: 117 | 0 0x4007e8 118 | 119 | Deadended states: 120 | 0 0x4007e4 121 | 1 0x4007e4 122 | 123 | [0x004007e8]> Msra 124 | [0x004007e4]> Msl 125 | Active states: 126 | 0 0x4007e8 127 | 1 0x4007e4 128 | 2 0x4007e4 129 | ``` 130 | 131 | Killing all states. 132 | 133 | ``` 134 | [0x004007e4]> Msl 135 | Active states: 136 | 0 0x4007e8 137 | 1 0x4007e4 138 | 2 0x4007e4 139 | 140 | [0x004007e4]> Mska 141 | [0x004007e4]> Msl 142 | Deadended states: 143 | 0 0x4007e8 144 | 1 0x4007e4 145 | 2 0x4007e4 146 | ``` 147 | 148 | ## Standard in/out 149 | 150 | After exploring to some address, it may be useful to print the stdin/stdout for any state. This can be done using the `Msi` and `Mso` commands. An example of printing the input is below. 151 | 152 | ``` 153 | [0x00400844]> Msi 154 | Active state 0 at 0x400844: 155 | b'Code_Talkers\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00' 156 | [0x00400844]> 157 | ``` 158 | 159 | And printing the output 160 | 161 | ``` 162 | [0x0040084e]> Mso 163 | Active state 0 at 0x40084e: 164 | Enter the password: Nice! 165 | 166 | [0x0040084e]> 167 | ``` 168 | -------------------------------------------------------------------------------- /docs/tutorial1.md: -------------------------------------------------------------------------------- 1 | # Basic Exploration 2 | 3 | The following tutorials will use binaries from the angr_ctf by *jakespringer* (included in the challenges/ folder) until I have time to write my own. We'll start with the first one, 00_angr_find. 4 | 5 | We'll start by opening and analyzing the binary with radare2. 6 | 7 | ``` 8 | shell@shell:~/github/r2angr/docs/challenges$ r2 00_angr_find 9 | -- Use rarun2 to launch your programs with a predefined environment. 10 | [0x08048450]> aaa 11 | [x] Analyze all flags starting with sym. and entry0 (aa) 12 | [x] Analyze function calls (aac) 13 | [x] Analyze len bytes of instructions for references (aar) 14 | [x] Check for objc references 15 | [x] Check for vtables 16 | [x] Type matching analysis for all functions (aaft) 17 | [x] Propagate noreturn information 18 | [x] Use -AA or aaaa to perform additional experimental analysis. 19 | ``` 20 | 21 | Looking at the main function, we can see that there's a function to mangle the user input and a strcmp() followed by a branch for success/failure. 22 | 23 | ``` 24 | │ 0x0804865c e86ffdffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) 25 | │ 0x08048661 83c410 add esp, 0x10 26 | │ 0x08048664 85c0 test eax, eax 27 | │ ┌─< 0x08048666 7412 je 0x804867a 28 | │ │ 0x08048668 83ec0c sub esp, 0xc 29 | │ │ 0x0804866b 6833870408 push str.Try_again. ; 0x8048733 ; "Try again." ; const char *s 30 | │ │ 0x08048670 e88bfdffff call sym.imp.puts ; int puts(const char *s) 31 | │ │ 0x08048675 83c410 add esp, 0x10 32 | │ ┌──< 0x08048678 eb10 jmp 0x804868a 33 | │ ││ ; CODE XREF from main @ 0x8048666 34 | │ │└─> 0x0804867a 83ec0c sub esp, 0xc 35 | │ │ 0x0804867d 6860870408 push str.Good_Job. ; 0x8048760 ; "Good Job." ; const char *s 36 | │ │ 0x08048682 e879fdffff call sym.imp.puts ; int puts(const char *s) 37 | │ │ 0x08048687 83c410 add esp, 0x10 38 | │ │ ; CODE XREF from main @ 0x8048678 39 | │ └──> 0x0804868a b800000000 mov eax, 0 40 | │ 0x0804868f 8b4df4 mov ecx, dword [canary] 41 | ``` 42 | 43 | Solving this manually would require reversing the `sym.complex_function` function. Instead, we can simply explore to the success branch, then print the stdin for that state. First we'll explore to the correct branch. 44 | 45 | ``` 46 | :> Meu 0x804867a 47 | [DEBUG] Starting exploration. Find: [0x804867a] 48 | WARNING | 2020-06-15 17:07:08,625 | angr.state_plugins.symbolic_memory | Filling memory at 0x804a040 with 240 unconstrained bytes referenced from 0x90512d0 (printf+0x0 in libc.so.6 (0x512d0)) 49 | WARNING | 2020-06-15 17:07:09,815 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffefffc with 103 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 50 | WARNING | 2020-06-15 17:07:09,815 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff70 with 4 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 51 | [DEBUG] Found 1 solutions 52 | ``` 53 | 54 | Then we'll list the states. We can see that there are two active states and 15 deadended states. 55 | 56 | ``` 57 | :> Msl 58 | Active states: 59 | 0 0x8048400 60 | 1 0x804867a 61 | 62 | Deadended states: 63 | 0 0x90303d0 64 | 1 0x90303d0 65 | 2 0x90303d0 66 | 3 0x90303d0 67 | 4 0x90303d0 68 | 5 0x90303d0 69 | 6 0x90303d0 70 | 7 0x90303d0 71 | 8 0x90303d0 72 | 9 0x90303d0 73 | 10 0x90303d0 74 | 11 0x90303d0 75 | 12 0x90303d0 76 | 13 0x90303d0 77 | 14 0x90303d0 78 | 15 0x90303d0 79 | ``` 80 | 81 | Since we only care about the state at `0x804867a`, we can kill state 0. 82 | 83 | ``` 84 | :> Msl 85 | Active states: 86 | 0 0x804867a 87 | 88 | Deadended states: 89 | 0 0x90303d0 90 | 1 0x90303d0 91 | 2 0x90303d0 92 | 3 0x90303d0 93 | 4 0x90303d0 94 | 5 0x90303d0 95 | 6 0x90303d0 96 | 7 0x90303d0 97 | 8 0x90303d0 98 | 9 0x90303d0 99 | 10 0x90303d0 100 | 11 0x90303d0 101 | 12 0x90303d0 102 | 13 0x90303d0 103 | 14 0x90303d0 104 | 15 0x90303d0 105 | 16 0x8048400 106 | ``` 107 | 108 | Finally, we can print stdin for the remaining state. 109 | 110 | ``` 111 | :> Msi 112 | Active state 0 at 0x804867a: 113 | QTMPXTYU 114 | ``` 115 | 116 | We can then test this against our binary to make sure it is correct! 117 | 118 | ``` 119 | [0x0804867a]> q 120 | shell@shell:~/github/modality/docs/challenges$ ./00_angr_find 121 | placeholder 122 | Enter the password: QTMPXTYU 123 | Good Job. 124 | ``` 125 | -------------------------------------------------------------------------------- /docs/tutorial2.md: -------------------------------------------------------------------------------- 1 | # Finding and Avoiding 2 | 3 | For this challenge we'll use a different find/avoid technique. The challenge is 01_angr_avoid. We'll start by opening the binary and doing some basic analysis. 4 | 5 | ``` 6 | shell@shell:~/github/modality/docs/challenges$ r2 01_angr_avoid 7 | [0x08048430]> aa 8 | [x] Analyze all flags starting with sym. and entry0 (aa) 9 | [0x08048430]> s main 10 | [0x08048602]> 11 | ``` 12 | 13 | The graph of the main function takes a while to render, so you may want to avoid that. Scrolling through the visual mode, we can see it gets some user input, calls `sym.complex_function`, then there's a lot of branches with calls to `sym.avoid_me` and `sym.maybe_good`. We'll seek to the first function and add a comment there that says "avoid". You can seek there and add a comment using `;` from visual mode, or add a comment with the `CC+` command. Once our avoid comment is added we'll seek to the `sym.maybe_good` function, and add a comment that says "find" on the branch that prints "Good Job.". 14 | 15 | Next we can run the `Me` commmand, which collects the addresses marked by find/avoid and explores using those locations. 16 | 17 | ``` 18 | [0x080485b5]> Me 19 | [R2ANGR] Importing angr 20 | [R2ANGR] Loading r2angr 21 | [R2ANGR] Initialized r2angr at entry point 22 | [DEBUG] Starting exploration. 23 | Find: [0x80485dd]. Avoid: [0x80485a8]. 24 | WARNING | 2020-06-15 17:24:37,083 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. 25 | WARNING | 2020-06-15 17:24:37,083 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: 26 | WARNING | 2020-06-15 17:24:37,083 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state 27 | WARNING | 2020-06-15 17:24:37,083 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null 28 | WARNING | 2020-06-15 17:24:37,083 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages. 29 | WARNING | 2020-06-15 17:24:37,084 | angr.state_plugins.symbolic_memory | Filling register edi with 4 unconstrained bytes referenced from 0x80d45e1 (__libc_csu_init+0x1 in 01_angr_avoid (0x80d45e1)) 30 | WARNING | 2020-06-15 17:24:37,086 | angr.state_plugins.symbolic_memory | Filling register ebx with 4 unconstrained bytes referenced from 0x80d45e3 (__libc_csu_init+0x3 in 01_angr_avoid (0x80d45e3)) 31 | WARNING | 2020-06-15 17:24:37,437 | angr.state_plugins.symbolic_memory | Filling memory at 0x80d6040 with 240 unconstrained bytes referenced from 0x90512d0 (printf+0x0 in libc.so.6 (0x512d0)) 32 | WARNING | 2020-06-15 17:24:39,695 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffefffc with 72 unconstrained bytes referenced from 0x907e8c0 (strncmp+0x0 in libc.so.6 (0x7e8c0)) 33 | WARNING | 2020-06-15 17:24:39,695 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff70 with 4 unconstrained bytes referenced from 0x907e8c0 (strncmp+0x0 in libc.so.6 (0x7e8c0)) 34 | WARNING | 2020-06-15 17:24:39,696 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff4d with 11 unconstrained bytes referenced from 0x907e8c0 (strncmp+0x0 in libc.so.6 (0x7e8c0)) 35 | WARNING | 2020-06-15 17:24:39,707 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fff0044 with 20 unconstrained bytes referenced from 0x907e8c0 (strncmp+0x0 in libc.so.6 (0x7e8c0)) 36 | [DEBUG] Found 1 solutions 37 | [0x080485dd]> 38 | ``` 39 | 40 | After a few seconds, we get a solution. We can list the states with the `Msl` command, and kill the unwanted active state as shown below. 41 | 42 | ``` 43 | [0x080485dd]> Msk 0 44 | [0x080485dd]> Msl 45 | Active states: 46 | 0 0x80485dd 47 | 48 | Deadended states: 49 | 0 0x90303d0 50 | 1 0x90303d0 51 | 2 0x90303d0 52 | 3 0x90303d0 53 | 4 0x90303d0 54 | 5 0x90303d0 55 | 6 0x90303d0 56 | 7 0x90303d0 57 | 8 0x90303d0 58 | 9 0x90303d0 59 | 10 0x90303d0 60 | 11 0x90303d0 61 | 12 0x90303d0 62 | 13 0x90303d0 63 | 14 0x90303d0 64 | 15 0x90303d0 65 | 16 0x80483d0 66 | 67 | [0x080485dd]> 68 | ``` 69 | 70 | We can then print the stdin of the remaining state with the `Msi` command. 71 | 72 | ``` 73 | [0x080485dd]> Msi 74 | Active state 0 at 0x80485dd: 75 | RNGFXITY 76 | ``` 77 | 78 | This password can be checked against the binary. 79 | 80 | ``` 81 | [0x080485dd]> q 82 | shell@shell:~/github/r2angr/docs/challenges$ ./01_angr_avoid 83 | placeholder 84 | Enter the password: RNGFXITY 85 | Good Job. 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/tutorial3.md: -------------------------------------------------------------------------------- 1 | # Finding Output 2 | 3 | For this challenge we'll be exploring until a state has a certain string in stdin. The binary is 02_angr_find_condition. We'll start by opening the binary and running some basic analysis. 4 | 5 | ``` 6 | shell@shell:~/github/r2angr/docs/challenges$ r2 02_angr_find_condition 7 | -- Check your IO plugins with 'r2 -L' 8 | [0x08048450]> aa 9 | [x] Analyze all flags starting with sym. and entry0 (aa) 10 | [0x08048450]> s main 11 | [0x080485c8]> 12 | ``` 13 | 14 | The CFG for the main function is, once again, very large, so I recommend you avoid graph mode. We can see from scrolling through the function in visual mode that again it gets user input from stdin, calls `sym.complex_function`, and then there is a large branching structure with various success/fail prints. Without investigating much futher, we already know we want it to print "Good Job.", so let's try exploring until we find that. First though, let's add hooks to all functions that print a debug statement so we can trace what's happening during exploration. We can add the hooks as shown below. 15 | 16 | ``` 17 | [0x08048778]> Mhf 18 | [R2ANGR] Importing angr 19 | [R2ANGR] Loading r2angr 20 | [R2ANGR] Initialized r2angr at entry point 21 | [HOOKS] Hooking function: entry0 at 0x8048450 22 | [HOOKS] Hooking import: sym.imp.__libc_start_main at 0x8048420 23 | [HOOKS] Hooking function: sym.deregister_tm_clones at 0x8048490 24 | [HOOKS] Hooking function: sym.register_tm_clones at 0x80484c0 25 | [HOOKS] Hooking function: sym.__do_global_dtors_aux at 0x8048500 26 | [HOOKS] Hooking function: entry.init0 at 0x8048520 27 | [HOOKS] Hooking function: sym.__libc_csu_fini at 0x804d2f0 28 | [HOOKS] Hooking function: sym.__x86.get_pc_thunk.bx at 0x8048480 29 | [HOOKS] Hooking function: sym.complex_function at 0x8048569 30 | [HOOKS] Hooking function: sym._fini at 0x804d2f4 31 | [HOOKS] Hooking function: sym.__libc_csu_init at 0x804d290 32 | [HOOKS] Hooking function: main at 0x80485c8 33 | [HOOKS] Hooking function: sym.print_msg at 0x804854b 34 | [HOOKS] Hooking import: sym.imp.printf at 0x80483e0 35 | [HOOKS] Hooking function: sym._init at 0x8048394 36 | [HOOKS] Hooking import: sym.imp.strcmp at 0x80483d0 37 | [HOOKS] Hooking import: sym.imp.__stack_chk_fail at 0x80483f0 38 | [HOOKS] Hooking import: sym.imp.puts at 0x8048400 39 | [HOOKS] Hooking import: sym.imp.exit at 0x8048410 40 | [HOOKS] Hooking import: sym.imp.__isoc99_scanf at 0x8048430 41 | [0x08048450]> 42 | ``` 43 | 44 | Then we can explore until a state has "Good Job" in stdout as shown below. 45 | 46 | ``` 47 | [0x08048450]> Meo Good Job 48 | [DEBUG] Starting exploration. Find: [Good Job] 49 | [HOOKS] Called entry0 (); 50 | [HOOKS] Called int sym.imp.__libc_start_main (func main, int argc, char **ubp_av, func init, func fini, func rtld_fini, void *stack_end); 51 | [HOOKS] Called sym.__libc_csu_init (int32_t arg_4h, int32_t arg_8h); 52 | WARNING | 2020-06-15 17:42:39,342 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. 53 | WARNING | 2020-06-15 17:42:39,342 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: 54 | WARNING | 2020-06-15 17:42:39,342 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state 55 | WARNING | 2020-06-15 17:42:39,342 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null 56 | WARNING | 2020-06-15 17:42:39,342 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages. 57 | WARNING | 2020-06-15 17:42:39,342 | angr.state_plugins.symbolic_memory | Filling register edi with 4 unconstrained bytes referenced from 0x804d291 (__libc_csu_init+0x1 in 02_angr_find_condition (0x804d291)) 58 | WARNING | 2020-06-15 17:42:39,344 | angr.state_plugins.symbolic_memory | Filling register ebx with 4 unconstrained bytes referenced from 0x804d293 (__libc_csu_init+0x3 in 02_angr_find_condition (0x804d293)) 59 | [HOOKS] Called sym.__x86.get_pc_thunk.bx (); 60 | [HOOKS] Called sym._init (); 61 | [HOOKS] Called sym.__x86.get_pc_thunk.bx (); 62 | [HOOKS] Called entry.init0 (); 63 | [HOOKS] Called sym.register_tm_clones (); 64 | [HOOKS] Called int main (int argc, char **argv, char **envp); 65 | [HOOKS] Called sym.print_msg (); 66 | [HOOKS] Called int sym.imp.printf (const char *format); 67 | WARNING | 2020-06-15 17:42:39,838 | angr.state_plugins.symbolic_memory | Filling memory at 0x804f040 with 240 unconstrained bytes referenced from 0x90512d0 (printf+0x0 in libc.so.6 (0x512d0)) 68 | [HOOKS] Called int sym.imp.printf (const char *format); 69 | [HOOKS] Called int sym.imp.__isoc99_scanf (const char *format); 70 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 71 | [HOOKS] Called int sym.imp.puts (const char *s); 72 | [HOOKS] Called int sym.imp.puts (const char *s); 73 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 74 | [HOOKS] Called void sym.imp.exit (int status); 75 | [HOOKS] Called void sym.imp.exit (int status); 76 | [HOOKS] Called int sym.imp.puts (const char *s); 77 | [HOOKS] Called int sym.imp.puts (const char *s); 78 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 79 | [HOOKS] Called void sym.imp.exit (int status); 80 | [HOOKS] Called void sym.imp.exit (int status); 81 | [HOOKS] Called int sym.imp.puts (const char *s); 82 | [HOOKS] Called int sym.imp.puts (const char *s); 83 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 84 | [HOOKS] Called void sym.imp.exit (int status); 85 | [HOOKS] Called void sym.imp.exit (int status); 86 | [HOOKS] Called int sym.imp.puts (const char *s); 87 | [HOOKS] Called int sym.imp.puts (const char *s); 88 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 89 | [HOOKS] Called void sym.imp.exit (int status); 90 | [HOOKS] Called void sym.imp.exit (int status); 91 | [HOOKS] Called int sym.imp.puts (const char *s); 92 | [HOOKS] Called int sym.imp.puts (const char *s); 93 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 94 | [HOOKS] Called void sym.imp.exit (int status); 95 | [HOOKS] Called void sym.imp.exit (int status); 96 | [HOOKS] Called int sym.imp.puts (const char *s); 97 | [HOOKS] Called int sym.imp.puts (const char *s); 98 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 99 | [HOOKS] Called void sym.imp.exit (int status); 100 | [HOOKS] Called void sym.imp.exit (int status); 101 | [HOOKS] Called int sym.imp.puts (const char *s); 102 | [HOOKS] Called int sym.imp.puts (const char *s); 103 | [HOOKS] Called sym.complex_function (int32_t arg_8h, int32_t arg_ch); 104 | [HOOKS] Called void sym.imp.exit (int status); 105 | [HOOKS] Called void sym.imp.exit (int status); 106 | [HOOKS] Called int sym.imp.puts (const char *s); 107 | [HOOKS] Called int sym.imp.puts (const char *s); 108 | [HOOKS] Called void sym.imp.exit (int status); 109 | [HOOKS] Called void sym.imp.exit (int status); 110 | [HOOKS] Called int sym.imp.strcmp (const char *s1, const char *s2); 111 | WARNING | 2020-06-15 17:42:41,897 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffefffc with 72 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 112 | WARNING | 2020-06-15 17:42:41,898 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff70 with 4 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 113 | WARNING | 2020-06-15 17:42:41,898 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffeff4d with 11 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 114 | WARNING | 2020-06-15 17:42:41,910 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fff0044 with 20 unconstrained bytes referenced from 0x907e300 (strcmp+0x0 in libc.so.6 (0x7e300)) 115 | [HOOKS] Called int sym.imp.puts (const char *s); 116 | [HOOKS] Called int sym.imp.puts (const char *s); 117 | [DEBUG] Found 1 solutions 118 | [0x080495d8]> 119 | ``` 120 | 121 | Our hooks give us some idea of what was happening during this exploration. We can then list the remaining states with the `Msl` command. 122 | 123 | ``` 124 | [0x080495d8]> Msl 125 | Active states: 126 | 0 0x804d26c 127 | 1 0x80495d8 128 | 129 | Deadended states: 130 | 0 0x90303d0 131 | 1 0x90303d0 132 | 2 0x90303d0 133 | 3 0x90303d0 134 | 4 0x90303d0 135 | 5 0x90303d0 136 | 6 0x90303d0 137 | 7 0x90303d0 138 | 8 0x90303d0 139 | 9 0x90303d0 140 | 10 0x90303d0 141 | 11 0x90303d0 142 | 12 0x90303d0 143 | 13 0x90303d0 144 | 14 0x90303d0 145 | 15 0x90303d0 146 | ``` 147 | 148 | We have two active states, let's see what is in stdout for each one using the `Mso` command. 149 | 150 | ``` 151 | [0x080495d8]> Mso 152 | Active state 0 at 0x804d26c: 153 | placeholder 154 | Enter the password: Try again. 155 | 156 | Active state 1 at 0x80495d8: 157 | placeholder 158 | Enter the password: Good Job. 159 | 160 | [0x080495d8]> 161 | ``` 162 | 163 | Looks like the first one failed, we'll kill it with the `Msk` command. 164 | 165 | ``` 166 | [0x080495d8]> Msk 0 167 | [0x080495d8]> 168 | ``` 169 | 170 | Now we'll list the stdin for the final state as shown below. 171 | 172 | ``` 173 | [0x080495d8]> Msi 174 | Active state 0 at 0x80495d8: 175 | UFOHHURD 176 | [0x080495d8]> 177 | ``` 178 | 179 | Let's try this password on the binary. 180 | 181 | ``` 182 | [0x080495d8]> q 183 | shell@shell:~/github/r2angr/docs/challenges$ ./02_angr_find_condition 184 | placeholder 185 | Enter the password: UFOHHURD 186 | Good Job. 187 | shell@shell:~/github/r2angr/docs/challenges$ 188 | ``` 189 | -------------------------------------------------------------------------------- /docs/tutorial4.md: -------------------------------------------------------------------------------- 1 | # Bitvector basics 2 | 3 | For this challenge we'll experiment with symbolizing registers and initializing at a different location than the entry point. We'll start by opening the binary and running some analysis. 4 | 5 | ``` 6 | shell@shell:~/github/r2angr/docs/challenges$ r2 03_angr_symbolic_registers 7 | -- A git pull a day keeps the segfault away 8 | [0x080483f0]> aaa 9 | [x] Analyze all flags starting with sym. and entry0 (aa) 10 | [x] Analyze function calls (aac) 11 | [x] Analyze len bytes of instructions for references (aar) 12 | [x] Check for objc references 13 | [x] Check for vtables 14 | [x] Type matching analysis for all functions (aaft) 15 | [x] Propagate noreturn information 16 | [x] Use -AA or aaaa to perform additional experimental analysis. 17 | [0x080483f0]> s main 18 | [0x080488a6]> 19 | ``` 20 | 21 | Looking at the main function we can see it calls a `sym.get_user_input()` function, which calls scanf with "%x %x %s". In previous challenges we relied on angr to automatically symbolize stdin, but angr doesn't know how to deal with multiple stdin values automatically. Since the three inputs end up in eax, ebx, and edx, we'll try initializing after the function, symbolizing the registers manually, and then exploring as normal. 22 | 23 | First we'll initialize a blank state at `0x80488d1`, after the call to `sym.get_user_input`. 24 | 25 | ``` 26 | [0x080488a6]> Mi 27 | [R2ANGR] Importing angr 28 | [R2ANGR] Loading r2angr 29 | [R2ANGR] Initialized r2angr at entry point 30 | WARNING | 2020-06-15 17:54:43,382 | angr.sim_state | Unused keyword arguments passed to SimState: args 31 | [0x080488a6]> Mib @ 0x80488d1 32 | [R2ANGR] Initialized r2angr blank state at current address 33 | [0x080488a6]> 34 | ``` 35 | 36 | Then we'll symbolize the three registers with bitvectors using the `Mbr` command. 37 | 38 | ``` 39 | [0x080488a6]> Mbr eax 40 | Symbolizing eax in active state 0 at 0x80483f0 41 | [0x080483f0]> Mbr ebx 42 | Symbolizing ebx in active state 0 at 0x80483f0 43 | [0x080483f0]> Mbr edx 44 | Symbolizing edx in active state 0 at 0x80483f0 45 | [0x080483f0]> 46 | ``` 47 | 48 | And we'll explore to the success branch. 49 | 50 | ``` 51 | :> Meu 0x8048937 52 | [DEBUG] Starting exploration. Find: [0x8048937] 53 | WARNING | 2020-06-15 17:58:01,969 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. 54 | WARNING | 2020-06-15 17:58:01,969 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: 55 | WARNING | 2020-06-15 17:58:01,969 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state 56 | WARNING | 2020-06-15 17:58:01,969 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null 57 | WARNING | 2020-06-15 17:58:01,969 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages. 58 | WARNING | 2020-06-15 17:58:01,969 | angr.state_plugins.symbolic_memory | Filling register ebp with 4 unconstrained bytes referenced from 0x80488d1 (main+0x2b in 03_angr_symbolic_registers (0x80488d1)) 59 | WARNING | 2020-06-15 17:58:01,970 | angr.state_plugins.symbolic_memory | Filling register eax with 4 unconstrained bytes referenced from 0x80488d1 (main+0x2b in 03_angr_symbolic_registers (0x80488d1)) 60 | WARNING | 2020-06-15 17:58:02,007 | angr.state_plugins.symbolic_memory | Filling register ebx with 4 unconstrained bytes referenced from 0x80488d4 (main+0x2e in 03_angr_symbolic_registers (0x80488d4)) 61 | WARNING | 2020-06-15 17:58:02,020 | angr.state_plugins.symbolic_memory | Filling register edx with 4 unconstrained bytes referenced from 0x80488d7 (main+0x31 in 03_angr_symbolic_registers (0x80488d7)) 62 | [DEBUG] Found 1 solutions 63 | ``` 64 | 65 | We can list the active states. 66 | 67 | ``` 68 | :> Msl 69 | Active states: 70 | 0 0x80483b0 71 | 1 0x9067b40 72 | 2 0x8048932 73 | 3 0x8048937 74 | ``` 75 | 76 | Since we only care about the one at `0x8048937`, we'll extract it and kill the others using the `Mse` command and the index. 77 | 78 | ``` 79 | :> Mse 3 80 | :> Msl 81 | Active states: 82 | 0 0x8048937 83 | 84 | Deadended states: 85 | 0 0x80483b0 86 | 1 0x9067b40 87 | 2 0x8048932 88 | ``` 89 | 90 | Finally, we'll solve our bitvectors (which were used to symbolize the registers) using the `Mbs` command. 91 | 92 | ``` 93 | :> Mbs 94 | 3674319863 95 | 1227938937 96 | 3514688974 97 | ``` 98 | 99 | If we convert these to hex (since stdin used "%x %x %x"), we get `db01abf7`, `4930dc79`, and `d17de5ce`. Let's try these as inputs to the binary. 100 | 101 | ``` 102 | [0x08048937]> q 103 | shell@shell:~/github/r2angr/docs/challenges$ ./03_angr_symbolic_registers 104 | placeholder 105 | Enter the password: db01abf7 4930dc79 d17de5ce 106 | Good Job. 107 | shell@shell:~/github/r2angr/docs/challenges$ 108 | ``` 109 | -------------------------------------------------------------------------------- /docs/visualization.md: -------------------------------------------------------------------------------- 1 | # Visualization 2 | 3 | ## State locations 4 | 5 | Modality will add highlights to radare2's visual mode at each state location. Active states are highlighted in blue, and deadended states are highlighted in red. Comments with the state index are also added at these locations, making killing/extracting states more efficient. 6 | 7 | Note: You'll have to press `r` to refresh the graph view each time a command is run for the highlights to update until I figure out how to make it auto-refresh. 8 | 9 | ## Other 10 | 11 | More visualizations coming soon. 12 | -------------------------------------------------------------------------------- /docs/watchpoints.md: -------------------------------------------------------------------------------- 1 | # Watchpoints 2 | 3 | Coming soon. 4 | -------------------------------------------------------------------------------- /plugin.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # Copyright (C) 2017 Chase Kanipe 3 | 4 | """ 5 | modality 6 | """ 7 | 8 | import r2lang 9 | import r2pipe 10 | import sys 11 | 12 | r = r2pipe.open() 13 | 14 | session = None 15 | initialized = False 16 | 17 | def modality(_): 18 | global session 19 | global initialized 20 | 21 | 22 | def process(command): 23 | global session 24 | global initialized 25 | 26 | if not command.startswith("M"): 27 | return 0 28 | 29 | binary = r.cmd("i~file").split("\n")[0].split(" ")[-1] 30 | 31 | if not initialized: 32 | sys.path.append("src/") 33 | try: 34 | from r2angr import R2ANGR 35 | session = R2ANGR(binary, r) 36 | initialized = True 37 | 38 | session.load_angr() 39 | 40 | except Exception as e: 41 | print(e) 42 | try: 43 | session.run(command[1:]) 44 | except Exception as e: 45 | print(e) 46 | 47 | try: 48 | return session.return_value 49 | except: 50 | return 1 51 | 52 | return {"name": "r2-angr", 53 | "licence": "GPLv3", 54 | "desc": "Integrates angr with radare2", 55 | "call": process} 56 | 57 | if not r2lang.plugin("core", modality): 58 | print("An error occurred while registering modality") 59 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # modality logo 2 | 3 | A **radare2** plugin to integrate the symbolic execution capabilities of **angr**. 4 | 5 | --- 6 | 7 | This project is *mid-development so bugs and missing features are expected*. The tool has partial gitbook [documentation](https://chasekanipe.gitbook.io/modality/). 8 | 9 |
10 | 11 |

12 | 13 |

14 | 15 | --- 16 | 17 | ## Installation (Manual) 18 | First install the python prerequisites 19 | 20 | ``` 21 | pip3 install angr termcolor r2pipe angrdbg cooldict 22 | ``` 23 | 24 | Then install r2lang 25 | 26 | ``` 27 | r2pm -i lang-python 28 | ``` 29 | 30 | Then clone the modality repo into the r2pm git folder. 31 | 32 | ``` 33 | git clone https://github.com/0xchase/modality .local/share/radare2/r2pm/git/modality 34 | ``` 35 | 36 | Copy the top level script to the plugins folder. 37 | 38 | ``` 39 | cp .local/share/radare2/r2pm/git/modality/plugin.py .local/share/radare2/plugins/modality.py 40 | ``` 41 | 42 | Then add the following lines to your `.bashrc` 43 | 44 | ``` 45 | export PYTHONPATH=~/.local/share/radare2/r2pm/git/modality/:~/.local/share/radare2/r2pm/git/modality/src: 46 | ``` 47 | 48 | The tool has only been tested on Ubuntu 20.04 (so far). If you have installation issues feel free to create a git issue. 49 | 50 | --- 51 | 52 | ## Goals 53 | 54 | This project intends to 55 | - Better integrate symbolic execution with the rest of the reverse engineering process 56 | - Provide a faster alternative to using angr than writing scripts 57 | - Provide useful visualizations of the angr backend 58 | - Allow for switching between concrete and symbolic execution (this feature is coming soon) 59 | - Include a suite of features for vulnerability detection, exploit generation, etc (coming soon) 60 | 61 | -------------------------------------------------------------------------------- /src/bitvectors.py: -------------------------------------------------------------------------------- 1 | from termcolor import colored 2 | import claripy 3 | 4 | # Chage commands to kill/revive state by numbers rather than addresses 5 | 6 | # ========== Commands ========== # 7 | class Bitvectors(): 8 | symbolic_values = [] 9 | 10 | def symbolize_register(self): 11 | reg = self.r2angr.command[1] 12 | 13 | bvs32 = claripy.BVS("symbolic_value" + str(len(self.symbolic_values)), 32) 14 | bvs64 = claripy.BVS("symbolic_value" + str(len(self.symbolic_values)), 64) 15 | 16 | i = 0 17 | for state in self.r2angr.simgr.active: 18 | 19 | if reg == "eax": 20 | state.regs.eax = bvs32 21 | self.symbolic_values.append(bvs32) 22 | elif reg == "ebx": 23 | state.regs.ebx = bvs32 24 | self.symbolic_values.append(bvs32) 25 | elif reg == "ecx": 26 | state.regs.ecx = bvs32 27 | self.symbolic_values.append(bvs32) 28 | elif reg == "edx": 29 | state.regs.edx = bvs32 30 | self.symbolic_values.append(bvs32) 31 | elif reg == "esi": 32 | state.regs.esi = bvs32 33 | self.symbolic_values.append(bvs32) 34 | elif reg == "edi": 35 | state.regs.edi = bvs32 36 | self.symbolic_values.append(bvs32) 37 | elif reg == "rax": 38 | state.regs.rax = bvs64 39 | self.symbolic_values.append(bvs64) 40 | elif reg == "rbx": 41 | state.regs.rbx = bvs64 42 | self.symbolic_values.append(bvs64) 43 | elif reg == "rcx": 44 | state.regs.rcx = bvs64 45 | self.symbolic_values.append(bvs64) 46 | elif reg == "rdx": 47 | state.regs.rdx = bvs64 48 | self.symbolic_values.append(bvs64) 49 | elif reg == "rsi": 50 | state.regs.rsi = bvs64 51 | self.symbolic_values.append(bvs64) 52 | elif reg == "rdi": 53 | state.regs.rdi = bvs64 54 | self.symbolic_values.append(bvs64) 55 | else: 56 | print(colored("Register not supported", "red")) 57 | return 58 | 59 | print("Symbolizing " + reg + colored(" in active", "cyan") + " state " + str(i) + " at " + colored(hex(state.addr), "green")) 60 | 61 | i += 1 62 | 63 | def solve(self): 64 | print("Solving...") 65 | for state in self.r2angr.simgr.active: 66 | for value in self.symbolic_values: 67 | solution = state.solver.eval(value) 68 | print(solution) 69 | 70 | -------------------------------------------------------------------------------- /src/bvs.py: -------------------------------------------------------------------------------- 1 | class BVS(): 2 | def __init__(self, filename): 3 | print("Setup BVS") 4 | 5 | bvs_arr = [] 6 | 7 | def bvs_register(self): 8 | command = self.command 9 | sym_arg = claripy.BVS('sym_arg', 8*8) 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/concrete/__pycache__/concrete.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/src/concrete/__pycache__/concrete.cpython-36.pyc -------------------------------------------------------------------------------- /src/concrete/__pycache__/memory_map.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/src/concrete/__pycache__/memory_map.cpython-36.pyc -------------------------------------------------------------------------------- /src/concrete/__pycache__/target.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xchase/modality/653d65ef4ea833bcf12f0c0fc5351ff72faa09f3/src/concrete/__pycache__/target.cpython-36.pyc -------------------------------------------------------------------------------- /src/concrete/concrete.py: -------------------------------------------------------------------------------- 1 | import logging 2 | l = logging.getLogger("angr_targets.concrete") 3 | #l.setLevel(logging.DEBUG) 4 | 5 | class ConcreteTarget(object): 6 | """ 7 | Concrete target used inside the SimConcreteEngine. 8 | This object is defined in the angr script. 9 | """ 10 | def __init__(self): 11 | self.timeout = None 12 | return 13 | 14 | def read_memory(self, address,nbytes, **kwargs): 15 | """ 16 | Reading from memory of the target 17 | :param int address: The address to read from 18 | :param int nbytes: The amount number of bytes to read 19 | :return: The memory read 20 | :rtype: bytes 21 | :raise angr.errors.ConcreteMemoryError: 22 | """ 23 | raise NotImplementedError() 24 | 25 | def write_memory(self, address, value, **kwargs): 26 | """ 27 | Writing to memory of the target 28 | :param int address: The address from where the memory-write should start 29 | :param str value: The actual value written to memory 30 | :raise angr.errors.ConcreteMemoryError: 31 | """ 32 | raise NotImplementedError() 33 | 34 | def read_register(self, register, **kwargs): 35 | """" 36 | Reads a register from the target 37 | :param str register: The name of the register 38 | :return: int value of the register content 39 | :rtype int 40 | :raise angr.errors.ConcreteRegisterError: in case the register doesn't exist or any other exception 41 | """ 42 | raise NotImplementedError() 43 | 44 | def write_register(self, register, value, **kwargs): 45 | """ 46 | Writes a register to the target 47 | :param str register: The name of the register 48 | :param int value: int value written to be written register 49 | :raise angr.errors.ConcreteRegisterError: 50 | """ 51 | raise NotImplementedError() 52 | 53 | def set_breakpoint(self, address, **kwargs): 54 | """ 55 | Inserts a breakpoint 56 | :param int address: The address at which to set the breakpoint 57 | :param optional bool hardware: Hardware breakpoint 58 | :param optional bool temporary: Tempory breakpoint 59 | :raise angr.errors.ConcreteBreakpointError: 60 | """ 61 | raise NotImplementedError() 62 | 63 | def remove_breakpoint(self, address, **kwargs): 64 | raise NotImplementedError() 65 | 66 | def set_watchpoint(self, address, **kwargs): 67 | """ 68 | Inserts a watchpoint 69 | :param address: The name of a variable or an address to watch 70 | :param optional bool write: Write watchpoint 71 | :param optional bool read: Read watchpoint 72 | :raise angr.errors.ConcreteBreakpointError: 73 | """ 74 | raise NotImplementedError() 75 | 76 | def remove_watchpoint(self, address, **kwargs): 77 | raise NotImplementedError() 78 | 79 | def get_mappings(self): 80 | raise NotImplementedError() 81 | 82 | def run(self): 83 | raise NotImplementedError() 84 | 85 | def stop(self): 86 | raise NotImplementedError() 87 | 88 | def execute_shellcode(self, shellcode, result_register): 89 | """ 90 | Use the methods provided by the ConcreteTarget to inject shellcode in concrete process and get the result of the shellcode in the "result_register" register 91 | :param concrete_target: ConcreteTarget where the shellcode will be injected 92 | :param shellcode: shellcode to be executed 93 | :param result_register: register which will contain the result 94 | :return: value contained in the result_register 95 | Example read fs[0] value on x64 96 | shellcode = "\x64\x48\x8B\x04\x25\x00\x00\x00\x00" # mov rax, fs:[0] 97 | result_register = "rax" 98 | execute_shellcode(target, shellcode, result_register) 99 | """ 100 | # FIXME: registers could be clobbered during shellcode execution, we should save 101 | # registers before shellcode and then restore them. 102 | 103 | l.debug("Execute shellcode method!") 104 | 105 | len_payload = len(shellcode) 106 | 107 | l.debug("encoded shellcode %s len shellcode %s" % (str(shellcode), len_payload)) 108 | 109 | pc = self.read_register("pc") 110 | l.debug("current pc %x" % (pc)) 111 | 112 | # save the content of the current instruction 113 | old_instr_content = self.read_memory(pc, len_payload) 114 | 115 | l.debug("current instruction %s" % (str(old_instr_content))) 116 | 117 | # saving value of the register which will be used to read segment register 118 | old_reg_value = self.read_register(result_register) 119 | l.debug("exfiltration reg %s value %x" % (result_register, old_reg_value)) 120 | 121 | # writing to pc shellcode 122 | self.write_memory(pc, shellcode) 123 | 124 | cur_instr_after_write = self.read_memory(pc, len_payload) 125 | l.debug("current instruction after write %s" % (str(cur_instr_after_write))) 126 | 127 | l.debug('setting breakpoint at address %#x' % (pc+len_payload)) 128 | 129 | self.set_breakpoint(pc + len_payload, temporary=True) 130 | self.run() 131 | 132 | current_pc = self.read_register("pc") 133 | l.debug("current pc %x" % (current_pc)) 134 | 135 | result_value = self.read_register(result_register) 136 | l.debug("result value %x " % (result_value)) 137 | 138 | # restoring previous pc 139 | self.write_register("pc", pc) 140 | 141 | current_pc = self.read_register("pc") 142 | l.debug("current pc %x" % (current_pc)) 143 | 144 | 145 | # restoring previous instruction 146 | self.write_memory(pc, old_instr_content) 147 | 148 | # restoring previous rax value 149 | self.write_register(result_register, old_reg_value) 150 | 151 | pc = self.read_register("pc") 152 | eax_value = self.read_register(result_register) 153 | instr_content = self.read_memory(pc, len_payload) 154 | l.debug("pc %x eax value %x instr content %s " % (pc, eax_value, str(instr_content))) 155 | 156 | return result_value 157 | -------------------------------------------------------------------------------- /src/concrete/memory_map.py: -------------------------------------------------------------------------------- 1 | class MemoryMap: 2 | """ 3 | Describing a memory range inside the concrete 4 | process. 5 | """ 6 | def __init__(self, start_address, end_address, offset, name): 7 | self.start_address = start_address 8 | self.end_address = end_address 9 | self.offset = offset 10 | self.name = name 11 | 12 | def __str__(self): 13 | my_str = "MemoryMap[start_address: 0x%x | end_address: 0x%x | name: %s" \ 14 | % (self.start_address, 15 | self.end_address, 16 | self.name) 17 | 18 | return my_str 19 | -------------------------------------------------------------------------------- /src/concrete/target.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from base64 import b64encode, b64decode 4 | 5 | from angr.errors import SimConcreteMemoryError, SimConcreteRegisterError, SimConcreteBreakpointError 6 | 7 | from angr_targets.concrete import ConcreteTarget 8 | 9 | l = logging.getLogger("angr_targets.r2") 10 | l.setLevel(logging.DEBUG) 11 | 12 | 13 | class R2ConcreteTarget(ConcreteTarget): 14 | 15 | def __init__(self, r2): 16 | self.r2 = r2 17 | super(R2ConcreteTarget, self).__init__() 18 | 19 | print("Initialized R2ConcreteTarget") 20 | 21 | def exit(self): 22 | self.r2.quit() 23 | 24 | def read_memory(self, address, nbytes, **kwargs): 25 | """ 26 | Reading from memory of the target 27 | :param int address: The address to read from 28 | :param int nbytes: The amount number of bytes to read 29 | :return: The memory read 30 | :rtype: str 31 | :raise angr.errors.SimMemoryError 32 | """ 33 | 34 | print("Reading R2ConcreteTarget memory") 35 | 36 | l.debug("R2ConcreteTarget read_memory at %x "%(address)) 37 | 38 | # Check if it's mapped 39 | if self.r2.cmd('dm.@{}'.format(hex(address))) == '': 40 | error = "R2ConcreteTarget can't read_memory at address {address}. Page is not mapped.".format(address=hex(address)) 41 | l.error(error) 42 | raise SimConcreteMemoryError(error) 43 | 44 | return bytes(self.r2.cmdj('pxj {} @ {}'.format(nbytes, hex(address)))) 45 | 46 | 47 | def write_memory(self,address, value, **kwargs): 48 | """ 49 | Writing to memory of the target 50 | :param int address: The address from where the memory-write should start 51 | :param bytes value: The actual value written to memory 52 | :raise angr.errors.ConcreteMemoryError 53 | """ 54 | 55 | print("Writing R2ConcreteTarget memory") 56 | 57 | assert type(value) is bytes, 'write_memory value is actually type {}'.format(type(value)) 58 | 59 | l.debug("R2ConcreteTarget write_memory at %x value %s " %(address, value)) 60 | 61 | try: 62 | self.r2.cmd("w6d {} @ {}".format(b64encode(value).decode(), hex(address))) 63 | """ 64 | if not res: 65 | l.warning("R2ConcreteTarget failed write_memory at %x value %s"%(address,value)) 66 | raise SimConcreteMemoryError("R2ConcreteTarget failed write_memory to address %x" % (address)) 67 | """ 68 | except Exception as e: 69 | l.warning("R2ConcreteTarget write_memory at %x value %s exception %s"%(address,value,e)) 70 | raise SimConcreteMemoryError("R2ConcreteTarget write_memory at %x value %s exception %s" % (address, str(value), e)) 71 | 72 | 73 | def read_register(self,register,**kwargs): 74 | """" 75 | Reads a register from the target 76 | :param str register: The name of the register 77 | :return: int value of the register content 78 | :rtype int 79 | :raise angr.errors.ConcreteRegisterError in case the register doesn't exist or any other exception 80 | """ 81 | 82 | print("Reading R2ConcreteTarget register") 83 | 84 | # Resolve some regs 85 | if register in ['pc','sp','bp']: 86 | l.debug('R2ConcreteTarget resolving %s',register) 87 | register = self.r2.cmd('drn {}'.format(register)).strip() 88 | l.debug('R2ConcreteTarget resolved to %s', register) 89 | 90 | try: 91 | l.debug("R2ConcreteTarget read_register at %s "%(register)) 92 | registers = self.r2.cmdj('drtj all') 93 | except Exception as e: 94 | l.debug("R2ConcreteTarget read_register %s exception %s %s "%(register,type(e).__name__,e)) 95 | raise SimConcreteRegisterError("R2ConcreteTarget can't read register %s exception %s" % (register, e)) 96 | 97 | if register in registers: 98 | return registers[register] 99 | 100 | # XMM 101 | if register.startswith('xmm'): 102 | try: 103 | return (registers[register + 'l'] << 64) + registers[register + 'h'] 104 | except KeyError: 105 | # This can be somewhat expected... xmm wasn't found 106 | l.warn('{} was not found.'.format(register)) 107 | pass 108 | 109 | # R2 Currently also has a bug with floating point registers being shown incorrectly: https://github.com/radare/radare2/issues/13118 110 | 111 | error = 'Unhandled register read of {}'.format(register) 112 | l.error(error) 113 | raise SimConcreteRegisterError(error) 114 | 115 | def write_register(self, register, value, **kwargs): 116 | """ 117 | Writes a register to the target 118 | :param str register: The name of the register 119 | :param int value: int value written to be written register 120 | :raise angr.errors.ConcreteRegisterError 121 | """ 122 | 123 | print("Writing R2ConcreteTarget register") 124 | 125 | # XMM/ST writes fail atm: https://github.com/radare/radare2/issues/13090 126 | # Resolve some regs 127 | if register in ['pc','sp','bp']: 128 | l.debug('R2ConcreteTarget resolving %s',register) 129 | register = self.r2.cmd('drn {}'.format(register)).strip() 130 | l.debug('R2ConcreteTarget resolved to %s', register) 131 | 132 | registers = self.r2.cmdj('drtj all') 133 | 134 | #TODO: Implement xmm writes 135 | 136 | if register not in registers: 137 | error = "R2ConcreteTarget write_register unhandled reg name of {}".format(register) 138 | l.error(error) 139 | raise SimConcreteRegisterError(error) 140 | 141 | l.debug("R2ConcreteTarget write_register at %s value %x "%(register,value)) 142 | self.r2.cmd('dr {}={}'.format(register, value)) 143 | 144 | # Validate write 145 | if self.read_register(register) != value: 146 | error = "R2ConcreteTarget write_register failed to correctly set register {}={}".format(register, hex(value)) 147 | l.error(error) 148 | raise SimConcreteRegisterError(error) 149 | 150 | 151 | def set_breakpoint(self,address, **kwargs): 152 | """Inserts a breakpoint 153 | :param optional bool hardware: Hardware breakpoint 154 | :param optional bool temporary: Tempory breakpoint 155 | :param optional str regex: If set, inserts breakpoints matching the regex 156 | :param optional str condition: If set, inserts a breakpoint with the condition 157 | :param optional int ignore_count: Amount of times the bp should be ignored 158 | :param optional int thread: Thread cno in which this breakpoints should be added 159 | :raise angr.errors.ConcreteBreakpointError 160 | """ 161 | 162 | if kwargs != {}: 163 | l.warn('R2ConcreteTarget set_breakpoint called with extra args "{}". Currently, R2 is not handling these and will set breakpoint as normal software breakpoint.'.format(kwargs)) 164 | 165 | l.debug("R2ConcreteTarget set_breakpoint at %x "%(address)) 166 | self.r2.cmd('db {}'.format(hex(address))) 167 | 168 | # Sanity check that breakpoint actually got set 169 | if not any(x for x in self.r2.cmdj('dbj') if x['addr'] == address): 170 | raise SimConcreteBreakpointError("R2ConcreteTarget failed to set_breakpoint at %x" % (address)) 171 | 172 | def remove_breakpoint(self, address, **kwargs): 173 | l.debug("R2ConcreteTarget remove_breakpoint at %x "%(address)) 174 | self.r2.cmd('db-{}'.format(hex(address))) 175 | 176 | # Sanity check that breakpoint got removed 177 | if any(x for x in self.r2.cmdj('dbj') if x['addr'] == address): 178 | raise SimConcreteBreakpointError("R2ConcreteTarget failed to remove_breakpoint at %x" % (address)) 179 | 180 | def set_watchpoint(self,address, **kwargs): 181 | """Inserts a watchpoint 182 | :param address: The name of a variable or an address to watch 183 | :param optional bool write: Write watchpoint 184 | :param optional bool read: Read watchpoint 185 | :raise angr.errors.ConcreteBreakpointError 186 | """ 187 | 188 | read = kwargs.pop('read', False) 189 | write = kwargs.pop('write', False) 190 | 191 | rw_str = '' 192 | if read: 193 | rw_str += 'r' 194 | if write: 195 | rw_str += 'w' 196 | 197 | if rw_str == '': 198 | error = 'R2ConcreteTarget set_watchpoint invalid watch requested for address {}. Must specify type of watchpoint.'.format(hex(address)) 199 | l.error(error) 200 | raise SimConcreteBreakpointError(error) 201 | 202 | if kwargs != {}: 203 | l.warn('R2ConcreteTarget set_watchpoint called with extra args "{}".'.format(kwargs)) 204 | 205 | l.debug("R2ConcreteTarget target set_watchpoing at %x value"%(address)) 206 | self.r2.cmd('dbw {address} {fmt}'.format(address=address, fmt=rw_str)) 207 | 208 | # Make sure it got set 209 | if not any(x for x in self.r2.cmdj('dbj') if x['addr'] == address and x['hw'] == True): 210 | raise SimConcreteBreakpointError("R2ConcreteTarget failed to set_breakpoint at %x" % (address)) 211 | 212 | def remove_watchpoint(self,address, **kwargs): 213 | """Removes a watchpoint 214 | :param address: The name of a variable or an address to watch 215 | :raise angr.errors.ConcreteBreakpointError 216 | """ 217 | 218 | if kwargs != {}: 219 | l.warn('R2ConcreteTarget set_watchpoint called with extra args "{}".'.format(kwargs)) 220 | 221 | # R2 treats watch points the same as hw breakpoints. Just passthing this call through. 222 | self.remove_breakpoint(address) 223 | 224 | 225 | def get_mappings(self): 226 | """Returns the mmap of the concrete process 227 | :return: 228 | """ 229 | 230 | l.debug("getting the vmmap of the concrete process") 231 | sections = self.r2.cmdj('dmj') 232 | files = self.r2.cmdj('dmmj') 233 | 234 | vmmap = [] 235 | 236 | for section in sections: 237 | 238 | map_start_address = section['addr'] 239 | map_end_address = section['addr_end'] 240 | 241 | try: 242 | file_map = next(x for x in files if x['file'] == section['file']) 243 | offset = map_start_address - file_map['addr'] 244 | 245 | except: 246 | offset = 0 247 | 248 | map_name = section['file'] 249 | vmmap.append(MemoryMap(map_start_address, map_end_address, offset, map_name)) 250 | 251 | return vmmap 252 | 253 | def is_running(self): 254 | # This implementation is synchronous. We will not be running if this call is being made. 255 | return False 256 | 257 | def stop(self): 258 | # This implementation is synchronous. We will not be running if this call is being made. 259 | return 260 | 261 | def run(self): 262 | """ 263 | Resume the execution of the target 264 | :return: 265 | """ 266 | 267 | l.debug('R2ConcreteTarget run') 268 | self.r2.cmd('dc') 269 | 270 | @property 271 | def architecture(self): 272 | return self.r2.cmdj('iAj')['bins'][0]['arch'] 273 | 274 | @property 275 | def bits(self): 276 | return self.r2.cmdj('iAj')['bins'][0]['bits'] 277 | 278 | 279 | from angr_targets.memory_map import MemoryMap 280 | -------------------------------------------------------------------------------- /src/debug.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import claripy 3 | from termcolor import colored 4 | 5 | class Debugger(): 6 | watchpoints = {} 7 | 8 | def __init__(self): 9 | pass 10 | 11 | # Continues execution 12 | def debug_continue(self): 13 | self.print_debug("Continuing emulation") 14 | self.r2angr.simgr.run() 15 | 16 | # Steps execution 17 | def debug_step(self): 18 | self.print_debug("Continuing emulation one step") 19 | 20 | if len(self.r2angr.command) == 1: 21 | self.r2angr.simgr.step() 22 | else: 23 | try: 24 | num = int(self.command[1]) 25 | except: 26 | print("Usage: mcs ") 27 | return 28 | 29 | for i in range(0, num): 30 | self.r2angr.simgr.step() 31 | 32 | def debug_function(self): 33 | print("Running function") 34 | f = self.project.factory.callable(int(self.command[1], 16)) 35 | for i in range(2, len(self.command)): 36 | try: 37 | self.command[i] = int(self.command[i]) 38 | except: 39 | try: 40 | self.command[i] = int(self.command[i], 16) 41 | except: 42 | pass 43 | if len(self.command[2:]) == 0: 44 | print(colored("Calling function with 0 arguments", "yellow")) 45 | f() 46 | elif len(self.command[2:]) == 1: 47 | print(colored("Calling function with 1 arguments", "yellow")) 48 | f(self.command[2]) 49 | elif len(self.command[2:]) == 2: 50 | print(colored("Calling function with 2 arguments", "yellow")) 51 | f(self.command[2], self.command[3]) 52 | elif len(self.command[2:]) == 3: 53 | print(colored("Calling function with 3 arguments", "yellow")) 54 | f(self.command[2], self.command[3], self.command[4]) 55 | else: 56 | print(colored("Unsupported number of arguments", "red")) 57 | return 58 | print("") 59 | print(colored("Return value: " + str(f.result_state.regs.rax), "yellow")) 60 | print(colored("Return concrete: " + str(f.result_state.regs.rax.concrete), "green")) 61 | 62 | 63 | def restore_state(self, simgr1): 64 | simgr1.active = self.active_backup 65 | simgr1.deadended = self.deadended_backup 66 | 67 | # Explores using find/avoid addresses 68 | def debug_explore(self): 69 | r2p = self.r2angr.r2p 70 | find = [] 71 | avoid = [] 72 | 73 | comments = r2p.cmdj("CCj") 74 | for comment in comments: 75 | if comment["name"] == "find": 76 | find.append(comment["offset"]) 77 | if comment["name"] == "avoid": 78 | avoid.append(comment["offset"]) 79 | 80 | if len(find) == 0: 81 | print(colored("Requires at least one find comment", "yellow")) 82 | return 83 | 84 | f_str = "" 85 | for a in find: 86 | f_str += colored(hex(a), "green") + colored(", ", "yellow") 87 | f_str = f_str[:-6] 88 | 89 | a_str = "" 90 | for a in avoid: 91 | a_str += colored(hex(a), "red") + colored(", ", "yellow") 92 | a_str = a_str[:-6] 93 | 94 | self.print_debug(colored("Starting exploration.\nFind: [", "yellow") + f_str + colored("]. Avoid: [", "yellow") + a_str + colored("].", "yellow")) 95 | 96 | self.r2angr.simgr.explore(find=find, avoid=avoid) 97 | 98 | 99 | if self.r2angr.simgr.active: 100 | self.print_explore() 101 | self.r2angr.simgr.unstash(from_stash="found", to_stash="active") 102 | else: 103 | print(colored("Exploration failed", "red")) 104 | 105 | def debug_explore_stdout(self): 106 | print("Exploring until stdout " + self.command[1]) 107 | self.simgr.explore(find=lambda s: self.command[1].strip().encode() in s.posix.dumps(1)).unstash(from_stash="found", to_stash="active") 108 | 109 | def debug_explore_until_dfs(self): 110 | print("Exploring using DFS") 111 | command = self.command 112 | simgr = self.simgr 113 | simgr.use_technique(self.angr.exploration_techniques.dfs.DFS()) 114 | 115 | old_active = [] 116 | old_deadended = [] 117 | 118 | for state in simgr.active: 119 | old_active.append(state) 120 | 121 | for state in simgr.deadended: 122 | old_deadended.append(state) 123 | 124 | if "0x" in command[1]: 125 | addr = int(command[1], 16) 126 | else: 127 | addr = int(self.symbol_to_address(command[1]), 16) 128 | 129 | simgr.use_technique(self.angr.exploration_techniques.explorer.Explorer()) 130 | 131 | print("Debug explore until " + hex(addr)) 132 | old_state = state 133 | simgr.explore(find=addr).unstash(from_stash="found", to_stash="active") 134 | 135 | if simgr.active: 136 | print(colored("Found " + str(len(simgr.active)) + " solutions", "green")) 137 | else: 138 | print(colored("Exploration failed", "red")) 139 | 140 | print("Reverting state (currently a bit buggy)") 141 | 142 | simgr.active = [] 143 | simgr.deadended = [] 144 | 145 | for state in old_active: 146 | simgr.active.append(state) 147 | for state in old_deadended: 148 | simgr.deadended.append(state) 149 | 150 | # Explore until a certain address 151 | def debug_explore_until(self): 152 | command = self.r2angr.command 153 | simgr = self.r2angr.simgr 154 | 155 | 156 | try: 157 | addr = self.get_addr(command[1]) 158 | except: 159 | print(colored(str(command[1]) + " not found", "yellow")) 160 | return 161 | 162 | self.print_debug(colored("Starting exploration. Find: [", "yellow") + colored(hex(addr), "green") + colored("]", "yellow")) 163 | 164 | simgr.explore(find=addr) 165 | 166 | if simgr.found: 167 | self.print_explore() 168 | simgr.unstash(from_stash="found", to_stash="active") 169 | else: 170 | print(colored("Exploration failed", "red")) 171 | 172 | def debug_explore_output(self): 173 | command = self.r2angr.command 174 | simgr = self.r2angr.simgr 175 | 176 | find_string = " ".join(command[1:]) 177 | avoid_string = "basdflksjdf" 178 | 179 | def is_good(state): 180 | return find_string in state.posix.dumps(1).decode() 181 | 182 | def is_bad(state): 183 | return avoid_string in state.posix.dumps(1).decode() 184 | 185 | self.print_debug(colored("Starting exploration. Find: [", "yellow") + colored(find_string, "green") + colored("]", "yellow")) 186 | 187 | simgr.explore(find=is_good) 188 | 189 | if simgr.found: 190 | self.print_explore() 191 | simgr.unstash(from_stash="found", to_stash="active") 192 | else: 193 | print(colored("Exploration failed", "red")) 194 | 195 | 196 | def debug_explore_revert(self): 197 | print("Restoring state") 198 | self.simgr.active = self.active_backup 199 | self.simgr.deadended_backup = self.deadended_backup 200 | 201 | def debug_explore_until_loop(self): 202 | command = self.command 203 | simgr = self.simgr 204 | 205 | self.save_state(simgr) 206 | temp_project = self.angr.Project(self.filename, auto_load_libs=False) 207 | 208 | print("Debug explore until loop") 209 | simgr.explore(find=self.loop_entry_addrs).unstash(from_stash="found", to_stash="active") 210 | 211 | if simgr.active: 212 | print(colored("Found " + str(len(simgr.active)) + " solutions", "green")) 213 | else: 214 | print(colored("Exploration failed", "red")) 215 | 216 | def loop_hook(self, state): 217 | loop = state.loop_data.current_loop 218 | #analysis = self.project.analyses.LoopAnalysis(loop, None) 219 | #print(str(analysis)) 220 | #print(str(state.loop_data.header_trip_counts)) 221 | sys.stdout.write("\b") 222 | sys.stdout.write("=>") 223 | sys.stdout.flush() 224 | 225 | def debug_explore_loop(self): 226 | command = self.command 227 | simgr = self.simgr 228 | 229 | old_active = [] 230 | old_deadended = [] 231 | 232 | for state in simgr.active: 233 | old_active.append(state) 234 | 235 | for state in simgr.deadended: 236 | old_deadended.append(state) 237 | 238 | temp_project = self.angr.Project(self.filename, auto_load_libs=False) 239 | 240 | cfg_fast = temp_project.analyses.CFGFast() 241 | addrs = [] 242 | for f in cfg_fast.functions: 243 | addrs.append(f) 244 | functions = [] 245 | for a in addrs: 246 | functions.append(cfg_fast.functions[a]) 247 | 248 | loops = temp_project.analyses.LoopFinder(functions=functions).loops 249 | 250 | simgr.use_technique(self.angr.exploration_techniques.loop_seer.LoopSeer(cfg=cfg_fast, functions=functions, loops=loops, use_header=False, bound=None, bound_reached=None, discard_stash='deadended')) 251 | #analysis = temp_project.analyses.LoopAnalysis(loop_finder.loops[0], None) 252 | #print(str(analysis)) 253 | self.project.hook(0x4008f4, self.loop_hook, length=0) 254 | 255 | print("Starting loop") 256 | sys.stdout.write(" [=") 257 | sys.stdout.flush() 258 | 259 | simgr.explore(find=0x4008fa).unstash(from_stash="found", to_stash="active") 260 | print("]") 261 | 262 | simgr.use_technique(self.angr.exploration_techniques.explorer.Explorer()) 263 | 264 | exit() 265 | 266 | print("Debug explore until loop") 267 | old_state = state 268 | simgr.explore(find=entries).unstash(from_stash="found", to_stash="active") 269 | 270 | if simgr.active: 271 | print(colored("Found " + str(len(simgr.active)) + " solutions", "green")) 272 | else: 273 | print(colored("Exploration failed", "red")) 274 | 275 | print("Reverting state (currently a bit buggy)") 276 | 277 | simgr.active = [] 278 | simgr.deadended = [] 279 | 280 | for state in old_active: 281 | simgr.active.append(state) 282 | for state in old_deadended: 283 | simgr.deadended.append(state) 284 | 285 | def hook_watchpoint(self, state): 286 | addr = state.solver.eval(state.regs.rip) 287 | hit_count, message = self.watchpoints[addr] 288 | self.watchpoints[addr] = (hit_count + 1, message) 289 | 290 | if message == "": 291 | data = colored(" [" + str(len(self.simgr.active)) + "|" + colored(str(len(self.simgr.deadended)), "red") + colored("]", "yellow"), "yellow"), colored("{Hit count: " + str(hit_count) + "}", "cyan"), " Reached watchpoint at " + hex(addr) 292 | state.history_arr.append(data) 293 | print(data) 294 | else: 295 | data = colored(" [" + str(len(self.simgr.active)) + "|" + colored(str(len(self.simgr.deadended)), "red") + colored("]", "yellow"), "yellow"), colored("{Hit count: " + str(hit_count) + "}", "cyan"), " " + message 296 | state.history_arr.append(data) 297 | print(data) 298 | 299 | def debug_watch(self): 300 | addr = int(self.command[1], 16) 301 | print("Adding watchpoint at " + hex(addr)) 302 | self.project.hook(addr, self.hook_watchpoint, length=0) 303 | 304 | if len(self.command) >= 3: 305 | self.watchpoints[addr] = (0, " ".join(self.command[2:])) 306 | else: 307 | self.watchpoints[addr] = (0, "") 308 | 309 | def hook_mergepoint(self, state): 310 | addr = state.solver.eval(state.regs.rip) 311 | simgr = self.simgr 312 | merge_count = 0 313 | 314 | i = 0 315 | j = len(simgr.active) 316 | while len(simgr.active) > 1 and i < 30: 317 | s_merged, flag, anything_merged = simgr.active[0].merge(simgr.active[1]) 318 | i += 1 319 | if anything_merged: 320 | merge_count += 1 321 | simgr.active.remove(simgr.active[0]) 322 | simgr.active[0] = s_merged 323 | else: 324 | print("Merge failed") 325 | print(colored(" [" + str(len(self.simgr.active)) + "|" + colored(str(len(self.simgr.deadended)), "red") + colored("]", "yellow"), "yellow"), colored("{Merging " + str(merge_count) + " states}", "cyan"), " Merging states at " + hex(addr)) 326 | 327 | def debug_merge(self): 328 | addr = int(self.command[1], 16) 329 | print("Adding mergepoint at " + hex(addr)) 330 | self.project.hook(addr, self.hook_mergepoint, length=0) 331 | 332 | def find(self, state): 333 | return self.find_string in state.posix.dumps(1) 334 | 335 | def avoid(self, state): 336 | return self.avoid_string in state.posix.dumps(1) 337 | 338 | def debug_explore_until_stdout(self): 339 | command = self.command 340 | simgr = self.simgr 341 | self.find_string = command[1].encode() 342 | self.avoid_string = command[2].encode() 343 | simgr.explore(find=self.find, avoid=self.avoid) 344 | 345 | if simgr.active: 346 | print("Found " + str(len(simgr.active)) + " solutions") 347 | else: 348 | print("Exploration failed") 349 | 350 | 351 | 352 | def debug_continue_until(self): 353 | simgr = self.r2angr.simgr 354 | 355 | try: 356 | addr = get_addr(self.r2angr.command[1]) 357 | print(str(addr)) 358 | except: 359 | print(colored("Usage: mcu ", "yellow")) 360 | return 361 | 362 | self.print_debug("Continuing until " + hex(addr)) 363 | 364 | while len(simgr.active) > 0 and simgr.active[0].addr != addr: 365 | simgr.step() 366 | 367 | # Continue emulation until new data in stdout 368 | def debug_continue_output(self): 369 | self.print_debug("Debug continue until output") 370 | output = self.r2angr.simgr.active[0].posix.dumps(1) 371 | output = [] 372 | 373 | for state in self.r2angr.simgr.active: 374 | output.append(state.posix.dumps(1)) 375 | 376 | stdout = "" 377 | cont = True 378 | while cont: 379 | self.r2angr.simgr.step() 380 | for i in range(0, len(output)): 381 | if output[i] != self.r2angr.simgr.active[i].posix.dumps(1): 382 | cont = False 383 | stdout = self.r2angr.simgr.active[i].posix.dumps(1) 384 | 385 | try: 386 | print(stdout.decode()) 387 | except: 388 | print(str(stdout)) 389 | 390 | # Continue emulation until a branch 391 | def debug_continue_until_branch(self): 392 | print("Continuing until branch") 393 | 394 | current = len(self.r2angr.simgr.active) 395 | while len(self.r2angr.simgr.active) <= current and len(self.r2angr.simgr.active) > 0: 396 | self.r2angr.simgr.step() 397 | 398 | def debug_continue_until_ret(self): 399 | print("Debug continue until ret") 400 | self.simgr.run() 401 | 402 | def debug_continue_until_call(self): 403 | print("Debug continue until call") 404 | self.simgr.run() 405 | 406 | def get_addr(self, s): 407 | if self.r2angr.r2p.cmd("afl") != "": 408 | functions = self.r2angr.r2p.cmdj("aflj") 409 | else: 410 | functions = None 411 | 412 | if functions != None: 413 | for f in functions: 414 | if f["name"] == s: 415 | return f["offset"] 416 | 417 | if "0x" in str(s): 418 | return int(s, 16) 419 | else: 420 | return int(s) 421 | 422 | def print_explore(self): 423 | self.print_debug(colored("Found " + str(len(self.r2angr.simgr.found)) + " solutions", "green")) 424 | 425 | def print_debug(self, s): 426 | print(colored("[", "yellow") + colored("DEBUG", "blue") + colored("] ", "yellow") + colored(s, "yellow")) 427 | -------------------------------------------------------------------------------- /src/disass.py: -------------------------------------------------------------------------------- 1 | import r2pipe 2 | #from tabulate import * 3 | 4 | class Disassembler(): 5 | def __init__(self, filename): 6 | r = r2pipe.open(filename) 7 | r.cmd("aa") 8 | 9 | temp_addrs = r.cmd("afll~[0]").split("\n") 10 | temp_names = r.cmd("afll~[14]").split("\n") 11 | 12 | self.functions = [] 13 | 14 | for i in range(0, len(temp_addrs)): 15 | if "xref" not in temp_names[i] and temp_addrs[i] != "" and "imp" not in temp_names[i]: 16 | self.functions.append((hex(int(temp_addrs[i], 16)), temp_names[i])) 17 | 18 | self.library_functions = [] 19 | 20 | for i in range(0, len(temp_addrs)): 21 | if "xref" not in temp_names[i] and temp_addrs[i] != "" and "imp" in temp_names[i]: 22 | self.library_functions.append((hex(int(temp_addrs[i], 16)), temp_names[i])) 23 | 24 | self.r = r 25 | 26 | def disassemble(self): 27 | command = self.command 28 | simgr = self.simgr 29 | r = self.r 30 | output = [] 31 | num = 10 32 | if len(command) > 1: 33 | num = int(command[1]) 34 | for s in simgr.active: 35 | output.append(r.cmd("pi " + str(num) + " @ " + hex(s.addr))) 36 | print(tabulate([output])) 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/exploit.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import sys 3 | import claripy 4 | from termcolor import colored 5 | import logging 6 | #import binascii 7 | import struct 8 | from subprocess import Popen, PIPE, STDOUT 9 | import os 10 | #from one_gadget import generate_one_gadget 11 | #import angrop 12 | 13 | if not hasattr(sys, "argv"): 14 | sys.argv = [''] 15 | 16 | #from pwn import * 17 | 18 | #from src.concrete.target import R2ConcreteTarget 19 | 20 | class Exploiter(): 21 | payload = b'DEFAULT_STDIN' 22 | def __init__(self): 23 | pass 24 | 25 | def test_run(self): 26 | p = Popen([os.getcwd() + "/" + self.r2angr.binary], stdout=PIPE, stdin=PIPE, stderr=PIPE) 27 | stdout_data = p.communicate(input=self.payload)[0] 28 | try: 29 | print(str(stdout_data.decode())) 30 | except: 31 | print(str(stdout_data)) 32 | 33 | def test_debug(self): 34 | with open(".temp.rr2", "w+") as f: 35 | f.write("#!/usr/bin/rarun2\nprogram=binary\nstdin=.buffer.txt\n") 36 | with open(".buffer.txt", "wb+") as f: 37 | f.write(self.payload + "\n".encode()) 38 | 39 | self.r2angr.r2p.cmd("e dbg.profile=temp.rr2") 40 | self.r2angr.r2p.cmd("ood") 41 | 42 | def test2(self): 43 | project = self.r2angr.angr.Project(self.r2angr.binary) 44 | print(str(self.payload)) 45 | state = project.factory.entry_state(args=[], stdin=self.payload) 46 | def print_return(state): 47 | print(" return") 48 | state.inspect.b("return", when=angr.BP_AFTER, action=print_return) 49 | 50 | simgr = project.factory.simgr(state) 51 | 52 | self.r2angr.r2p.cmd("aa") 53 | 54 | for func in self.r2angr.r2p.cmdj("aflj"): 55 | if ".imp." in func["name"]: 56 | project.hook(func["offset"], self.library_function_hook) 57 | else: 58 | project.hook(func["offset"], self.function_hook) 59 | 60 | while len(simgr.active) > 0: 61 | output = simgr.active[0].posix.dumps(1) 62 | if len(output) > 3: 63 | print(output) 64 | simgr.step() 65 | 66 | print("Done testing") 67 | 68 | def explore(self): 69 | print("Exploring for vulnerable program state") 70 | #p = angr.Project("basic", load_options={"auto_load_libs": False}) 71 | p = self.r2angr.project 72 | simgr = self.r2angr.simgr 73 | 74 | p.hook_symbol("printf", hook_printf()) 75 | p.hook_symbol("__isoc99_scanf", hook_scanf()) 76 | 77 | for state in simgr.active: 78 | pass 79 | #state.inspect.b("mem_write", when=angr.BP_AFTER, action=self.breakpoint_mem_write) 80 | 81 | #stdin = claripy.BVS("stdin", 300*8) 82 | #state = p.factory.entry_state(stdin=stdin); 83 | #simgr = p.factory.simgr(state, save_unconstrained=True); 84 | 85 | print("Current number of active states is: " + str(len(simgr.active))) 86 | while len(simgr.active) > 0: 87 | #print(str(simgr) + " " + str(simgr.active[0].regs.rip)) 88 | simgr.step() 89 | 90 | if len(simgr.unconstrained) > 0: 91 | print("Found unconstrained state") 92 | 93 | for s in simgr.unconstrained: 94 | if self.fully_symbolic(s, s.regs.pc): 95 | print("Reached fully symbolic pc") 96 | #break 97 | 98 | state_copy = s.copy() 99 | constraints = [] 100 | for i in range(int(state_copy.arch.bits/8)): 101 | constraints.append(claripy.And(state_copy.regs.pc.get_byte(i) == 0x42)) 102 | 103 | if state_copy.solver.satisfiable(extra_constraints=constraints): 104 | print("Can overwrite pc") 105 | for constraint in constraints: 106 | state_copy.add_constraints(constraint) 107 | 108 | logging.getLogger("claripy.ast.bv").disabled = True 109 | 110 | skipped = 0 111 | count = 0 112 | for byte in self.r2angr.stdin.chop(8): 113 | if state_copy.solver.satisfiable(extra_constraints=[byte == "\x41"]): 114 | state_copy.add_constraints(byte == "A") 115 | print(colored("[", "yellow") + colored("R2ANGR", "green") + colored("] ", "yellow") + colored("Constraining stdin bytes. Constrained: ", "yellow") + str(count) + colored(", Skipped: ", "yellow") + str(skipped), end="\r") 116 | count += 1 117 | else: 118 | skipped += 1 119 | 120 | print(colored("[", "yellow") + colored("R2ANGR", "green") + colored("] ", "yellow") + colored("Constraining stdin bytes. Constrained: ", "yellow") + str(count) + colored(", Skipped: ", "yellow") + str(skipped)) 121 | 122 | logging.getLogger("claripy.ast.bv").disabled = False 123 | 124 | stdin_shellcode = state_copy.posix.dumps(0) 125 | 126 | self.base_shellcode = stdin_shellcode 127 | 128 | #stdin_shellcode = stdin_shellcode[0:stdin_shellcode.rfind(b'\x42')+1] 129 | 130 | self.print_line() 131 | 132 | self.print_shellcode(stdin_shellcode) 133 | 134 | self.make_payload_auto(s) 135 | 136 | simgr.unconstrained.remove(s) 137 | 138 | simgr.drop(stash="unconstrained") 139 | 140 | # ============================================================================== 141 | 142 | def make_payload_auto(self, state): 143 | # Check if each method is possible, print out valid ones 144 | print("Weaponizing shellcode") 145 | r = self.r2angr.r2p 146 | 147 | nx = r.cmdj("ij")["bin"]["nx"] 148 | pic = r.cmdj("ij")["bin"]["pic"] 149 | canary = r.cmdj("ij")["bin"]["canary"] 150 | 151 | self.make_payload_shellcode(state) 152 | self.make_payload_ret2libc(state) 153 | self.make_payload_ret2plt(state) 154 | 155 | return self.make_payload_ret2plt(state) 156 | #return self.make_payload_one_gadget(state) 157 | 158 | def make_payload_shellcode(self, state): 159 | self.print_line() 160 | print(colored("[", "yellow") + colored("R2ANGR", "green") + colored("] ", "yellow") + colored("Building payload: ", "yellow") + colored("Jump to shellcode", "green")) 161 | 162 | if self.r2angr.r2p.cmdj("ij")["bin"]["nx"]: 163 | print(colored("Failed: ", "red") + "nx enabled") 164 | return 165 | 166 | payload = self.base_shellcode 167 | shellcode = "\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05" 168 | 169 | max_length = payload.rfind(b'\x42'*8)+12 170 | 171 | print(colored("Failed: ", "red") + "Couldn't find buffer") 172 | return 173 | 174 | for buf_addr in self.find_symbolic_buffer(state, len(shellcode)): 175 | #print("Found symbolic buffer at " + hex(buf_addr)) 176 | memory = state.memory.load(buf_addr, len(shellcode)) 177 | sc_bvv = state.solver.BVV(shellcode) 178 | 179 | if state.satisfiable(extra_constraints=(memory == sc_bvv, ep.regs.pc == buf_addr)): 180 | #print("Found buffer for shellcode, finishing exploit") 181 | state.add_constraints(memory == sc_bvv) 182 | state.add_constraints(state.regs.pc == buf_addr) 183 | break 184 | 185 | 186 | payload = payload[:max_length] 187 | self.payload = payload 188 | self.print_shellcode(payload) 189 | 190 | return payload 191 | 192 | def make_payload_ret2libc(self, state): 193 | self.print_line() 194 | self.r2angr.r2p.cmd("e search.in = dbg.maps") 195 | self.r2angr.r2p.cmd("ood") 196 | self.r2angr.r2p.cmd("dcu entry0") 197 | system_addr = self.r2angr.r2p.cmdj("dmij libc system")[0]["vaddr"] 198 | binsh_addr = self.r2angr.r2p.cmdj("/j /bin/sh")[0]["offset"] 199 | self.r2angr.r2p.cmd("doc") 200 | 201 | print("Found system() at " + hex(system_addr)) 202 | print("Found \"/bin/sh\" at " + hex(binsh_addr)) 203 | 204 | shellcode = self.base_shellcode[0:self.base_shellcode.rfind(b'\x42')+1] 205 | 206 | if state.arch.bits == 32: 207 | print("Constructing 32 bit shellcode") 208 | shellcode = shellcode[:-4] + struct.pack("> ", end="") 296 | 297 | addr = struct.pack(" (seek to index), mss+ (seek to next state) mss- (seek to previous state) 28 | # Can declare an exploration sequence (find1, find2, find3, find4, etc). Use mes to explore this list of comments. 29 | # Add keys to visual mode to seek between symbolic states 30 | # Continue commands seek to addresses during emulation 31 | # Use lambdas to add state comments/highlights to in radare2 during exploration 32 | # Command to quit exploration 33 | # Command to quit continuation 34 | 35 | # Commands to add/remove find/avoid. Commands to remove all. Commands to list find/avoid. 36 | # Get rid of classes, import all of them into sessions class 37 | # Find/avoid commands (add comments at address). Work with @. 38 | # Make find/avoid commands recolor block (can disable this in config). 39 | # Make debugger comment/recolor/highlight at state location 40 | 41 | class R2ANGR(): 42 | is_initialized = False 43 | stdin = "" 44 | argv = [] 45 | command = "" 46 | 47 | debugger = Debugger() 48 | stash = Stash() 49 | watcher = Watcher() 50 | hooks = Hooks() 51 | initializer = Initializer() 52 | bitvectors = Bitvectors() 53 | exploiter = Exploiter() 54 | 55 | return_value = "" 56 | 57 | commands = [ 58 | ("c", debugger.debug_continue, "c" + colored("[?] ", "yellow") + colored("Continue emulation", "green")), 59 | ("e", debugger.debug_explore, "e" + colored("[?] ", "yellow") + colored("Explore using find/avoid comments", "green")), 60 | ("i", None, "i" + colored("[?] ", "yellow") + colored("Initialize at entry point", "green")), 61 | ("ie", initializer.initialize_entry, "ie" + colored("[?] ", "yellow") + colored("Initialize at entry point", "green")), 62 | ("ib", initializer.initialize_blank, "ib" + colored("[?] ", "yellow") + colored("Initialize blank state at current address", "green")), 63 | ("id", initializer.initialize_debugger, "id" + colored("[?] ", "yellow") + colored("Create angr state from current debugger state", "green")), 64 | ("s", stash.list, "s" + colored("[?] ", "yellow") + colored("States list", "green")), 65 | 66 | ("h", None, "h" + colored("[?] ", "yellow") + colored("Hooks", "green")), 67 | ("hf", hooks.hook_functions, "hf" + colored(" ", "yellow") + colored("Hook all functions", "green")), 68 | ("hl", hooks.hook_loops, "hl" + colored(" ", "yellow") + colored("Hook all loops", "green")), 69 | 70 | ("cs", debugger.debug_step, "cs" + colored(" ", "yellow") + colored("Continue emulation one step", "green")), 71 | ("cu", debugger.debug_continue_until, "cu" + colored(" ", "yellow") + colored("Continue emulation until address", "green")), 72 | ("cb", debugger.debug_continue_until_branch, "cb " + colored("Continue emulation until branch", "green")), 73 | ("co", debugger.debug_continue_output, "co " + colored("Continue emulation until output", "green")), 74 | 75 | ("eu", debugger.debug_explore_until, "eu" + colored(" ", "yellow") + colored("Explore until address", "green")), 76 | ("eo", debugger.debug_explore_output, "eo" + colored(" ", "yellow") + colored("Explore until string is in stdout", "green")), 77 | 78 | ("sl", stash.list, "sl"+colored(" ", "yellow") + colored("List states", "green")), 79 | ("slv", stash.info, "slv"+colored(" ", "yellow") + colored("List active state verbose", "green")), 80 | ("si", stash.print_input, "si"+colored(" ", "yellow") + colored("Print state stdin", "green")), 81 | ("so", stash.print_output, "so"+colored(" ", "yellow") + colored("Print state stdout", "green")), 82 | ("sk", stash.kill, "sk" + colored("[?] ", "yellow") + colored("Kill state by index or address", "green")), 83 | ("ska", stash.kill_all, "ska"+colored(" ", "yellow") + colored("Kill all states", "green")), 84 | ("sr", stash.revive, "sr" + colored("[?] ", "yellow") + colored("Revive state by index or address", "green")), 85 | ("sra", stash.revive_all, "sra"+colored(" ", "yellow") + colored("Revive all states", "green")), 86 | ("ss", None, "ss" + colored("[?] ", "yellow") + colored("", "green")), 87 | ("br", bitvectors.symbolize_register, "br" + colored(" ", "yellow") + colored("Symbolize register with a bitvector", "green")), 88 | ("bs", bitvectors.solve, "bs" + colored(" ", "yellow") + colored("Solve all bitvector values", "green")), 89 | ("se", stash.extract, "se"+colored(" ", "yellow") + colored("Extract single state and kill all others", "green")), 90 | 91 | ("w", watcher.add_watchpoint, "w"+colored("[?] ", "yellow") + colored("Add a watchpoint", "green")), 92 | ("wl", watcher.list_watchpoints, "wl"+colored(" ", "yellow") + colored("List watchpoint", "green")), 93 | ("wr", watcher.remove_watchpoint, "wr"+colored(" ", "yellow") + colored("Remove watchpoint", "green")), 94 | ("E", exploiter.explore, "E"+colored(" ", "yellow") + colored("Run the exploit main command", "green")), 95 | ] 96 | 97 | def load_angr(self): 98 | print(colored("[", "yellow") + colored("R2ANGR", "green") + colored("] ", "yellow") + colored("Importing angr", "yellow")) 99 | import angr 100 | import claripy 101 | self.angr = angr 102 | print(colored("[", "yellow") + colored("R2ANGR", "green") + colored("] ", "yellow") + colored("Loading r2angr", "yellow")) 103 | self.is_initialized = True 104 | 105 | if False: 106 | self.concrete_target = R2ConcreteTarget(self.r2p) 107 | self.project = angr.Project(self.binary, concrete_target = self.concrete_target) 108 | self.fast_project = angr.Project(self.binary, concrete_target=self.concrete_target, auto_load_libs=False) 109 | else: 110 | self.project = angr.Project(self.binary) 111 | self.fast_project = angr.Project(self.binary, auto_load_libs=False) 112 | 113 | self.initialize_entry() 114 | 115 | def initialize_entry(self): 116 | state = self.project.factory.entry_state(args=self.argv, stdin=self.stdin) 117 | 118 | #state.options.add(self.angr.options.SYMBION_SYNC_CLE) 119 | #state.options.add(self.angr.options.SYMBION_KEEP_STUBS_ON_SYNC) 120 | 121 | self.simgr = self.project.factory.simgr(state, save_unconstrained=True) 122 | 123 | try: 124 | self.r2p.cmd("s " + hex(state.solver.eval(state.regs.rip))) 125 | except: 126 | self.r2p.cmd("s " + hex(state.solver.eval(state.regs.eip))) 127 | 128 | print(colored("[", "yellow") + colored("R2ANGR", "green") + colored("] ", "yellow") + colored("Initialized r2angr at entry point", "yellow")) 129 | 130 | def __init__(self, binary, r2p): 131 | self.stdin = claripy.BVS("stdin", 150*8) 132 | self.binary = binary 133 | self.r2p = r2p 134 | 135 | def run(self, command): 136 | command = command.split(" ") 137 | 138 | if "i" == command[0] and False: 139 | self.initialize_entry() 140 | self.is_initialized = True 141 | return 142 | 143 | self.command = command 144 | self.debugger.r2angr = self 145 | self.stash.r2angr = self 146 | self.watcher.r2angr = self 147 | self.hooks.r2angr = self 148 | self.initializer.r2angr = self 149 | self.bitvectors.r2angr = self 150 | self.exploiter.r2angr = self 151 | 152 | found = False 153 | for c, f, h in self.commands: 154 | if c == command[0]: 155 | if not self.is_initialized: 156 | print("r2angr not initialized, use the mi command") 157 | else: 158 | self.return_value = "" 159 | f() 160 | if not c == "s" and not c == "sl": 161 | self.update_highlight() 162 | found = True 163 | 164 | if not found or "?" in command: 165 | self.help(command) 166 | 167 | def help(self, command): 168 | self.return_value = "" 169 | command[0] = command[0].replace("?", "") 170 | print("Getting help") 171 | for c, f, h in self.commands: 172 | if command[0] in c.split(" ")[0].replace("?", "") and len(c) - len(command[0]) < 2: 173 | print("| M" + h) 174 | 175 | def update_highlight(self): 176 | for comment in self.r2p.cmdj("CCj"): 177 | if "r2angr" in comment["name"]: 178 | self.r2p.cmd("CC- @ " + hex(comment["offset"])) 179 | self.r2p.cmd("ecH- @ " + hex(comment["offset"])) 180 | 181 | i = 0 182 | for state in self.simgr.deadended: 183 | self.r2p.cmd("ecHi red @ " + hex(state.addr)) 184 | self.r2p.cmd("CC- @ " + hex(state.addr)) 185 | self.r2p.cmd("CC+r2angr \"deadended\" state " + str(i) + " @ " + hex(state.addr)) 186 | if not "invalid" in self.r2p.cmd("pd 2 @ " + hex(state.addr)): 187 | self.r2p.cmd("s " + hex(state.addr)) 188 | i += 1 189 | 190 | i = 0 191 | for state in self.simgr.active: 192 | self.r2p.cmd("ecHi blue @ " + hex(state.addr)) 193 | self.r2p.cmd("CC- @ " + hex(state.addr)) 194 | self.r2p.cmd("CC+r2angr \"active\" state " + str(i) + " @ " + hex(state.addr)) 195 | if not "invalid" in self.r2p.cmd("pd 2 @ " + hex(state.addr)): 196 | self.r2p.cmd("s " + hex(state.addr)) 197 | i += 1 198 | 199 | for addr in self.watcher.watchpoints: 200 | count, name = self.watcher.watchpoints[addr] 201 | self.r2p.cmd("ecHi magenta @ " + hex(addr)) 202 | self.r2p.cmd("CC- @ " + hex(addr)) 203 | 204 | if count > 0: 205 | self.r2p.cmd("CC+Watchpoint " + name + " @ " + hex(addr)) 206 | else: 207 | self.r2p.cmd("CC+Watchpoint " + name + "(Hits: " + str(count) + ") @ " + hex(addr)) 208 | 209 | -------------------------------------------------------------------------------- /src/r2angrdbg.py: -------------------------------------------------------------------------------- 1 | import r2pipe 2 | import base64 3 | import os 4 | import struct 5 | from angrdbg import * 6 | 7 | class R2Debugger(Debugger): 8 | def __init__(self, r2): 9 | self.r2 = r2 10 | self.base_addr = None 11 | 12 | def _get_vmmap(self): 13 | dm = self.r2.cmdj("dmj") 14 | maps = [] 15 | for s in dm: 16 | start = s["addr"] 17 | end = s["addr_end"] 18 | mapperm = 0 19 | if "r" in s["perm"]: 20 | mapperm |= SEG_PROT_R 21 | if "w" in s["perm"]: 22 | mapperm |= SEG_PROT_W 23 | if "x" in s["perm"]: 24 | mapperm |= SEG_PROT_X 25 | maps += [(start, end, mapperm, s["name"])] 26 | return maps 27 | 28 | # ------------------------------------- 29 | def before_stateshot(self): 30 | self.vmmap = self._get_vmmap() 31 | 32 | for sec in self.r2.cmdj("iSj"): 33 | if sec["name"] == load_project().arch.got_section_name: 34 | self.got = (sec["vaddr"], sec["vaddr"] + sec["vsize"]) 35 | elif sec["name"] == ".plt": 36 | self.plt = (sec["vaddr"], sec["vaddr"] + sec["vsize"]) 37 | 38 | def after_stateshot(self, state): 39 | pass 40 | # ------------------------------------- 41 | 42 | def is_active(self): 43 | return self.r2.cmd("dm") != "" 44 | 45 | # ------------------------------------- 46 | def input_file(self): 47 | # path = self.r2.cmdj("dmj")[0]["file"] 48 | path = self.r2.cmdj("ij")['core']['file'] 49 | return open(path, "rb") 50 | 51 | def image_base(self): 52 | if self.base_addr is None: 53 | self.base_addr = int(self.r2.cmd("e bin.baddr"), 16) 54 | return self.base_addr 55 | 56 | # ------------------------------------- 57 | def get_byte(self, addr): 58 | try: 59 | return ord(base64.b64decode(self.r2.cmd("p6e 1 @ %d" % addr))) 60 | except BaseException as e: 61 | print(e) 62 | return None 63 | 64 | def get_word(self, addr): 65 | try: 66 | return struct.unpack( 67 | "= start and addr < end: 147 | return Segment(name, start, end, perms) 148 | return None 149 | 150 | def get_got(self): # return tuple(start_addr, end_addr) 151 | return self.got 152 | 153 | def get_plt(self): # return tuple(start_addr, end_addr) 154 | return self.plt 155 | 156 | # ------------------------------------- 157 | def resolve_name(self, name): # return None on fail 158 | print("resovle name") 159 | try: 160 | modules = self.r2.cmdj("dmmj") 161 | for m in modules[1:]: 162 | addr = m["address"] 163 | lib = os.path.basename(m["name"]).split(".")[0].split("-")[0] 164 | o = self.r2.cmd("dmi* %s %s" % (lib, name)) 165 | for line in o.split("\n"): 166 | line = line.split() 167 | if len(line) < 4: 168 | continue 169 | if line[1] == name or line[3] == "sym."+name: 170 | return int(line[3], 16) 171 | except: 172 | pass 173 | return None 174 | 175 | 176 | def init(r2): 177 | register_debugger(R2Debugger(r2)) 178 | -------------------------------------------------------------------------------- /src/stash.py: -------------------------------------------------------------------------------- 1 | from termcolor import colored 2 | import claripy 3 | 4 | # Chage commands to kill/revive state by numbers rather than addresses 5 | 6 | # ========== Commands ========== # 7 | class Stash(): 8 | def info(self): 9 | i = 0 10 | for state in self.r2angr.simgr.active: 11 | self.print_return(colored("Active", "cyan") + " state " + str(i) + " at " + colored(hex(state.addr), "green")) 12 | s = self.r2angr.r2p.cmd("pdi 5 @ " + hex(state.addr)) 13 | for line in s.split("\n"): 14 | self.print_return(" " + line) 15 | i += 1 16 | 17 | def list(self): 18 | table = [] 19 | if len(self.r2angr.simgr.active) > 0: 20 | self.print_return(colored("Active", "cyan") + " states:") 21 | for i in range(0, len(self.r2angr.simgr.active)): 22 | self.print_return(" " + str(i) + " " + colored(hex(self.r2angr.simgr.active[i].addr), "yellow")) 23 | self.print_return("") 24 | 25 | if len(self.r2angr.simgr.deadended) > 0: 26 | self.print_return(colored("Deadended", "red") + " states:") 27 | for i in range(0, len(self.r2angr.simgr.deadended)): 28 | self.print_return(" " + str(i) + " " + colored(hex(self.r2angr.simgr.deadended[i].addr), "yellow")) 29 | self.print_return("") 30 | 31 | def print_input(self): 32 | command = self.r2angr.command 33 | simgr = self.r2angr.simgr 34 | addr = 0 35 | 36 | if len(command) == 1: 37 | i = 0 38 | for state in self.r2angr.simgr.active: 39 | print(colored("Active", "cyan") + " state " + str(i) + " at " + colored(hex(state.addr), "green") + ":") 40 | try: 41 | print(state.posix.dumps(0).decode()) 42 | except: 43 | print(state.posix.dumps(0)) 44 | i += 1 45 | elif "0x" in command[1]: 46 | for state in simgr.active: 47 | if state.addr == addr: 48 | self.print_decode(state.posix.dumps(0)) 49 | else: 50 | self.print_decode(simgr.active[int(command[1])].posix.dumps(0)) 51 | 52 | def print_output(self): 53 | command = self.r2angr.command 54 | simgr = self.r2angr.simgr 55 | addr = 0 56 | 57 | if len(command) == 1: 58 | i = 0 59 | for state in simgr.active: 60 | self.print_return(colored("Active", "cyan") + " state " + str(i) + " at " + colored(hex(state.addr), "green") + ":") 61 | self.print_decode(state.posix.dumps(1)) 62 | i += 1 63 | elif "0x" in command[1]: 64 | for state in simgr.active: 65 | if state.addr == addr: 66 | self.print_decode(state.posix.dumps(1)) 67 | else: 68 | self.print_decode(simgr.active[int(command[1])].posix.dumps(1)) 69 | 70 | def kill(self): 71 | command = self.r2angr.command 72 | simgr = self.r2angr.simgr 73 | addr = 0 74 | 75 | if "0x" in command[1]: 76 | addr = int(command[1], 16) 77 | else: 78 | addr = simgr.active[int(command[1])].addr 79 | 80 | simgr.move(from_stash='active', to_stash='deadended', filter_func=lambda s: s.addr == addr) 81 | 82 | def extract(self): 83 | simgr = self.r2angr.simgr 84 | command = self.r2angr.command 85 | addr = 0 86 | 87 | if "0x" in command[1]: 88 | addr = int(command[1], 16) 89 | else: 90 | addr = simgr.active[int(command[1])].addr 91 | simgr.move(from_stash='active', to_stash='deadended', filter_func=lambda s: s.addr != addr) 92 | 93 | def seek(self): 94 | command = self.r2angr.command 95 | simgr = self.r2angr.simgr 96 | if "0x" in command[1]: 97 | addr = int(command[1], 16) 98 | simgr.move(from_stash='active', to_stash='deadended', filter_func=lambda s: s.addr != addr) 99 | self.r2angr.r2p.cmd("s " + command[1]) 100 | else: 101 | num = int(command[1]) 102 | temp = simgr.active[num] 103 | self.r2angr.r2p.cmd("s " + hex(temp.addr)) 104 | 105 | def revive(self): 106 | command = self.r2angr.command 107 | simgr = self.r2angr.simgr 108 | addr = 0 109 | 110 | if "0x" in command[1]: 111 | addr = int(command[1], 16) 112 | else: 113 | addr = simgr.deadended[int(command[1])].addr 114 | 115 | simgr.move(from_stash='deadended', to_stash='active', filter_func=lambda s: s.addr == addr) 116 | 117 | def revive_all(self): 118 | simgr = self.r2angr.simgr 119 | simgr.move(from_stash='deadended', to_stash='active', filter_func=lambda s: True) 120 | 121 | def revive_stdout(main, command, simgr): 122 | if len(command) > 1: 123 | i = 0 124 | while i < len(simgr.deadended): 125 | state = simgr.deadended[i] 126 | if command[1].encode() in state.posix.dumps(1): 127 | simgr.active.append(state) 128 | simgr.deadended.remove(state) 129 | i -= 1 130 | i += 1 131 | 132 | def kill_stdout(main, command, simgr): 133 | if len(command) > 1: 134 | i = 0 135 | while i < len(simgr.active): 136 | state = simgr.active[i] 137 | if command[1].encode() in state.posix.dumps(1): 138 | simgr.deadended.append(state) 139 | simgr.active.remove(state) 140 | i -= 1 141 | i += 1 142 | 143 | def kill_all(self): 144 | simgr = self.r2angr.simgr 145 | simgr.move(from_stash='active', to_stash='deadended', filter_func=lambda s: True) 146 | 147 | # ========== Utilities ========== # 148 | 149 | def get_name(state): 150 | if hasattr(state, "name"): 151 | return state.name 152 | else: 153 | return hex(state.addr) 154 | 155 | def print_decode(self, data): 156 | try: 157 | self.print_return(data.decode()) 158 | except: 159 | self.print_return(str(data)) 160 | 161 | def print_return(self, s): 162 | self.r2angr.return_value += s + "\n" 163 | -------------------------------------------------------------------------------- /src/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def clear(): 4 | os.system("clear") 5 | 6 | -------------------------------------------------------------------------------- /src/watcher.py: -------------------------------------------------------------------------------- 1 | from termcolor import colored 2 | 3 | class Watcher(): 4 | watchpoints = {} 5 | 6 | def hook_watchpoint(self, state): 7 | addr = state.solver.eval(state.regs.rip) 8 | hit_count, message = self.watchpoints[addr] 9 | self.watchpoints[addr] = (hit_count + 1, message) 10 | 11 | if message == "": 12 | data = colored(" [" + str(len(self.r2angr.simgr.active)) + "|" + colored(str(len(self.r2angr.simgr.deadended)), "red") + colored("]", "yellow"), "yellow") + " " + colored("{Hits: " + str(hit_count) + "}", "cyan") + " Hit watchpoint at " + hex(addr) 13 | print(data) 14 | else: 15 | data = colored(" [" + str(len(self.r2angr.simgr.active)) + "|" + colored(str(len(self.r2angr.simgr.deadended)), "red") + colored("]", "yellow"), "yellow") + " " + colored("{Hit count: " + str(hit_count) + "}", "cyan") + " " + message 16 | print(data) 17 | 18 | def add_watchpoint(self): 19 | addr = int(self.r2angr.command[1], 16) 20 | print("Adding watchpoint at " + hex(addr)) 21 | self.r2angr.project.hook(addr, self.hook_watchpoint, length=0) 22 | 23 | if len(self.r2angr.command) >= 3: 24 | self.watchpoints[addr] = (0, " ".join(self.r2angr.command[2:])) 25 | else: 26 | self.watchpoints[addr] = (0, "") 27 | 28 | def list_watchpoints(self): 29 | i = 0 30 | print("Listing " + colored("watchpoints", "magenta") + ":") 31 | for addr in self.watchpoints: 32 | print(" " + str(i) + " " + hex(addr)) 33 | i += 1 34 | 35 | def remove_watchpoint(self): 36 | print("Unimplemented") 37 | 38 | --------------------------------------------------------------------------------