├── LICENSE ├── Makefile ├── README ├── analyze.py ├── binaries └── elf-Linux-x86 ├── flatten.py ├── gadgets.py ├── gracoparser.py ├── grammar.py ├── grammar ├── Makefile ├── crop.graco └── parser.py ├── libcapstone.3.dylib ├── libcapstone.a ├── libcapstone.dylib ├── main.py ├── primitives.py ├── process.py ├── ropcompile.py ├── ropgadget ├── __init__.py ├── __init__.pyc ├── args.py ├── args.pyc ├── binary.py ├── binary.pyc ├── core.py ├── core.pyc ├── gadgets.py ├── gadgets.pyc ├── loaders │ ├── __init__.py │ ├── __init__.pyc │ ├── elf.py │ ├── elf.pyc │ ├── macho.py │ ├── macho.pyc │ ├── pe.py │ ├── pe.pyc │ ├── raw.py │ ├── raw.pyc │ ├── universal.py │ └── universal.pyc ├── options.py ├── options.pyc ├── rgutils.py ├── rgutils.pyc ├── ropchain │ ├── __init__.py │ ├── __init__.pyc │ ├── arch │ │ ├── __init__.py │ │ ├── __init__.pyc │ │ ├── ropmaker.py │ │ ├── ropmakercfi.py │ │ ├── ropmakerx64.py │ │ ├── ropmakerx64.pyc │ │ ├── ropmakerx86.py │ │ └── ropmakerx86.pyc │ ├── ropmaker.py │ └── ropmaker.pyc ├── updateAlert.py ├── updateAlert.pyc ├── version.py └── version.pyc ├── samples ├── sample.rop └── sample1.rop ├── symbols.py ├── tests.py ├── tests └── tokenizer │ ├── SIMPLE-0.out │ └── SIMPLE-0.test ├── tokenize.py └── validate.py /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Justin Brower 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | grammar: 2 | cd grammar && make 3 | debug: grammar 4 | python main.py samples/sample.rop binaries/Elf-Linux-x86 5 | all: grammar 6 | python main.py samples/sample.rop binaries/Elf-Linux-x86 --verbose 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | a simple compiler for ROP gadgets. Will be optimized for CFI-based attacks. 2 | For research @ Brown University. 3 | -------------------------------------------------------------------------------- /analyze.py: -------------------------------------------------------------------------------- 1 | from symbols import * 2 | from copy import deepcopy 3 | 4 | def action_references_symbol(action, sym, DEBUG=False): 5 | if action["type"] == "action" and action["action"] == "bind": 6 | # If it's a bind, return true if the rval is used. 7 | # 8 | # This is technically more complicated, since a reassignment might detach the variable. 9 | # But whatever 10 | return action["sym"]["val"] == sym["val"] or action_references_symbol(action["rvalue"], sym) 11 | elif action["type"] == "sym": 12 | # If it's a symbol, return true if correct symbol. 13 | return action["val"] == sym["val"] 14 | elif action["type"] == "action" and action["action"] == "apply": 15 | # If it's an apply, check the function symbol + all args. 16 | return action["sym"]["val"] == sym["val"] or (action["args"] and reduce(lambda x,y: x or y, map(lambda a: action_references_symbol(a, sym), action["args"]))) 17 | else: 18 | # Could be an immediate constant, or something else. 19 | return False 20 | 21 | def action_replace_symbol(action, sym, alternateSym, DEBUG=False): 22 | original_action = deepcopy(action) 23 | if action["type"] == "action" and action["action"] == "bind": 24 | # If it's a bind, return true if the rval is used. 25 | # 26 | # This is technically more complicated, since a reassignment might detach the variable. 27 | # But whatever 28 | return action_replace_symbol(action["rvalue"], sym, alternateSym) 29 | elif action["type"] == "sym": 30 | # If it's a symbol, return true if correct symbol. 31 | if action["val"] == sym["val"]: 32 | for key in alternateSym: 33 | action[key] = alternateSym[key] 34 | return True 35 | return False 36 | elif action["type"] == "action" and action["action"] == "apply": 37 | # If it's an apply, check the function symbol + all args. 38 | result = False 39 | if action["sym"]["val"] == sym["val"]: 40 | action["sym"] = alternateSym 41 | result = True 42 | 43 | for i in range(len(action["args"])): 44 | arg = action["args"][i] 45 | result = action_replace_symbol(arg, sym, alternateSym) or result 46 | if result: 47 | if DEBUG: print "[analyzer] Did substitution: {} => {}".format(rts(original_action), rts(action)) 48 | return result 49 | else: 50 | return False 51 | 52 | def propogateConstants(actions, DEBUG=False): 53 | for i in range(len(actions)): 54 | action = actions[i] 55 | if action["type"] == "action" and action["action"] == "bind": 56 | symbol = action["sym"] 57 | # check to see if it is a constant, replacable. 58 | if isImm(action["rvalue"]) and action["rvalue"]["dtype"] != "constant_string": 59 | action["constant"] = True 60 | hits = 0 61 | for j in range(i+1, len(actions)): 62 | # direct substitution 63 | if action_replace_symbol(actions[j], symbol, action["rvalue"], DEBUG=DEBUG): 64 | hits = hits + 1 65 | if actions[j]["type"] == "action" and actions[j]["action"] == "bind" and actions[j]["sym"] == symbol: 66 | if DEBUG: print "[analyzer] [!] Stopped constant substitution bc of rebinding (at {}).".format(actions[j]) 67 | break 68 | return actions 69 | 70 | def generateSymTable(actions, DEBUG=False): 71 | sym_table = {} 72 | for i in range(len(actions)): 73 | action = actions[i] 74 | if action["type"] == "action" and action["action"] == "bind": 75 | # variable binding. introduce this. 76 | symbol = action["sym"] 77 | sym_table[symbol["val"]] = { 78 | "enter" : i 79 | } 80 | # find out where this exits, by finding last reference 81 | exit = -1 82 | for j in reversed(range(len(actions))): 83 | if action_references_symbol(actions[j], symbol, DEBUG=DEBUG): 84 | sym_table[symbol["val"]]["exit"] = j 85 | exit = j 86 | break 87 | # if i == j, then the result of the expression is unused. 88 | action["unused"] = (i == j) or (exit == -1) 89 | if action["unused"]: sym_table[symbol["val"]]["exit"] = i 90 | return sym_table -------------------------------------------------------------------------------- /binaries/elf-Linux-x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/binaries/elf-Linux-x86 -------------------------------------------------------------------------------- /flatten.py: -------------------------------------------------------------------------------- 1 | from process import ROPSyntaxError 2 | from primitives import primitives 3 | from copy import deepcopy 4 | from symbols import * 5 | 6 | def flatten(actions, optimize=True, DEBUG=False): 7 | ''' 8 | Given a bunch of actions, flatten them as best as possible. 9 | 10 | i.e: 11 | 12 | let y = 4; 13 | let x = 10 + y; 14 | 15 | ''' 16 | output_actions = [] 17 | for action in actions: 18 | if DEBUG: print "Flattening " + rts(action) 19 | if action["action"] == "bind": 20 | # a variable binding (let = ) 21 | r_value = action["rvalue"] 22 | setup, r_value = flatten_rvalue(r_value, DEBUG=DEBUG) 23 | action["rvalue"] = r_value 24 | output_actions.extend(setup) 25 | else: 26 | # any expression () 27 | setup, action = flatten_rvalue(action, DEBUG=DEBUG) 28 | output_actions.extend(setup) 29 | output_actions.append(action) 30 | return output_actions, output_actions != actions 31 | 32 | def flatten_rvalue(rvalue, DEBUG=False): 33 | setup = [] 34 | rvalue = deepcopy(rvalue) 35 | if rvalue["type"] == "action": 36 | if rvalue["action"] == "apply": 37 | if isSym(rvalue["sym"]) and rvalue["sym"]["val"] in primitives["bin"]: 38 | # _!!__ Binary Operator Optimizations __!!___ 39 | if DEBUG: print "Attempting to optimize binary operator." 40 | prim = primitives["bin"][rvalue["sym"]["val"]] 41 | if isImm(rvalue["args"][0]) and isImm(rvalue["args"][1]): 42 | expected_types = prim["expects"] 43 | expected_type = expected_types[0] 44 | if not immsAreTypes(rvalue["args"], expected_types): 45 | raise ROPSyntaxError("{}: Expected arguments of type(s) '{}'".format(rvalue["sym"], expected_type)) 46 | else: 47 | # These are totally collapsable. 48 | rvalue = getImmRef(prim["func"](*map(lambda x: x["val"], rvalue["args"])), expected_type, None) 49 | if DEBUG: print "Collapsed rvalue to {}".format(rts(rvalue)) 50 | elif isImm(rvalue["args"][0]) or isImm(rvalue["args"][1]): 51 | # one of them is primitive. check for special cases. 52 | optimizedArg = rvalue["args"][(0 if isImm(rvalue["args"][0]) else 1)] 53 | other = 1 if optimizedArg == rvalue["args"][0] else 0 54 | if rvalue["sym"] == "ma_add" and optimizedArg["val"] == 0: 55 | # adding nothing -- default to other r-value. 56 | rvalue = other 57 | elif rvalue["sym"] == "ma_multiply": 58 | if optimizedArg["val"] == 0: 59 | # mult w/ zero = 0 60 | rvalue = getImmRef(0, "constant_numerical", None) 61 | elif optimizedArg["val"] == 1: 62 | rvalue = other 63 | if rvalue["type"] == "action" and rvalue["action"] == "apply": 64 | # General function flattening. 65 | print "Flattening r-val: {}".format(rvalue) 66 | for i in range(len(rvalue["args"])): 67 | arg = rvalue["args"][i] 68 | # Try to reduce the argument 69 | print "Attempting to flatten arg #{}: {}".format(i, arg) 70 | arg_setup, arg = flatten_rvalue(arg) 71 | setup.extend(arg_setup) 72 | rvalue["args"][i] = arg 73 | if refRequiresTemp(arg): 74 | # If it actually reduces and requires setup 75 | if DEBUG: print "Moving out arg #{} of {} to temporary variable.".format(i, rts(rvalue["sym"])) 76 | # Move this out to a temporary binding. 77 | extra_step = makeTemporaryBindAction(arg, arg["loc"]) 78 | setup.append(extra_step) 79 | if DEBUG: print "Extra setup: {}".format(rts(extra_step)) 80 | rvalue["args"][i] = extra_step["sym"] 81 | if DEBUG: print "Reassigned arg to: " + rts(rvalue["args"][i]) 82 | return setup, rvalue 83 | -------------------------------------------------------------------------------- /gadgets.py: -------------------------------------------------------------------------------- 1 | import ropgadget 2 | from ropgadget.binary import Binary 3 | from ropgadget.options import Options 4 | from ropgadget.args import Args 5 | from ropgadget.gadgets import Gadgets 6 | from ropgadget.core import Core 7 | import re 8 | 9 | def mkgadget(vaddr, gtype): 10 | return {"vaddr" : vaddr, "type" : gtype} 11 | 12 | def esp_lift(vaddr, amount, affected_regs, roplen): 13 | g = mkgadget(vaddr, "ESP_LFT") 14 | g["REGS"] = affected_regs 15 | g["LEN"] = roplen 16 | g["AMT"] = amount 17 | return g 18 | 19 | def reg_load_stck(vaddr, affected_regs, roplen): 20 | ''' 21 | Gadget which allows loading registers from stack. 22 | ''' 23 | g = mkgadget(vaddr, "REG_LOAD_MEM") 24 | g["REGS"] = affected_regs 25 | g["LEN"] = roplen 26 | return g 27 | 28 | def reg_write_mem(vaddr, affected_regs, affected_mem, roplen): 29 | ''' 30 | Gadget to write from register -> memory. 31 | ''' 32 | g = mkgadget(vaddr, "REG_LOAD_MEM") 33 | g["REGS"] = affected_regs 34 | g["WHERE"] = affected_mem 35 | g["LEN"] = roplen 36 | return g 37 | 38 | def findAffectedRegisters(disasm): 39 | instrs = disasm.split(";") 40 | regs = r"(eax|esi|edi|esp|ebx|ebp|edx|ecx)" 41 | return re.findall(regs, disasm) 42 | 43 | P_IS_REG_LOAD = r"pop (eax|esi|edi|esp|ebx|ebp|edx|ecx)" 44 | P_ADD = r"add" 45 | P_SUB = r"sub" 46 | P_ESP_LFT = r"(add esp)|(pop)" 47 | 48 | P_MEM_WRITE = r"mov (?P[d|q]word|word|byte) ptr (?P\[(eax|esi|edi|esp|ebx|ebp|edx|ecx)\]), (?Peax|esi|edi|esp|ebx|ebp|edx|ecx)" 49 | P_MEM_READ = r"mov (?Peax|esi|edi|esp|ebx|ebp|edx|ecx), (?P[d|q]word|word|byte) ptr (?P\[(eax|esi|edi|esp|ebx|ebp|edx|ecx)\])" 50 | 51 | TYPES = { 52 | "reg_load" : lambda disasm: re.search(P_IS_REG_LOAD, disasm), # gadgets to load reg from stack 53 | "add" : lambda disasm: re.search(P_ADD, disasm), # gadgets to perform addition on register 54 | "sub" : lambda disasm: re.search(P_SUB, disasm), # gadgets to perform subtraction on register 55 | "esp_lift" : lambda disasm: re.search(P_ESP_LFT, disasm), # gadgets to lift ESP 56 | "mem_read" : lambda disasm: re.search(P_MEM_READ, disasm), # gadgets to read word from memory into reg 57 | "mem_write" : lambda disasm: re.search(P_MEM_WRITE, disasm), # gadgets to write word from reg into memory. 58 | "other" : lambda disasm: True 59 | } 60 | 61 | def parseEspLift(disasm, data): 62 | # count number of pops. 63 | # count amount added to esp 64 | numPops = re.findall(r"pop", disasm) 65 | espAdds = re.findall(r"add esp, (?P0x[a-fA-F0-9]+)", disasm) 66 | 67 | data["AMT"] = len(numPops) 68 | data["REGS"] = findAffectedRegisters(disasm) 69 | if espAdds: 70 | data["AMT"] += sum(map(lambda ref: int(ref, 16), espAdds)) 71 | 72 | def parseMemOp(disasm, data): 73 | writes = re.finditer(P_MEM_WRITE, disasm) 74 | for write in writes: 75 | reg = write.group('to') 76 | from_reg = write.group('from') 77 | amt = write.group('amt') 78 | if not "LOAD" in data or not data["LOAD"]: data["LOAD"] = set() 79 | data["LOAD"].add((from_reg, reg)) # indicating reading or writing from reg -> reg 80 | data["AMT"] = amt 81 | 82 | 83 | PARSERS = { 84 | "reg_load" : lambda disasm, data: re.search(P_IS_REG_LOAD, disasm), # gadgets to load reg from stack 85 | "add" : lambda disasm, data: re.search(P_ADD, disasm), # gadgets to perform addition on register 86 | "sub" : lambda disasm, data: re.search(P_SUB, disasm), # gadgets to perform subtraction on register 87 | "esp_lift" : parseEspLift, # gadgets to lift ESP 88 | "mem_read" : parseMemOp, # gadgets to read word from memory into reg 89 | "mem_write" : parseMemOp, # gadgets to write word from reg into memory. 90 | "other" : lambda disasm, data: True 91 | } 92 | 93 | def classifyGadget(gadget): 94 | ''' 95 | {'gadget': 96 | u'xor dword ptr [edx], 0x2b ; add byte ptr [eax], al ; ret', 97 | 'prev': '\x0f\x08[=\x01\xf0\xff\xff\x0f', 98 | 'bytes': '\x832+\x00\x00\xc3', 99 | 'decodes': , 100 | 'vaddr': 134568009L} 101 | ''' 102 | gadget["REGS"] = findAffectedRegisters(gadget["gadget"]) 103 | if not "types" in gadget: gadget["types"] = [] 104 | for key in TYPES: 105 | validator = TYPES[key] 106 | if validator(gadget["gadget"]): 107 | gadget["types"].append(key) 108 | PARSERS[key](gadget["gadget"], gadget) 109 | return gadget["types"] 110 | 111 | def gadgets(path): 112 | # TODO: Use ROPGadget to load gadgets from the binary in path 113 | args = ["--binary", path, "--callPreceded"] 114 | ropcore = Core(Args(args).getArgs()) 115 | all_gadgets = ropcore.getGadgetsQuiet() 116 | print "ropgadget provided {} gadgets.".format(len(all_gadgets)) 117 | gadgets = { 118 | "reg_load" : [], # gadgets to load reg from stack 119 | "add" : [], # gadgets to perform addition on register 120 | "sub" : [], # gadgets to perform subtraction on register 121 | "esp_lift" : [], # gadgets to lift ESP 122 | "mem_read" : [], # gadgets to read word from memory into reg 123 | "mem_write" : [], # gadgets to write word from reg into memory. 124 | "other" : [] 125 | } 126 | 127 | for gadget in all_gadgets: 128 | gtypes = classifyGadget(gadget) 129 | for gtype in gtypes: 130 | gadgets[gtype].append(gadget) 131 | print "----Gadget Types Loaded----" 132 | for key in gadgets: 133 | print "{}: {} gadgets".format(key, len(gadgets[key])) 134 | print "---------------------------" 135 | print_gadgets_from = "mem_write" 136 | for gadget in gadgets[print_gadgets_from]: 137 | print "{}\n writing ability: {}\n".format(gadget["gadget"],gadget["LOAD"]) 138 | assert False 139 | return gadgets -------------------------------------------------------------------------------- /gracoparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # CAVEAT UTILITOR 5 | # 6 | # This file was automatically generated by Grako. 7 | # 8 | # https://pypi.python.org/pypi/grako/ 9 | # 10 | # Any changes you make to it will be overwritten the next time 11 | # the file is generated. 12 | 13 | 14 | from __future__ import print_function, division, absolute_import, unicode_literals 15 | 16 | from grako.buffering import Buffer 17 | from grako.parsing import graken, Parser 18 | from grako.util import re, RE_FLAGS, generic_main # noqa 19 | 20 | 21 | KEYWORDS = {} 22 | 23 | 24 | class CropBuffer(Buffer): 25 | def __init__( 26 | self, 27 | text, 28 | whitespace=re.compile('[\\t\\n\\s]+', RE_FLAGS | re.DOTALL), 29 | nameguard=False, 30 | comments_re='\\/\\*(.|\\s|\\n)*?\\*\\/', 31 | eol_comments_re='\\/\\/.*?$', 32 | ignorecase=None, 33 | namechars='', 34 | **kwargs 35 | ): 36 | super(CropBuffer, self).__init__( 37 | text, 38 | whitespace=whitespace, 39 | nameguard=nameguard, 40 | comments_re=comments_re, 41 | eol_comments_re=eol_comments_re, 42 | ignorecase=ignorecase, 43 | namechars=namechars, 44 | **kwargs 45 | ) 46 | 47 | 48 | class CropParser(Parser): 49 | def __init__( 50 | self, 51 | whitespace=re.compile('[\\t\\n\\s]+', RE_FLAGS | re.DOTALL), 52 | nameguard=False, 53 | comments_re='\\/\\*(.|\\s|\\n)*?\\*\\/', 54 | eol_comments_re='\\/\\/.*?$', 55 | ignorecase=None, 56 | left_recursion=False, 57 | parseinfo=True, 58 | keywords=None, 59 | namechars='', 60 | buffer_class=CropBuffer, 61 | **kwargs 62 | ): 63 | if keywords is None: 64 | keywords = KEYWORDS 65 | super(CropParser, self).__init__( 66 | whitespace=whitespace, 67 | nameguard=nameguard, 68 | comments_re=comments_re, 69 | eol_comments_re=eol_comments_re, 70 | ignorecase=ignorecase, 71 | left_recursion=left_recursion, 72 | parseinfo=parseinfo, 73 | keywords=keywords, 74 | namechars=namechars, 75 | buffer_class=buffer_class, 76 | **kwargs 77 | ) 78 | 79 | @graken() 80 | def _constant_string_(self): 81 | self._pattern(r'".*"') 82 | self.name_last_node('val') 83 | self.ast._define( 84 | ['val'], 85 | [] 86 | ) 87 | 88 | @graken() 89 | def _constant_numerical_(self): 90 | self._pattern(r'[0-9]+') 91 | self.name_last_node('val') 92 | self.ast._define( 93 | ['val'], 94 | [] 95 | ) 96 | 97 | @graken() 98 | def _constant_hexadecimal_(self): 99 | self._pattern(r'0x([a-f]|[A-F]|[0-9])+') 100 | self.name_last_node('val') 101 | self.ast._define( 102 | ['val'], 103 | [] 104 | ) 105 | 106 | @graken() 107 | def _identifier_(self): 108 | self._pattern(r'[a-zA-Z][A-Za-z0-9_]*') 109 | self.name_last_node('val') 110 | self.ast._define( 111 | ['val'], 112 | [] 113 | ) 114 | 115 | @graken() 116 | def _add_expr_(self): 117 | self._expr_() 118 | self._token('+') 119 | self._expr_() 120 | 121 | @graken() 122 | def _sub_expr_(self): 123 | self._expr_() 124 | self._token('-') 125 | self._expr_() 126 | 127 | @graken() 128 | def _mul_expr_(self): 129 | self._expr_() 130 | self._token('*') 131 | self._expr_() 132 | 133 | @graken() 134 | def _div_expr_(self): 135 | self._expr_() 136 | self._token('/') 137 | self._expr_() 138 | 139 | @graken() 140 | def _type_(self): 141 | with self._group(): 142 | with self._choice(): 143 | with self._option(): 144 | self._token('int') 145 | with self._option(): 146 | self._token('string') 147 | with self._option(): 148 | self._token('float') 149 | self._error('expecting one of: float int string') 150 | 151 | @graken() 152 | def _ARGLIST_(self): 153 | self._token('(') 154 | with self._optional(): 155 | self._complex_expr_() 156 | self.add_last_node_to_name('@') 157 | 158 | def block1(): 159 | self._token(',') 160 | self._complex_expr_() 161 | self.add_last_node_to_name('@') 162 | self._closure(block1) 163 | self._token(')') 164 | 165 | @graken() 166 | def _function_application_(self): 167 | self._expr_() 168 | self.name_last_node('id') 169 | self._ARGLIST_() 170 | self.name_last_node('args') 171 | self.ast._define( 172 | ['args', 'id'], 173 | [] 174 | ) 175 | 176 | @graken() 177 | def _inline_application_(self): 178 | with self._group(): 179 | with self._choice(): 180 | with self._option(): 181 | self._add_expr_() 182 | with self._option(): 183 | self._sub_expr_() 184 | with self._option(): 185 | self._mul_expr_() 186 | with self._option(): 187 | self._div_expr_() 188 | self._error('no available options') 189 | self.name_last_node('bin_function') 190 | self.ast._define( 191 | ['bin_function'], 192 | [] 193 | ) 194 | 195 | @graken() 196 | def _function_declaration_(self): 197 | self._token('func') 198 | self._identifier_() 199 | self.name_last_node('id') 200 | self._token('=') 201 | self._complex_expr_() 202 | self.name_last_node('rval') 203 | self._token(':') 204 | self._type_() 205 | self.ast._define( 206 | ['id', 'rval'], 207 | [] 208 | ) 209 | 210 | @graken() 211 | def _bind_(self): 212 | self._token('let') 213 | self._identifier_() 214 | self.name_last_node('id') 215 | self._token('=') 216 | self._complex_expr_() 217 | self.name_last_node('rval') 218 | self.ast._define( 219 | ['id', 'rval'], 220 | [] 221 | ) 222 | 223 | @graken() 224 | def _expr_(self): 225 | with self._group(): 226 | with self._choice(): 227 | with self._option(): 228 | self._constant_string_() 229 | with self._option(): 230 | self._constant_hexadecimal_() 231 | with self._option(): 232 | self._constant_numerical_() 233 | with self._option(): 234 | self._identifier_() 235 | self._error('no available options') 236 | 237 | @graken() 238 | def _complex_expr_(self): 239 | with self._choice(): 240 | with self._option(): 241 | self._inline_application_() 242 | with self._option(): 243 | self._function_application_() 244 | with self._option(): 245 | self._expr_() 246 | self._error('no available options') 247 | 248 | @graken() 249 | def _line_(self): 250 | with self._optional(): 251 | with self._choice(): 252 | with self._option(): 253 | self._bind_() 254 | with self._option(): 255 | self._function_application_() 256 | with self._option(): 257 | self._function_declaration_() 258 | self._error('no available options') 259 | self._token(';') 260 | 261 | @graken() 262 | def _start_(self): 263 | self._line_() 264 | self.add_last_node_to_name('@') 265 | 266 | def block1(): 267 | self._line_() 268 | self.add_last_node_to_name('@') 269 | self._positive_closure(block1) 270 | 271 | 272 | class CropSemantics(object): 273 | def constant_string(self, ast): 274 | return ast 275 | 276 | def constant_numerical(self, ast): 277 | return ast 278 | 279 | def constant_hexadecimal(self, ast): 280 | return ast 281 | 282 | def identifier(self, ast): 283 | return ast 284 | 285 | def add_expr(self, ast): 286 | return ast 287 | 288 | def sub_expr(self, ast): 289 | return ast 290 | 291 | def mul_expr(self, ast): 292 | return ast 293 | 294 | def div_expr(self, ast): 295 | return ast 296 | 297 | def type(self, ast): 298 | return ast 299 | 300 | def ARGLIST(self, ast): 301 | return ast 302 | 303 | def function_application(self, ast): 304 | return ast 305 | 306 | def inline_application(self, ast): 307 | return ast 308 | 309 | def function_declaration(self, ast): 310 | return ast 311 | 312 | def bind(self, ast): 313 | return ast 314 | 315 | def expr(self, ast): 316 | return ast 317 | 318 | def complex_expr(self, ast): 319 | return ast 320 | 321 | def line(self, ast): 322 | return ast 323 | 324 | def start(self, ast): 325 | return ast 326 | 327 | 328 | def main(filename, startrule, **kwargs): 329 | with open(filename) as f: 330 | text = f.read() 331 | parser = CropParser() 332 | return parser.parse(text, startrule, filename=filename, **kwargs) 333 | 334 | 335 | if __name__ == '__main__': 336 | import json 337 | from grako.util import asjson 338 | 339 | ast = generic_main(main, CropParser, name='Crop') 340 | print('AST:') 341 | print(ast) 342 | print() 343 | print('JSON:') 344 | print(json.dumps(asjson(ast), indent=2)) 345 | print() 346 | -------------------------------------------------------------------------------- /grammar.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | grammar = { 4 | "tokens" : [ 5 | { 6 | "name" : "let", 7 | "regex" : r"(\A|\b)let" 8 | }, 9 | { 10 | "name" : "single_line_comment", 11 | "regex" : r"//(.*)$" 12 | }, 13 | { 14 | "name" : "multi_line_comment_start", 15 | "regex" : r"/[*]+" 16 | }, 17 | { 18 | "name" : "multi_line_comment_end", 19 | "regex" : r"[*]+/" 20 | }, 21 | { 22 | "name" : "EOL", 23 | "regex" : r";" 24 | }, 25 | { 26 | "name" : "ma_add", 27 | "regex" : r"\+" 28 | }, 29 | { 30 | "name" : "ma_subtract", 31 | "regex" : r"\-" 32 | }, 33 | { 34 | "name" : "ma_multiply", 35 | "regex" : r"\*" 36 | }, 37 | { 38 | "name" : "if", 39 | "regex" : r"\bif\b" 40 | }, 41 | { 42 | "name" : "assign", 43 | "regex" : r"\s+=\s+" 44 | }, 45 | { 46 | "name" : "equals", 47 | "regex" : r"\s==\s" 48 | }, 49 | { 50 | "name" : "start_apply", 51 | "regex" : r"\(" 52 | }, 53 | { 54 | "name" : "end_apply", 55 | "regex" : r"\)" 56 | }, 57 | { 58 | "name" : "arglist_separator", 59 | "regex" : r"," 60 | }, 61 | { 62 | "name" : "constant_string", 63 | "regex" : r"\".*\"" 64 | }, 65 | { 66 | "name" : "constant_numerical", 67 | "regex" : r"[0-9]+", 68 | "transform" : (lambda val: int(val)) 69 | }, 70 | { 71 | "name" : "identifier", 72 | "regex" : r"(\s+|\w|^)(?!let)(?!if)[a-z]+(\s*)" 73 | } 74 | ] 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /grammar/Makefile: -------------------------------------------------------------------------------- 1 | grammar: crop.graco 2 | echo "Building Grammar..." 3 | grako crop.graco -n -m Crop --generate-parser --outfile parser.py 4 | cp parser.py ../gracoparser.py 5 | 6 | test: grammar 7 | python parser.py ../samples/sample.rop 8 | -------------------------------------------------------------------------------- /grammar/crop.graco: -------------------------------------------------------------------------------- 1 | (* Support C-style comments. *) 2 | @@nameguard :: False 3 | @@whitespace :: /[\t\n\s]+/ 4 | @@comments :: /\/\*(.|\s|\n)*?\*\// 5 | @@eol_comments :: /\/\/.*?$/ 6 | 7 | 8 | constant_string = val:/".*"/ ; 9 | constant_numerical = val:/[0-9]+/ ; 10 | constant_hexadecimal = val:/0x([a-f]|[A-F]|[0-9])+/ ; 11 | identifier = val:/[a-zA-Z][A-Za-z0-9_]*/ ; 12 | 13 | add_expr = expr '+' expr ; 14 | sub_expr = expr '-' expr ; 15 | mul_expr = expr '*' expr ; 16 | div_expr = expr '/' expr ; 17 | 18 | type = ("int" | "string" | "float") ; 19 | 20 | ARGLIST = '(' [@+:complex_expr {',' @+:complex_expr}*] ')' ; 21 | function_application = id:expr args:ARGLIST ; 22 | 23 | inline_application = bin_function:(add_expr | sub_expr | mul_expr | div_expr); 24 | function_declaration = "func" id:identifier "=" rval:complex_expr ":" type; 25 | 26 | bind = 'let' id:identifier '=' rval:complex_expr ; 27 | 28 | expr = ( constant_string 29 | | constant_hexadecimal 30 | | constant_numerical 31 | | identifier 32 | ); 33 | 34 | complex_expr = inline_application | function_application | expr; 35 | 36 | line = 37 | [bind | function_application | function_declaration] ";"; 38 | 39 | start = @+:line { @+:line }+ ; 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /grammar/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # CAVEAT UTILITOR 5 | # 6 | # This file was automatically generated by Grako. 7 | # 8 | # https://pypi.python.org/pypi/grako/ 9 | # 10 | # Any changes you make to it will be overwritten the next time 11 | # the file is generated. 12 | 13 | 14 | from __future__ import print_function, division, absolute_import, unicode_literals 15 | 16 | from grako.buffering import Buffer 17 | from grako.parsing import graken, Parser 18 | from grako.util import re, RE_FLAGS, generic_main # noqa 19 | 20 | 21 | KEYWORDS = {} 22 | 23 | 24 | class CropBuffer(Buffer): 25 | def __init__( 26 | self, 27 | text, 28 | whitespace=re.compile('[\\t\\n\\s]+', RE_FLAGS | re.DOTALL), 29 | nameguard=False, 30 | comments_re='\\/\\*(.|\\s|\\n)*?\\*\\/', 31 | eol_comments_re='\\/\\/.*?$', 32 | ignorecase=None, 33 | namechars='', 34 | **kwargs 35 | ): 36 | super(CropBuffer, self).__init__( 37 | text, 38 | whitespace=whitespace, 39 | nameguard=nameguard, 40 | comments_re=comments_re, 41 | eol_comments_re=eol_comments_re, 42 | ignorecase=ignorecase, 43 | namechars=namechars, 44 | **kwargs 45 | ) 46 | 47 | 48 | class CropParser(Parser): 49 | def __init__( 50 | self, 51 | whitespace=re.compile('[\\t\\n\\s]+', RE_FLAGS | re.DOTALL), 52 | nameguard=False, 53 | comments_re='\\/\\*(.|\\s|\\n)*?\\*\\/', 54 | eol_comments_re='\\/\\/.*?$', 55 | ignorecase=None, 56 | left_recursion=False, 57 | parseinfo=True, 58 | keywords=None, 59 | namechars='', 60 | buffer_class=CropBuffer, 61 | **kwargs 62 | ): 63 | if keywords is None: 64 | keywords = KEYWORDS 65 | super(CropParser, self).__init__( 66 | whitespace=whitespace, 67 | nameguard=nameguard, 68 | comments_re=comments_re, 69 | eol_comments_re=eol_comments_re, 70 | ignorecase=ignorecase, 71 | left_recursion=left_recursion, 72 | parseinfo=parseinfo, 73 | keywords=keywords, 74 | namechars=namechars, 75 | buffer_class=buffer_class, 76 | **kwargs 77 | ) 78 | 79 | @graken() 80 | def _constant_string_(self): 81 | self._pattern(r'".*"') 82 | self.name_last_node('val') 83 | self.ast._define( 84 | ['val'], 85 | [] 86 | ) 87 | 88 | @graken() 89 | def _constant_numerical_(self): 90 | self._pattern(r'[0-9]+') 91 | self.name_last_node('val') 92 | self.ast._define( 93 | ['val'], 94 | [] 95 | ) 96 | 97 | @graken() 98 | def _constant_hexadecimal_(self): 99 | self._pattern(r'0x([a-f]|[A-F]|[0-9])+') 100 | self.name_last_node('val') 101 | self.ast._define( 102 | ['val'], 103 | [] 104 | ) 105 | 106 | @graken() 107 | def _identifier_(self): 108 | self._pattern(r'[a-zA-Z][A-Za-z0-9_]*') 109 | self.name_last_node('val') 110 | self.ast._define( 111 | ['val'], 112 | [] 113 | ) 114 | 115 | @graken() 116 | def _add_expr_(self): 117 | self._expr_() 118 | self._token('+') 119 | self._expr_() 120 | 121 | @graken() 122 | def _sub_expr_(self): 123 | self._expr_() 124 | self._token('-') 125 | self._expr_() 126 | 127 | @graken() 128 | def _mul_expr_(self): 129 | self._expr_() 130 | self._token('*') 131 | self._expr_() 132 | 133 | @graken() 134 | def _div_expr_(self): 135 | self._expr_() 136 | self._token('/') 137 | self._expr_() 138 | 139 | @graken() 140 | def _type_(self): 141 | with self._group(): 142 | with self._choice(): 143 | with self._option(): 144 | self._token('int') 145 | with self._option(): 146 | self._token('string') 147 | with self._option(): 148 | self._token('float') 149 | self._error('expecting one of: float int string') 150 | 151 | @graken() 152 | def _ARGLIST_(self): 153 | self._token('(') 154 | with self._optional(): 155 | self._complex_expr_() 156 | self.add_last_node_to_name('@') 157 | 158 | def block1(): 159 | self._token(',') 160 | self._complex_expr_() 161 | self.add_last_node_to_name('@') 162 | self._closure(block1) 163 | self._token(')') 164 | 165 | @graken() 166 | def _function_application_(self): 167 | self._expr_() 168 | self.name_last_node('id') 169 | self._ARGLIST_() 170 | self.name_last_node('args') 171 | self.ast._define( 172 | ['args', 'id'], 173 | [] 174 | ) 175 | 176 | @graken() 177 | def _inline_application_(self): 178 | with self._group(): 179 | with self._choice(): 180 | with self._option(): 181 | self._add_expr_() 182 | with self._option(): 183 | self._sub_expr_() 184 | with self._option(): 185 | self._mul_expr_() 186 | with self._option(): 187 | self._div_expr_() 188 | self._error('no available options') 189 | self.name_last_node('bin_function') 190 | self.ast._define( 191 | ['bin_function'], 192 | [] 193 | ) 194 | 195 | @graken() 196 | def _function_declaration_(self): 197 | self._token('func') 198 | self._identifier_() 199 | self.name_last_node('id') 200 | self._token('=') 201 | self._complex_expr_() 202 | self.name_last_node('rval') 203 | self._token(':') 204 | self._type_() 205 | self.ast._define( 206 | ['id', 'rval'], 207 | [] 208 | ) 209 | 210 | @graken() 211 | def _bind_(self): 212 | self._token('let') 213 | self._identifier_() 214 | self.name_last_node('id') 215 | self._token('=') 216 | self._complex_expr_() 217 | self.name_last_node('rval') 218 | self.ast._define( 219 | ['id', 'rval'], 220 | [] 221 | ) 222 | 223 | @graken() 224 | def _expr_(self): 225 | with self._group(): 226 | with self._choice(): 227 | with self._option(): 228 | self._constant_string_() 229 | with self._option(): 230 | self._constant_hexadecimal_() 231 | with self._option(): 232 | self._constant_numerical_() 233 | with self._option(): 234 | self._identifier_() 235 | self._error('no available options') 236 | 237 | @graken() 238 | def _complex_expr_(self): 239 | with self._choice(): 240 | with self._option(): 241 | self._inline_application_() 242 | with self._option(): 243 | self._function_application_() 244 | with self._option(): 245 | self._expr_() 246 | self._error('no available options') 247 | 248 | @graken() 249 | def _line_(self): 250 | with self._optional(): 251 | with self._choice(): 252 | with self._option(): 253 | self._bind_() 254 | with self._option(): 255 | self._function_application_() 256 | with self._option(): 257 | self._function_declaration_() 258 | self._error('no available options') 259 | self._token(';') 260 | 261 | @graken() 262 | def _start_(self): 263 | self._line_() 264 | self.add_last_node_to_name('@') 265 | 266 | def block1(): 267 | self._line_() 268 | self.add_last_node_to_name('@') 269 | self._positive_closure(block1) 270 | 271 | 272 | class CropSemantics(object): 273 | def constant_string(self, ast): 274 | return ast 275 | 276 | def constant_numerical(self, ast): 277 | return ast 278 | 279 | def constant_hexadecimal(self, ast): 280 | return ast 281 | 282 | def identifier(self, ast): 283 | return ast 284 | 285 | def add_expr(self, ast): 286 | return ast 287 | 288 | def sub_expr(self, ast): 289 | return ast 290 | 291 | def mul_expr(self, ast): 292 | return ast 293 | 294 | def div_expr(self, ast): 295 | return ast 296 | 297 | def type(self, ast): 298 | return ast 299 | 300 | def ARGLIST(self, ast): 301 | return ast 302 | 303 | def function_application(self, ast): 304 | return ast 305 | 306 | def inline_application(self, ast): 307 | return ast 308 | 309 | def function_declaration(self, ast): 310 | return ast 311 | 312 | def bind(self, ast): 313 | return ast 314 | 315 | def expr(self, ast): 316 | return ast 317 | 318 | def complex_expr(self, ast): 319 | return ast 320 | 321 | def line(self, ast): 322 | return ast 323 | 324 | def start(self, ast): 325 | return ast 326 | 327 | 328 | def main(filename, startrule, **kwargs): 329 | with open(filename) as f: 330 | text = f.read() 331 | parser = CropParser() 332 | return parser.parse(text, startrule, filename=filename, **kwargs) 333 | 334 | 335 | if __name__ == '__main__': 336 | import json 337 | from grako.util import asjson 338 | 339 | ast = generic_main(main, CropParser, name='Crop') 340 | print('AST:') 341 | print(ast) 342 | print() 343 | print('JSON:') 344 | print(json.dumps(asjson(ast), indent=2)) 345 | print() 346 | -------------------------------------------------------------------------------- /libcapstone.3.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/libcapstone.3.dylib -------------------------------------------------------------------------------- /libcapstone.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/libcapstone.a -------------------------------------------------------------------------------- /libcapstone.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/libcapstone.dylib -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from process import processAST, ROPSyntaxError 4 | from flatten import flatten 5 | from validate import validate 6 | from analyze import generateSymTable, propogateConstants 7 | from ropcompile import precompile_payload, compile_payload, ROPCompilationError 8 | from gadgets import gadgets 9 | from copy import deepcopy 10 | from tests import run_test_suite 11 | from gracoparser import CropParser 12 | from symbols import * 13 | 14 | import os 15 | import sys 16 | 17 | def printUsage(): 18 | print "usage: {} [--verbose | -v] [--test | -t]".format(sys.argv[0]) 19 | print "program: a program written in crop, to be compiled." 20 | print "binary: an ELF, linux x86 binary to attack." 21 | print "Optional Arguments -" 22 | print "[v]erbose: Enter verbose mode, for debugging." 23 | print "[t]est: Run the test suite." 24 | 25 | def main(args): 26 | 27 | DEBUG = "--verbose" in args or "-v" in args 28 | if DEBUG: 29 | idx = args.index("--verbose") if "--verbose" in args else args.index("-v") 30 | del args[idx] 31 | RUN_TESTS = "--test" in args or "-t" in args 32 | if RUN_TESTS: 33 | idx = args.index("--test") if "--test" in args else args.index("-t") 34 | del args[idx] 35 | 36 | if len(args) != 2: 37 | printUsage() 38 | return 39 | 40 | if RUN_TESTS: 41 | # TODO: Run test suite. 42 | print "crop: Test Suite" 43 | print "------------------" 44 | run_test_suite() 45 | print "------------------" 46 | else: 47 | BUFFER_ADDR = 0xb77ff300 48 | print "[info] Buffer located at {}".format(BUFFER_ADDR) 49 | 50 | # Regular mode. 51 | corpus = args[0] 52 | fname = args[1] 53 | 54 | # Load Text 55 | with open(corpus, "r") as f: 56 | corpus = f.read() 57 | 58 | # Process Text into program actions. 59 | try: 60 | # Tokenize text using the grako parser. 61 | parser = CropParser() 62 | ast = parser.parse(corpus) 63 | print "[+] Parser finished." 64 | 65 | 66 | # Process tokens -> actions. First pass. 67 | actions = processAST(ast) 68 | print "[+] Lexer finished." 69 | 70 | if DEBUG: 71 | print "---Stage 1 Compilation---" 72 | for action in actions: 73 | print rts(action) 74 | print "------------------------------" 75 | 76 | validate(actions) 77 | print "[+] Validator finished." 78 | 79 | flattening = True 80 | 81 | while flattening: 82 | # Propogate Constants 83 | actions = propogateConstants(actions, DEBUG=True) 84 | print "[-] Analyzer propogated constants..." 85 | # Flatten actions to optimize them. 86 | actions, flattening = flatten(actions, optimize=True, DEBUG=False) 87 | print "[-] Flattened" 88 | print "Flattening done." 89 | print "Final propogation: " 90 | actions = propogateConstants(actions, DEBUG=True) 91 | 92 | if DEBUG: 93 | print "" 94 | print "---Stage 2 Compilation---" 95 | for action in actions: 96 | print rts(action) 97 | print "------------------------------" 98 | 99 | 100 | # Analyze for variable lifetimes. 101 | sym_table = generateSymTable(actions, DEBUG=DEBUG) 102 | print "[+] Analyzer generated Symbol Table" 103 | 104 | if DEBUG: 105 | print "## Sym table ##" 106 | for symbol in sym_table: 107 | print "# {} -> ({}, {})".format(symbol, sym_table[symbol]["enter"], sym_table[symbol]["exit"]) 108 | print "###############" 109 | 110 | for action in actions: 111 | if "unused" in action and action["unused"] and not "constant" in action: 112 | print "[analyzer] Warning: variable '{}' unused.".format(action["sym"]["val"]) 113 | if DEBUG: 114 | print "---Stage 3 Compilation---" 115 | for action in actions: 116 | print rts(action) 117 | print "------------------------------" 118 | except ROPSyntaxError as e: 119 | print e 120 | return 121 | 122 | try: 123 | # Given the actions, find ROP sequences that satisfy these actions. 124 | sequences = None 125 | payload = precompile_payload(actions, sym_table, sequences, DEBUG=DEBUG) 126 | if DEBUG: 127 | printStackPayload(payload) 128 | print "[+] Payload Generation: Precompiled payload. " 129 | 130 | # load the gadgets from the binary. 131 | print "[-] ropgadget: Loading gadgets..." 132 | binary_gadgets = gadgets(fname) 133 | num_gadgets_total = sum([len(binary_gadgets[k]) for k in binary_gadgets]) 134 | 135 | print "[+] ropgadget: Loaded gadgets from \"{}\" (got {})".format(fname, num_gadgets_total) 136 | 137 | # compile the actual payload. 138 | full_payload = compile_payload(payload, binary_gadgets, BUFFER_ADDR, DEBUG=DEBUG) 139 | print "[+] Compiled final payload." 140 | 141 | if DEBUG: 142 | printStackPayload(payload) 143 | except ROPCompilationError as e: 144 | print e 145 | return 146 | 147 | if __name__ == "__main__": 148 | main(sys.argv[1:]) -------------------------------------------------------------------------------- /primitives.py: -------------------------------------------------------------------------------- 1 | primitives = { 2 | "bin" : { 3 | "ma_add" : { 4 | "argc" : 2, # num args required 5 | "func" : lambda x,y: x + y, # anonymous function 6 | "expects" : ["constant_numerical", "constant_hexadecimal"], # arg types 7 | "desc" : "+" # description 8 | }, 9 | "ma_subtract" : { 10 | "argc" : 2, 11 | "func" : lambda x,y: x - y, 12 | "expects" : ["constant_numerical", "constant_hexadecimal"], 13 | "desc" : "-" 14 | }, 15 | "ma_multiply" : { 16 | "argc" : 2, 17 | "func" : lambda x,y: x * y, 18 | "expects" : ["constant_numerical", "constant_hexadecimal"], 19 | "desc" : "*" 20 | } 21 | }, 22 | "std" : { 23 | "mem_read" :{ 24 | "argc" : 2, 25 | "desc" : "mem_read()", 26 | "info" : "read a word from memory" 27 | }, 28 | "mem_write" : { 29 | "argc" : 2, 30 | "desc" : "mem_write()", 31 | "info" : "write a word to memory" 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /process.py: -------------------------------------------------------------------------------- 1 | from symbols import * 2 | from primitives import primitives 3 | import grako 4 | 5 | #### Process the tokens into immediate forms. 6 | class ROPSyntaxError(Exception): 7 | def __init__(self, message, location): 8 | # Call the base class constructor with the parameters it needs 9 | super(ROPSyntaxError, self).__init__("Syntax Error: {} (at line {}, character {})".format(message, location["line"], location["char"])) 10 | self.location = location 11 | self.message = message 12 | 13 | def processAST(ast): 14 | ''' 15 | returns a list of to give to the rest of the compiler. 16 | ''' 17 | stmts = [] 18 | for statement in ast: 19 | assert len(statement) == 2 and statement[1] == ";" 20 | contents = statement[0] 21 | contents_normalized = normalizeExpression(contents) 22 | stmts.append(contents_normalized) 23 | return stmts 24 | 25 | # AST dict 2 location. convenience method returns (line, char) pairs. 26 | def ast2l(pi): 27 | try: 28 | return {"line" : pi["parseinfo"].line, "char" : pi["parseinfo"].pos} 29 | except TypeError as e: 30 | print "Error parsing '{}' - {}".format(pi, e) 31 | raise Exception("Uncaught.") 32 | 33 | def normalizeExpression(ast): 34 | ''' 35 | Attempts to recursively normalize a Grako AST to fit our compiler spec. 36 | Just renames some things. 37 | ''' 38 | action = None 39 | #print "Processing {}".format(ast) 40 | root_loc = ast2l(ast) 41 | ttype = ast["parseinfo"].rule 42 | 43 | if ttype == "bind": 44 | # Parse variable binding. (let = ) 45 | identifier = ast["id"] 46 | identifier_location = ast2l(identifier) 47 | 48 | lvalue = normalizeExpression(identifier) 49 | rvalue = normalizeExpression(ast["rval"]) 50 | 51 | action = makeBindActionRaw(lvalue, rvalue, root_loc) 52 | elif ttype == "function_application": 53 | # 1. normalize all arguments. (filter for AST arguments only -- everything else is garbage.) 54 | # 55 | # For example, on empty invocations (e.g call()), grako will leave behind 56 | # call : { args: ["(", ")"]}, which aren't even AST members. /shrug? 57 | args = map(normalizeExpression, filter(lambda x: type(x) is grako.ast.AST, ast["args"])) 58 | 59 | # 2. normalize identifier / constant. 60 | fid = normalizeExpression(ast["id"]) 61 | 62 | # 3. formulate action. 63 | action = makeApplyActionRaw(fid, args, fid["loc"]) 64 | elif ttype == "inline_application": 65 | print ast 66 | 67 | expr = ast["bin_function"] 68 | fargs = [expr[0], expr[2]] # + --> 0, 2 are args 69 | fargs = map(normalizeExpression, fargs) 70 | 71 | fname = expr[1] 72 | for operator in primitives["bin"]: 73 | if fname == primitives["bin"][operator]["desc"]: 74 | real_fname = operator 75 | break 76 | assert real_fname, "Unknown operator" 77 | 78 | # TODO: This is technically wrong -- we should propogate metadata about binary operators. 79 | inline_sym_ref = getSymRef(real_fname, root_loc) 80 | action = makeApplyActionRaw(inline_sym_ref, fargs, root_loc) 81 | elif ttype == "identifier": 82 | # Parse identifiers. 83 | action = getSymRef(ast["val"], ast2l(ast)) 84 | elif ttype in ["constant_numerical", "constant_hexadecimal", "constant_string"]: 85 | # Parse constant types. 86 | val = ast["val"] 87 | 88 | # add ur own bases here lol 89 | mappers = { 90 | "constant_numerical" : lambda x: int(x, 10), 91 | "constant_hexadecimal" : lambda x: int(x, 16), 92 | "constant_string" : lambda x: x[1:][:-1] 93 | } 94 | 95 | action = getImmRef(mappers[ttype](val), ttype, ast2l(ast)) 96 | else: 97 | print ast["parseinfo"].rule 98 | return action 99 | 100 | -------------------------------------------------------------------------------- /ropcompile.py: -------------------------------------------------------------------------------- 1 | from symbols import * 2 | from copy import deepcopy 3 | from operator import itemgetter 4 | from primitives import primitives 5 | 6 | class ROPCompilationError(Exception): 7 | def __init__(self, message): 8 | # Call the base class constructor with the parameters it needs 9 | super(ROPCompilationError, self).__init__("Compilation Error: {}".format(message)) 10 | 11 | 12 | def unusedOrConstant(expr): 13 | return ("unused" in expr and expr["unused"]) or ("constant" in expr and expr["constant"]) 14 | 15 | def sym2retaddr(sym): 16 | sym = deepcopy(sym) 17 | if sym["dtype"] == "constant_numerical": 18 | sym["dtype"] = "constant_hexadecimal" 19 | sym["roptype"] = "CALL" 20 | sym["ropdtype"] = text_green(sym["roptype"]) 21 | return sym 22 | 23 | def sym2emptyref(sym, val=""): 24 | sym = deepcopy(sym) 25 | sym["roptype"] = val 26 | return sym 27 | 28 | def sym2arg(sym, argi): 29 | sym = deepcopy(sym) 30 | sym["roptype"] = "ARG" 31 | sym["ropdata"] = argi 32 | return sym 33 | 34 | def imm2esplift(sym): 35 | sym = deepcopy(sym) 36 | sym["roptype"] = "ESP_LFT" 37 | sym["ropdata"] = sym 38 | return sym 39 | 40 | def emptyAddrRef(): 41 | return sym2retaddr(getImmRef(0, "constant_hexadecimal", None)) 42 | 43 | def emptyPaddingRef(): 44 | return sym2emptyref(getImmRef(0, "constant_hexadecimal", None), val="") 45 | 46 | def dataReadRef(fromPlace): 47 | sym["type"] = "builtin" 48 | sym["roptype"] = "G_READ" 49 | sym["ropdtype"] = text_green(sym["roptype"]) 50 | sym["ropdata"] = fromPlace 51 | return sym 52 | 53 | def dataWriteRef(fromPlace, intoPlace): 54 | sym = deepcopy(intoPlace) 55 | sym["roptype"] = "G_WRITE" 56 | sym["ropdtype"] = text_green(sym["roptype"]) 57 | sym["ropdata"] = fromPlace 58 | return sym 59 | 60 | def absoluteDataRef(data): 61 | sym = deepcopy(data) 62 | sym["roptype"] = "ADDR" 63 | return sym 64 | 65 | def emptyRef(): 66 | return sym2emptyref(getImmRef(0, "constant_hexadecimal", None)) 67 | 68 | def imm2dataRef(sym): 69 | sym = deepcopy(sym) 70 | sym["roptype"] = "DATA" 71 | sym["ropdata"] = sym 72 | return sym 73 | 74 | def imm2constRef(sym): 75 | sym = deepcopy(sym) 76 | sym["roptype"] = "CONST" 77 | sym["ropdtype"] = text_bold(sym["roptype"]) 78 | return sym 79 | 80 | def dataRef(idx): 81 | return imm2dataRef(getImmRef(idx, "constant_numerical", None)) 82 | 83 | def esplift(amt): 84 | return imm2esplift(getImmRef(amt, "constant_numerical", None)) 85 | 86 | def stack_entry_gadget(gtype, vaddr): 87 | sym = getImmRef(vaddr, "constant_hexadecimal", None) 88 | sym["roptype"] = "GADGET" 89 | sym["ropdtype"] = text_blue(sym["roptype"]) 90 | sym["ropdata"] = getImmRef(gtype, "string", None) 91 | return sym 92 | 93 | def resolvedEspLift(gadget): 94 | return { 95 | "roptype" : "" 96 | } 97 | 98 | def reserve(stack, upTo): 99 | print "[stack] Reserving stack space up to index {}.".format(upTo) 100 | while len(stack) <= upTo: 101 | stack.append(emptyRef()) 102 | 103 | def printStackInfo(stack): 104 | print "[stack] size = {}".format(len(stack)) 105 | 106 | def isBuiltIn(sym): 107 | for ftype in primitives: 108 | if sym["val"] in primitives[ftype]: 109 | return ftype 110 | return "" 111 | 112 | def dataRefToAbsoluteRef(payload, payload_base, item): 113 | print "Resolving data item: {}".format(item) 114 | which = item["val"] 115 | data_region_offset = payload["data_begins"] * 4 116 | data_offset = payload["data_locs"][which] * 4 117 | data_location = payload_base + data_region_offset + data_offset 118 | return absoluteDataRef(getImmRef(data_location, "constant_hexadecimal", None)) 119 | 120 | def precompile_payload(actions, symtable, sequences, DEBUG=False): 121 | ''' 122 | Given a bunch of ROP gadgets, compile a payload (or attempt to.) 123 | 124 | actions - The actions the program has requested. 125 | symtable - The symtable for this set of actions. 126 | 127 | ''' 128 | # TODO: Resolve primitives of gadgets. 129 | # TODO: Convert local vars into register assignment. 130 | # TODO: register spilling 131 | # TODO: determine basic block side-effects. 132 | # TODO: Given primitives + register assignments, compile payload. 133 | 134 | stack = [] # eventually, a high level form of the compiled payload. 0 is the lowest address in memory. 135 | data = [] # data units to be held at the top of the stack. 136 | 137 | sp = 0 # an index into stack. 138 | intermediateExprs = [] # a set of assignment actions to perform, prior to a call. 139 | 140 | regTable = {} # a stateful map of register> 141 | varTable = {} # a stateful map of stack slot> 142 | dataTable = {} # a stateful map of idx> 143 | 144 | # index of the next gadget 145 | next_gadget = 0 146 | stack.append(emptyAddrRef()) # for the first gadget 147 | stack.append(emptyAddrRef()) # for the second gadget 148 | 149 | for idx, action in enumerate(actions): 150 | if unusedOrConstant(action): 151 | # Skip variable allocation for variables that are unused / constant. 152 | print "[compile] skipping {}".format(rts(action)) 153 | continue 154 | if action["type"] == "action": 155 | if action["action"] == "bind": 156 | # Wait until an application to bind 157 | print "[compile] saving {}".format(rts(action)) 158 | if isImm(action["rvalue"]) and action["rvalue"]["dtype"] == "constant_string": 159 | varsym = action["sym"] 160 | print "[compile] Storing str value {} in data portion of stack.".format(varsym) 161 | data.append(action["rvalue"]["val"]) 162 | dataTable[varsym["val"]] = len(data) - 1 163 | else: 164 | intermediateExprs.append(action) 165 | elif action["action"] == "apply": 166 | # See if anything needs to be bound 167 | print "[compile] processing {}".format(rts(action)) 168 | if intermediateExprs: 169 | print "[compile] expression {} required {} intermediate bindings.".format(rts(action), len(intermediateExprs)) 170 | for idx, expr in enumerate(intermediateExprs): 171 | print "[{}]: {}".format(idx, rts(expr)) 172 | intermediateExprs = [] 173 | 174 | reserve(stack, next_gadget + 1 + len(action["args"])) 175 | ftype = isBuiltIn(action["sym"]) 176 | if not ftype: 177 | stack[next_gadget] = sym2retaddr(action["sym"]) # addr to call. 178 | if action["args"]: 179 | argbase = next_gadget + 2 180 | for idx, arg in enumerate(action["args"]): 181 | if arg["type"] == "sym" and arg["val"] in dataTable: 182 | print "[compile] Resolving str variable {}".format(rts(arg)) 183 | print "[compile] * stored at payload idx {}".format(argbase + idx) 184 | stack[argbase + idx] = dataRef(dataTable[arg["val"]]) 185 | else: 186 | stack[argbase + idx] = sym2arg(arg, getImmRef(idx, "constant_numerical", None)) # args in reversed order 187 | next_gadget = next_gadget + 1 188 | # request ESP lift. 189 | stack[next_gadget] = esplift(len(action["args"])) 190 | next_gadget = next_gadget + len(action["args"]) + 1 191 | else: 192 | # no ESP lift required. Just tick forward to the next gadget. 193 | next_gadget = next_gadget + 1 194 | else: 195 | # built in! see what we got. 196 | if ftype == "std": 197 | reserve(stack, next_gadget + 1) # no arguments. 198 | # the 'crop' standard functions. 199 | f_args = action["args"] 200 | if action["sym"]["val"] == "mem_write": 201 | stack[next_gadget] = dataWriteRef(f_args[0], f_args[1]) 202 | next_gadget = next_gadget + 1 203 | printStackInfo(stack) 204 | print "Next gadget: {}".format(next_gadget) 205 | # Concatenate data region onto the stack. 206 | begin_data = len(stack) 207 | 208 | var_locations = [] 209 | cur_location = 0 210 | 211 | for val in data: 212 | var_locations.append(cur_location) 213 | var_words = str2words(val, 4) 214 | stack.extend(map(lambda v: imm2constRef(getImmRef(v, "constant_string", None)), var_words)) 215 | cur_location += len(var_words) 216 | return {"data_begins" : begin_data, "stack" : stack, "data" : data, "data_table" : dataTable, "data_locs" : var_locations} 217 | 218 | def find_esp_lift(gadgets, at_least): 219 | # Look for all ESP lifts of atleast 'at_least'. 220 | candidates = filter(lambda g: g["AMT"] > at_least, gadgets) 221 | # sort the list so that the most appropriate candidate is first. 222 | # that is, the ESP lift which is closest to being 'at_least'. 223 | candidates = sorted(candidates, key=itemgetter('AMT')) 224 | return candidates 225 | 226 | def insert_padding(payload, at, amt): 227 | # Inserts number of padding references into the payload at index . 228 | for _ in range(amt): payload.insert(at, emptyPaddingRef()) 229 | 230 | def resolveESPlifts(payload, gadgets, DEBUG=True): 231 | stack_payload = payload["stack"] 232 | i = 0 233 | while i < len(stack_payload): 234 | item = stack_payload[i] 235 | if item["roptype"] == "ESP_LFT": 236 | at_least = item["ropdata"]["val"] 237 | candidates = find_esp_lift(gadgets, at_least) 238 | if not candidates: 239 | raise ROPCompilationError("No candidate sequence for {}".format(rtsse_short(item))) 240 | else: 241 | # pick the first candidate. 242 | lift = candidates[0] 243 | overshoot = lift["AMT"] - at_least 244 | if overshoot: 245 | print "ESP Lift too large ({} > {}). Requires {} padding.".format(lift["AMT"], at_least, overshoot) 246 | # arguments will finish after "at_least", so we'll want to insert there. 247 | insert_padding(stack_payload, i+at_least+1, overshoot) 248 | # Patch in the correct address. 249 | stack_payload[i] = stack_entry_gadget("ESP_LIFT", lift["vaddr"]) 250 | payload["data_begins"] += overshoot 251 | i = i + 1 252 | 253 | def resolveVars(payload, gadgets, DEBUG=True): 254 | pass 255 | 256 | def resolveDataRefs(payload, stack_base): 257 | stack_payload = payload["stack"] 258 | i = 0 259 | while i < len(stack_payload): 260 | item = stack_payload[i] 261 | if item["roptype"] == "DATA": 262 | # patch in a data reference. 263 | print "[compiler] Patching data reference @ position {}".format(i) 264 | stack_payload[i] = dataRefToAbsoluteRef(payload, stack_base, item) 265 | i = i + 1 266 | 267 | def compile_payload(payload, gadgets, buffer_base, DEBUG=True): 268 | ''' 269 | Given the previous stage compilation, and the available gadgets, compile 270 | a payload. 271 | ''' 272 | # resolve all ESP lifts. 273 | resolveESPlifts(payload, gadgets["esp_lift"], DEBUG=DEBUG) 274 | 275 | # resolve all variables that needed additional setup. 276 | resolveVars(payload, gadgets, DEBUG=DEBUG) 277 | 278 | # resolve all references to data. 279 | resolveDataRefs(payload, buffer_base) 280 | 281 | return payload 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /ropgadget/__init__.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | import ropgadget.args 10 | import ropgadget.binary 11 | import ropgadget.core 12 | import ropgadget.gadgets 13 | import ropgadget.options 14 | import ropgadget.rgutils 15 | import ropgadget.updateAlert 16 | import ropgadget.version 17 | import ropgadget.loaders 18 | import ropgadget.ropchain 19 | 20 | def main(): 21 | import sys 22 | from ropgadget.args import Args 23 | from ropgadget.core import Core 24 | sys.exit(Core(Args().getArgs()).analyze()) 25 | -------------------------------------------------------------------------------- /ropgadget/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/__init__.pyc -------------------------------------------------------------------------------- /ropgadget/args.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | import argparse 10 | import sys 11 | 12 | from ropgadget.updateAlert import UpdateAlert 13 | from ropgadget.version import * 14 | 15 | class Args: 16 | def __init__(self, arguments=None): 17 | self.__args = None 18 | custom_arguments_provided = True 19 | 20 | # If no custom arguments are provided, use the program arguments 21 | if not arguments: 22 | arguments = sys.argv[1:] 23 | custom_arguments_provided = False 24 | 25 | 26 | self.__parse(arguments, custom_arguments_provided) 27 | 28 | def __parse(self, arguments, custom_arguments_provided=False): 29 | parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, 30 | description="""description: 31 | ROPgadget lets you search your gadgets on a binary. It supports several 32 | file formats and architectures and uses the Capstone disassembler for 33 | the search engine. 34 | 35 | formats supported: 36 | - ELF 37 | - PE 38 | - Mach-O 39 | - Raw 40 | 41 | architectures supported: 42 | - x86 43 | - x86-64 44 | - ARM 45 | - ARM64 46 | - MIPS 47 | - PowerPC 48 | - Sparc 49 | """, 50 | epilog="""examples: 51 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 52 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --ropchain 53 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --depth 3 54 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "main" 55 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n" 56 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3 57 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret" 58 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret" 59 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub" 60 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys 61 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000 62 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba 63 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh" 64 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --console 65 | ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|7f|42" 66 | ROPgadget.py --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000 67 | ROPgadget.py --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5 68 | ROPgadget.py --binary ./test-suite-binaries/elf-ARM64-bash --depth 5 69 | ROPgadget.py --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32""") 70 | 71 | parser.add_argument("-v", "--version", action="store_true", help="Display the ROPgadget's version") 72 | parser.add_argument("-c", "--checkUpdate", action="store_true", help="Checks if a new version is available") 73 | parser.add_argument("--binary", type=str, metavar="", help="Specify a binary filename to analyze") 74 | parser.add_argument("--opcode", type=str, metavar="", help="Search opcode in executable segment") 75 | parser.add_argument("--string", type=str, metavar="", help="Search string in readable segment") 76 | parser.add_argument("--memstr", type=str, metavar="", help="Search each byte in all readable segment") 77 | parser.add_argument("--depth", type=int, metavar="", default=10, help="Depth for search engine (default 10)") 78 | parser.add_argument("--only", type=str, metavar="", help="Only show specific instructions") 79 | parser.add_argument("--filter", type=str, metavar="", help="Suppress specific instructions") 80 | parser.add_argument("--range", type=str, metavar="", default="0x0-0x0", help="Search between two addresses (0x...-0x...)") 81 | parser.add_argument("--badbytes", type=str, metavar="", help="Rejects specific bytes in the gadget's address") 82 | parser.add_argument("--rawArch", type=str, metavar="", help="Specify an arch for a raw file") 83 | parser.add_argument("--rawMode", type=str, metavar="", help="Specify a mode for a raw file") 84 | parser.add_argument("--re", type=str, metavar="", help="Regular expression") 85 | parser.add_argument("--offset", type=str, metavar="", help="Specify an offset for gadget addresses") 86 | parser.add_argument("--ropchain", action="store_true", help="Enable the ROP chain generation") 87 | parser.add_argument("--thumb" , action="store_true", help="Use the thumb mode for the search engine (ARM only)") 88 | parser.add_argument("--console", action="store_true", help="Use an interactive console for search engine") 89 | parser.add_argument("--norop", action="store_true", help="Disable ROP search engine") 90 | parser.add_argument("--nojop", action="store_true", help="Disable JOP search engine") 91 | parser.add_argument("--callPreceded", action="store_true", help="Only show gadgets which are call-preceded") 92 | parser.add_argument("--nosys", action="store_true", help="Disable SYS search engine") 93 | parser.add_argument("--multibr", action="store_true", help="Enable multiple branch gadgets") 94 | parser.add_argument("--all", action="store_true", help="Disables the removal of duplicate gadgets") 95 | parser.add_argument("--dump", action="store_true", help="Outputs the gadget bytes") 96 | 97 | self.__args = parser.parse_args(arguments) 98 | 99 | if self.__args.version: 100 | self.__printVersion() 101 | sys.exit(0) 102 | 103 | elif self.__args.checkUpdate: 104 | UpdateAlert().checkUpdate() 105 | sys.exit(0) 106 | 107 | elif self.__args.depth < 2: 108 | print("[Error] The depth must be >= 2") 109 | sys.exit(-1) 110 | 111 | elif not custom_arguments_provided and not self.__args.binary and not self.__args.console: 112 | print("[Error] Need a binary filename (--binary/--console or --help)") 113 | sys.exit(-1) 114 | 115 | elif self.__args.range: 116 | try: 117 | rangeS = int(self.__args.range.split('-')[0], 16) 118 | rangeE = int(self.__args.range.split('-')[1], 16) 119 | except: 120 | print("[Error] A range must be set in hexadecimal. Ex: 0x08041000-0x08042000") 121 | sys.exit(-1) 122 | if rangeS > rangeE: 123 | print("[Error] The start value must be greater than end value") 124 | sys.exit(-1) 125 | 126 | def __printVersion(self): 127 | print("Version: %s" %(PYROPGADGET_VERSION)) 128 | print("Author: Jonathan Salwan" ) 129 | print("Author page: https://twitter.com/JonathanSalwan" ) 130 | print("Project page: http://shell-storm.org/project/ROPgadget/" ) 131 | 132 | def getArgs(self): 133 | return self.__args 134 | 135 | -------------------------------------------------------------------------------- /ropgadget/args.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/args.pyc -------------------------------------------------------------------------------- /ropgadget/binary.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | from ropgadget.loaders.elf import * 10 | from ropgadget.loaders.pe import * 11 | from ropgadget.loaders.raw import * 12 | from ropgadget.loaders.macho import * 13 | from ropgadget.loaders.universal import * 14 | from binascii import unhexlify 15 | 16 | class Binary: 17 | def __init__(self, options): 18 | self.__fileName = options.binary 19 | self.__rawBinary = None 20 | self.__binary = None 21 | 22 | try: 23 | fd = open(self.__fileName, "rb") 24 | self.__rawBinary = fd.read() 25 | fd.close() 26 | except: 27 | print("[Error] Can't open the binary or binary not found") 28 | return None 29 | 30 | if options.rawArch and options.rawMode: 31 | self.__binary = Raw(self.__rawBinary, options.rawArch, options.rawMode) 32 | elif self.__rawBinary[:4] == unhexlify(b"7f454c46"): 33 | self.__binary = ELF(self.__rawBinary) 34 | elif self.__rawBinary[:2] == unhexlify(b"4d5a"): 35 | self.__binary = PE(self.__rawBinary) 36 | elif self.__rawBinary[:4] == unhexlify(b"cafebabe"): 37 | self.__binary = UNIVERSAL(self.__rawBinary) 38 | elif self.__rawBinary[:4] == unhexlify(b"cefaedfe") or self.__rawBinary[:4] == unhexlify(b"cffaedfe"): 39 | self.__binary = MACHO(self.__rawBinary) 40 | else: 41 | print("[Error] Binary format not supported") 42 | return None 43 | 44 | def getFileName(self): 45 | return self.__fileName 46 | 47 | def getRawBinary(self): 48 | return self.__rawBinary 49 | 50 | def getBinary(self): 51 | return self.__binary 52 | 53 | def getEntryPoint(self): 54 | return self.__binary.getEntryPoint() 55 | 56 | def getDataSections(self): 57 | return self.__binary.getDataSections() 58 | 59 | def getExecSections(self): 60 | return self.__binary.getExecSections() 61 | 62 | def getArch(self): 63 | return self.__binary.getArch() 64 | 65 | def getArchMode(self): 66 | return self.__binary.getArchMode() 67 | 68 | def getFormat(self): 69 | return self.__binary.getFormat() 70 | 71 | -------------------------------------------------------------------------------- /ropgadget/binary.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/binary.pyc -------------------------------------------------------------------------------- /ropgadget/core.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-17 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | import cmd 10 | import os 11 | import re 12 | import codecs 13 | import ropgadget.rgutils as rgutils 14 | import sqlite3 15 | 16 | from ropgadget.binary import Binary 17 | from capstone import CS_MODE_32 18 | from ropgadget.gadgets import Gadgets 19 | from ropgadget.options import Options 20 | from ropgadget.ropchain.ropmaker import ROPMaker 21 | 22 | class Core(cmd.Cmd): 23 | def __init__(self, options): 24 | cmd.Cmd.__init__(self) 25 | self.__options = options 26 | self.__binary = None 27 | self.__gadgets = [] 28 | self.__offset = 0 29 | self.prompt = '(ROPgadget)> ' 30 | 31 | 32 | def __checksBeforeManipulations(self): 33 | if self.__binary == None or self.__binary.getBinary() == None or self.__binary.getArch() == None or self.__binary.getArchMode() == None: 34 | return False 35 | return True 36 | 37 | def __getAllgadgets(self): 38 | 39 | if self.__checksBeforeManipulations() == False: 40 | return False 41 | 42 | G = Gadgets(self.__binary, self.__options, self.__offset) 43 | execSections = self.__binary.getExecSections() 44 | 45 | # Find ROP/JOP/SYS gadgets 46 | self.__gadgets = [] 47 | for section in execSections: 48 | if not self.__options.norop: self.__gadgets += G.addROPGadgets(section) 49 | if not self.__options.nojop: self.__gadgets += G.addJOPGadgets(section) 50 | if not self.__options.nosys: self.__gadgets += G.addSYSGadgets(section) 51 | 52 | # Pass clean single instruction and unknown instructions 53 | self.__gadgets = G.passClean(self.__gadgets, self.__options.multibr) 54 | 55 | # Delete duplicate gadgets 56 | if not self.__options.all: 57 | self.__gadgets = rgutils.deleteDuplicateGadgets(self.__gadgets) 58 | 59 | # Applicate some Options 60 | self.__gadgets = Options(self.__options, self.__binary, self.__gadgets).getGadgets() 61 | 62 | # Sorted alphabetically 63 | self.__gadgets = rgutils.alphaSortgadgets(self.__gadgets) 64 | 65 | return True 66 | 67 | def getGadgetsQuiet(self): 68 | self.__binary = Binary(self.__options) 69 | self.__getAllgadgets() 70 | return self.__gadgets 71 | 72 | def __lookingForGadgets(self): 73 | 74 | if self.__checksBeforeManipulations() == False: 75 | return False 76 | 77 | arch = self.__binary.getArchMode() 78 | print("Gadgets information\n============================================================") 79 | for gadget in self.__gadgets: 80 | vaddr = gadget["vaddr"] 81 | insts = gadget["gadget"] 82 | bytes = gadget["bytes"] 83 | bytesStr = " // " + bytes.encode('hex') if self.__options.dump else "" 84 | 85 | print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts) + bytesStr) 86 | 87 | print("\nUnique gadgets found: %d" %(len(self.__gadgets))) 88 | return True 89 | 90 | 91 | def __lookingForAString(self, string): 92 | 93 | if self.__checksBeforeManipulations() == False: 94 | return False 95 | 96 | dataSections = self.__binary.getDataSections() 97 | arch = self.__binary.getArchMode() 98 | print("Strings information\n============================================================") 99 | for section in dataSections: 100 | allRef = [m.start() for m in re.finditer(string, section["opcodes"])] 101 | for ref in allRef: 102 | vaddr = self.__offset + section["vaddr"] + ref 103 | string = section["opcodes"][ref:ref+len(string)] 104 | rangeS = int(self.__options.range.split('-')[0], 16) 105 | rangeE = int(self.__options.range.split('-')[1], 16) 106 | if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE): 107 | print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(string)) 108 | return True 109 | 110 | 111 | def __lookingForOpcodes(self, opcodes): 112 | 113 | if self.__checksBeforeManipulations() == False: 114 | return False 115 | 116 | execSections = self.__binary.getExecSections() 117 | arch = self.__binary.getArchMode() 118 | print("Opcodes information\n============================================================") 119 | for section in execSections: 120 | allRef = [m.start() for m in re.finditer(opcodes.decode("hex"), section["opcodes"])] 121 | for ref in allRef: 122 | vaddr = self.__offset + section["vaddr"] + ref 123 | rangeS = int(self.__options.range.split('-')[0], 16) 124 | rangeE = int(self.__options.range.split('-')[1], 16) 125 | if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE): 126 | print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(opcodes)) 127 | return True 128 | 129 | 130 | def __lookingForMemStr(self, memstr): 131 | 132 | if self.__checksBeforeManipulations() == False: 133 | return False 134 | 135 | sections = self.__binary.getExecSections() 136 | sections += self.__binary.getDataSections() 137 | arch = self.__binary.getArchMode() 138 | print("Memory bytes information\n=======================================================") 139 | chars = list(memstr) 140 | for char in chars: 141 | try: 142 | for section in sections: 143 | allRef = [m.start() for m in re.finditer(char, section["opcodes"])] 144 | for ref in allRef: 145 | vaddr = self.__offset + section["vaddr"] + ref 146 | rangeS = int(self.__options.range.split('-')[0], 16) 147 | rangeE = int(self.__options.range.split('-')[1], 16) 148 | if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE): 149 | print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : '%c'" %(char)) 150 | raise 151 | except: 152 | pass 153 | return True 154 | 155 | 156 | def analyze(self): 157 | 158 | try: 159 | self.__offset = int(self.__options.offset, 16) if self.__options.offset else 0 160 | except ValueError: 161 | print("[Error] The offset must be in hexadecimal") 162 | return False 163 | 164 | if self.__options.console: 165 | if self.__options.binary: 166 | self.__binary = Binary(self.__options) 167 | if self.__checksBeforeManipulations() == False: 168 | return False 169 | self.cmdloop() 170 | return True 171 | 172 | self.__binary = Binary(self.__options) 173 | if self.__checksBeforeManipulations() == False: 174 | return False 175 | 176 | if self.__options.string: return self.__lookingForAString(self.__options.string) 177 | elif self.__options.opcode: return self.__lookingForOpcodes(self.__options.opcode) 178 | elif self.__options.memstr: return self.__lookingForMemStr(self.__options.memstr) 179 | else: 180 | self.__getAllgadgets() 181 | self.__lookingForGadgets() 182 | if self.__options.ropchain: 183 | ROPMaker(self.__binary, self.__gadgets, self.__offset) 184 | return True 185 | 186 | 187 | def gadgets(self): 188 | return self.__gadgets 189 | 190 | 191 | 192 | 193 | # Console methods ============================================ 194 | 195 | def do_binary(self, s, silent=False): 196 | # Do not split the filename with spaces since it might contain 197 | # whitespaces 198 | if len(s) == 0: 199 | if not silent: 200 | return self.help_binary() 201 | return False 202 | 203 | binary = s 204 | 205 | self.__options.binary = binary 206 | self.__binary = Binary(self.__options) 207 | if self.__checksBeforeManipulations() == False: 208 | return False 209 | 210 | if not silent: 211 | print("[+] Binary loaded") 212 | 213 | 214 | def help_binary(self): 215 | print("Syntax: binary -- Load a binary") 216 | return False 217 | 218 | 219 | def do_EOF(self, s, silent=False): 220 | return self.do_quit(s, silent) 221 | 222 | def do_quit(self, s, silent=False): 223 | return True 224 | 225 | 226 | def help_quit(self): 227 | print("Syntax: quit -- Terminates the application") 228 | return False 229 | 230 | 231 | def do_load(self, s, silent=False): 232 | 233 | if self.__binary == None: 234 | if not silent: 235 | print("[-] No binary loaded.") 236 | return False 237 | 238 | if not silent: 239 | print("[+] Loading gadgets, please wait...") 240 | self.__getAllgadgets() 241 | 242 | if not silent: 243 | print("[+] Gadgets loaded !") 244 | 245 | 246 | def help_load(self): 247 | print("Syntax: load -- Load all gadgets") 248 | return False 249 | 250 | 251 | def do_display(self, s, silent=False): 252 | self.__lookingForGadgets() 253 | 254 | 255 | def help_display(self): 256 | print("Syntax: display -- Display all gadgets loaded") 257 | return False 258 | 259 | 260 | def do_depth(self, s, silent=False): 261 | try: 262 | depth = int(s.split()[0]) 263 | except: 264 | if not silent: 265 | return self.help_depth() 266 | return False 267 | if depth <= 0: 268 | if not silent: 269 | print("[-] The depth value must be > 0") 270 | return False 271 | self.__options.depth = int(depth) 272 | 273 | if not silent: 274 | print("[+] Depth updated. You have to reload gadgets") 275 | 276 | 277 | def help_depth(self): 278 | print("Syntax: depth -- Set the depth search engine") 279 | return False 280 | 281 | 282 | def do_badbytes(self, s, silent=False): 283 | try: 284 | bb = s.split()[0] 285 | except: 286 | if not silent: 287 | return self.help_badbytes() 288 | else: 289 | return False 290 | self.__options.badbytes = bb 291 | 292 | if not silent: 293 | print("[+] Bad bytes updated. You have to reload gadgets") 294 | 295 | 296 | def help_badbytes(self): 297 | print("Syntax: badbytes -- ") 298 | return False 299 | 300 | 301 | def __withK(self, listK, gadget): 302 | if len(listK) == 0: 303 | return True 304 | for a in listK: 305 | if a not in gadget: 306 | return False 307 | return True 308 | 309 | def __withoutK(self, listK, gadget): 310 | for a in listK: 311 | if a in gadget: 312 | return False 313 | return True 314 | 315 | def do_search(self, s, silent=False): 316 | args = s.split() 317 | if not len(args): 318 | return self.help_search() 319 | withK, withoutK = [], [] 320 | for a in args: 321 | if a[0:1] == "!": 322 | withoutK += [a[1:]] 323 | else: 324 | withK += [a] 325 | if self.__checksBeforeManipulations() == False: 326 | if not silent: 327 | print("[-] You have to load a binary") 328 | return False 329 | arch = self.__binary.getArchMode() 330 | for gadget in self.__gadgets: 331 | vaddr = gadget["vaddr"] 332 | insts = gadget["gadget"] 333 | if self.__withK(withK, insts) and self.__withoutK(withoutK, insts): 334 | # What to do if silent = True? 335 | print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts)) 336 | 337 | 338 | def help_search(self): 339 | print("Syntax: search -- Filter with or without keywords") 340 | print("keyword = with") 341 | print("!keyword = witout") 342 | return False 343 | 344 | 345 | def count(self): 346 | return len(self.__gadgets) 347 | 348 | def do_count(self, s, silent=False): 349 | if not silent: 350 | print("[+] %d loaded gadgets." % self.count()) 351 | 352 | 353 | def help_count(self): 354 | print("Shows the number of loaded gadgets.") 355 | return False 356 | 357 | 358 | def do_filter(self, s, silent=False): 359 | try: 360 | self.__options.filter = s.split()[0] 361 | except: 362 | if not silent: 363 | return self.help_filter() 364 | return False 365 | 366 | if not silent: 367 | print("[+] Filter setted. You have to reload gadgets") 368 | 369 | 370 | def help_filter(self): 371 | print("Syntax: filter - Suppress specific instructions") 372 | return False 373 | 374 | 375 | def do_only(self, s, silent=False): 376 | try: 377 | if s.lower() == "none": 378 | self.__options.only = None 379 | else: 380 | self.__options.only = s.split()[0] 381 | except: 382 | if not silent: 383 | return self.help_only() 384 | return False 385 | 386 | if not silent: 387 | print("[+] Only setted. You have to reload gadgets") 388 | 389 | 390 | def help_only(self): 391 | print("Syntax: only - Only show specific instructions") 392 | return False 393 | 394 | 395 | def do_range(self, s, silent=False): 396 | try: 397 | rangeS = int(s.split('-')[0], 16) 398 | rangeE = int(s.split('-')[1], 16) 399 | self.__options.range = s.split()[0] 400 | except: 401 | if not silent: 402 | return self.help_range() 403 | return False 404 | 405 | if rangeS > rangeE: 406 | if not silent: 407 | print("[-] The start value must be greater than the end value") 408 | return False 409 | 410 | if not silent: 411 | print("[+] Range setted. You have to reload gadgets") 412 | 413 | 414 | def help_range(self): 415 | print("Syntax: range - Search between two addresses (0x...-0x...)") 416 | return False 417 | 418 | 419 | def do_settings(self, s, silent=False): 420 | print("All: %s" %(self.__options.all)) 421 | print("Badbytes: %s" %(self.__options.badbytes)) 422 | print("Binary: %s" %(self.__options.binary)) 423 | print("Depth: %s" %(self.__options.depth)) 424 | print("Filter: %s" %(self.__options.filter)) 425 | print("Memstr: %s" %(self.__options.memstr)) 426 | print("MultiBr: %s" %(self.__options.multibr)) 427 | print("NoJOP: %s" %(self.__options.nojop)) 428 | print("NoROP: %s" %(self.__options.norop)) 429 | print("NoSYS: %s" %(self.__options.nosys)) 430 | print("Offset: %s" %(self.__options.offset)) 431 | print("Only: %s" %(self.__options.only)) 432 | print("Opcode: %s" %(self.__options.opcode)) 433 | print("ROPchain: %s" %(self.__options.ropchain)) 434 | print("Range: %s" %(self.__options.range)) 435 | print("RawArch: %s" %(self.__options.rawArch)) 436 | print("RawMode: %s" %(self.__options.rawMode)) 437 | print("Re: %s" %(self.__options.re)) 438 | print("String: %s" %(self.__options.string)) 439 | print("Thumb: %s" %(self.__options.thumb)) 440 | 441 | def help_settings(self): 442 | print("Display setting's environment") 443 | return False 444 | 445 | 446 | def do_nojop(self, s, silent=False): 447 | try: 448 | arg = s.split()[0] 449 | except: 450 | return self.help_nojop() 451 | 452 | if arg == "enable": 453 | self.__options.nojop = True 454 | if not silent: 455 | print("[+] NoJOP enable. You have to reload gadgets") 456 | 457 | elif arg == "disable": 458 | self.__options.nojop = False 459 | if not silent: 460 | print("[+] NoJOP disable. You have to reload gadgets") 461 | 462 | else: 463 | if not silent: 464 | return self.help_nojop() 465 | return False 466 | 467 | 468 | def help_nojop(self): 469 | print("Syntax: nojop - Disable JOP search engin") 470 | return False 471 | 472 | 473 | def do_norop(self, s, silent=False): 474 | try: 475 | arg = s.split()[0] 476 | except: 477 | return self.help_norop() 478 | 479 | if arg == "enable": 480 | self.__options.norop = True 481 | if not silent: 482 | print("[+] NoROP enable. You have to reload gadgets") 483 | 484 | elif arg == "disable": 485 | self.__options.norop = False 486 | if not silent: 487 | print("[+] NoROP disable. You have to reload gadgets") 488 | 489 | else: 490 | if not silent: 491 | return self.help_norop() 492 | return False 493 | 494 | 495 | def help_norop(self): 496 | print("Syntax: norop - Disable ROP search engin") 497 | return False 498 | 499 | 500 | def do_nosys(self, s, silent=False): 501 | try: 502 | arg = s.split()[0] 503 | except: 504 | return self.help_nosys() 505 | 506 | if arg == "enable": 507 | self.__options.nosys = True 508 | if not silent: 509 | print("[+] NoSYS enable. You have to reload gadgets") 510 | 511 | elif arg == "disable": 512 | self.__options.nosys = False 513 | if not silent: 514 | print("[+] NoSYS disable. You have to reload gadgets") 515 | 516 | else: 517 | if not silent: 518 | return self.help_nosys() 519 | 520 | return False 521 | 522 | 523 | def help_nosys(self): 524 | print("Syntax: nosys - Disable SYS search engin") 525 | return False 526 | 527 | 528 | def do_thumb(self, s, silent=False): 529 | try: 530 | arg = s.split()[0] 531 | except: 532 | return self.help_thumb() 533 | 534 | if arg == "enable": 535 | self.__options.thumb = True 536 | if not silent: 537 | print("[+] Thumb enable. You have to reload gadgets") 538 | 539 | elif arg == "disable": 540 | self.__options.thumb = False 541 | if not silent: 542 | print("[+] Thumb disable. You have to reload gadgets") 543 | 544 | else: 545 | if not silent: 546 | return self.help_thumb() 547 | return False 548 | 549 | 550 | def help_thumb(self): 551 | print("Syntax: thumb - Use the thumb mode for the search engine (ARM only)") 552 | return False 553 | 554 | 555 | def do_all(self, s, silent=False): 556 | if s == "enable": 557 | self.__options.all = True 558 | if not silent: 559 | print("[+] Showing all gadgets enabled. You have to reload gadgets") 560 | 561 | elif s == "disable": 562 | self.__options.all = False 563 | if not silent: 564 | print("[+] Showing all gadgets disabled. You have to reload gadgets") 565 | 566 | else: 567 | if not silent: 568 | return self.help_all() 569 | 570 | return False 571 | 572 | 573 | def help_multibr(self): 574 | print("Syntax: multibr - Enable/Disable multiple branch gadgets") 575 | return False 576 | 577 | 578 | def do_multibr(self, s, silent=False): 579 | if s == "enable": 580 | self.__options.multibr = True 581 | if not silent: 582 | print("[+] Multiple branch gadgets enabled. You have to reload gadgets") 583 | 584 | elif s == "disable": 585 | self.__options.multibr = False 586 | if not silent: 587 | print("[+] Multiple branch gadgets disabled. You have to reload gadgets") 588 | 589 | else: 590 | if not silent: 591 | return self.help_all() 592 | 593 | return False 594 | 595 | 596 | def help_all(self): 597 | print("Syntax: all - Regular expression") 603 | return False 604 | 605 | 606 | def do_re(self, s, silent=False): 607 | if s.lower() == 'none': 608 | self.__options.re = None 609 | elif s == "": 610 | self.help_re() 611 | silent = True 612 | else: 613 | self.__options.re = s 614 | 615 | if not silent: 616 | print("[+] Re setted. You have to reload gadgets") 617 | -------------------------------------------------------------------------------- /ropgadget/core.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/core.pyc -------------------------------------------------------------------------------- /ropgadget/gadgets.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | import re 10 | from capstone import * 11 | 12 | 13 | class Gadgets: 14 | def __init__(self, binary, options, offset): 15 | self.__binary = binary 16 | self.__options = options 17 | self.__offset = offset 18 | 19 | 20 | def __checkInstructionBlackListedX86(self, insts): 21 | bl = ["db", "int3"] 22 | for inst in insts: 23 | for b in bl: 24 | if inst.split(" ")[0] == b: 25 | return True 26 | return False 27 | 28 | def __checkMultiBr(self, insts, br): 29 | count = 0 30 | for inst in insts: 31 | if inst.split()[0] in br: 32 | count += 1 33 | return count 34 | 35 | def __passCleanX86(self, gadgets, multibr=False): 36 | new = [] 37 | br = ["ret", "retf", "int", "sysenter", "jmp", "call", "syscall"] 38 | for gadget in gadgets: 39 | insts = gadget["gadget"].split(" ; ") 40 | if len(insts) == 1 and insts[0].split(" ")[0] not in br: 41 | continue 42 | if insts[-1].split(" ")[0] not in br: 43 | continue 44 | if self.__checkInstructionBlackListedX86(insts): 45 | continue 46 | if not multibr and self.__checkMultiBr(insts, br) > 1: 47 | continue 48 | if len([m.start() for m in re.finditer("ret", gadget["gadget"])]) > 1: 49 | continue 50 | new += [gadget] 51 | return new 52 | 53 | def __gadgetsFinding(self, section, gadgets, arch, mode): 54 | 55 | C_OP = 0 56 | C_SIZE = 1 57 | C_ALIGN = 2 58 | PREV_BYTES = 9 # Number of bytes prior to the gadget to store. 59 | ret = [] 60 | md = Cs(arch, mode) 61 | for gad in gadgets: 62 | allRefRet = [m.start() for m in re.finditer(gad[C_OP], section["opcodes"])] 63 | for ref in allRefRet: 64 | for i in range(self.__options.depth): 65 | if (section["vaddr"]+ref-(i*gad[C_ALIGN])) % gad[C_ALIGN] == 0: 66 | decodes = md.disasm(section["opcodes"][ref-(i*gad[C_ALIGN]):ref+gad[C_SIZE]], section["vaddr"]+ref) 67 | gadget = "" 68 | for decode in decodes: 69 | gadget += (decode.mnemonic + " " + decode.op_str + " ; ").replace(" ", " ") 70 | if re.search(gad[C_OP],decode.bytes) is None: 71 | continue 72 | if len(gadget) > 0: 73 | gadget = gadget[:-3] 74 | off = self.__offset 75 | vaddr = off+section["vaddr"]+ref-(i*gad[C_ALIGN]) 76 | prevBytesAddr = max(section["vaddr"], vaddr - PREV_BYTES) 77 | prevBytes = section["opcodes"][prevBytesAddr-section["vaddr"]:vaddr-section["vaddr"]] 78 | ret += [{"vaddr" : vaddr, "gadget" : gadget, "decodes" : decodes, "bytes": section["opcodes"][ref-(i*gad[C_ALIGN]):ref+gad[C_SIZE]], "prev": prevBytes}] 79 | return ret 80 | 81 | def addROPGadgets(self, section): 82 | 83 | arch = self.__binary.getArch() 84 | arch_mode = self.__binary.getArchMode() 85 | 86 | if arch == CS_ARCH_X86: 87 | gadgets = [ 88 | [b"\xc3", 1, 1], # ret 89 | [b"\xc2[\x00-\xff]{2}", 3, 1], # ret 90 | [b"\xcb", 1, 1], # retf 91 | [b"\xca[\x00-\xff]{2}", 3, 1], # retf 92 | # MPX 93 | [b"\xf2\xc3", 2, 1], # ret 94 | [b"\xf2\xc2[\x00-\xff]{2}", 4, 1], # ret 95 | ] 96 | 97 | elif arch == CS_ARCH_MIPS: gadgets = [] # MIPS doesn't contains RET instruction set. Only JOP gadgets 98 | elif arch == CS_ARCH_PPC: 99 | gadgets = [ 100 | [b"\x4e\x80\x00\x20", 4, 4] # blr 101 | ] 102 | arch_mode = arch_mode + CS_MODE_BIG_ENDIAN 103 | 104 | elif arch == CS_ARCH_SPARC: 105 | gadgets = [ 106 | [b"\x81\xc3\xe0\x08", 4, 4], # retl 107 | [b"\x81\xc7\xe0\x08", 4, 4], # ret 108 | [b"\x81\xe8\x00\x00", 4, 4] # restore 109 | ] 110 | arch_mode = CS_MODE_BIG_ENDIAN 111 | 112 | elif arch == CS_ARCH_ARM: gadgets = [] # ARM doesn't contains RET instruction set. Only JOP gadgets 113 | elif arch == CS_ARCH_ARM64: 114 | gadgets = [ 115 | [b"\xc0\x03\x5f\xd6", 4, 4] # ret 116 | ] 117 | arch_mode = CS_MODE_ARM 118 | 119 | else: 120 | print("Gadgets().addROPGadgets() - Architecture not supported") 121 | return None 122 | 123 | if len(gadgets) > 0 : 124 | return self.__gadgetsFinding(section, gadgets, arch, arch_mode) 125 | return gadgets 126 | 127 | 128 | def addJOPGadgets(self, section): 129 | arch = self.__binary.getArch() 130 | arch_mode = self.__binary.getArchMode() 131 | 132 | 133 | 134 | if arch == CS_ARCH_X86: 135 | gadgets = [ 136 | [b"\xff[\x20\x21\x22\x23\x26\x27]{1}", 2, 1], # jmp [reg] 137 | [b"\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 2, 1], # jmp [reg] 138 | [b"\xff[\x10\x11\x12\x13\x16\x17]{1}", 2, 1], # jmp [reg] 139 | [b"\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 2, 1], # call [reg] 140 | # MPX 141 | [b"\xf2\xff[\x20\x21\x22\x23\x26\x27]{1}", 3, 1], # jmp [reg] 142 | [b"\xf2\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 3, 1], # jmp [reg] 143 | [b"\xf2\xff[\x10\x11\x12\x13\x16\x17]{1}", 3, 1], # jmp [reg] 144 | [b"\xf2\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 3, 1] # call [reg] 145 | ] 146 | 147 | 148 | elif arch == CS_ARCH_MIPS: 149 | gadgets = [ 150 | [b"\x09\xf8\x20\x03[\x00-\xff]{4}", 8, 4], # jrl $t9 151 | [b"\x08\x00\x20\x03[\x00-\xff]{4}", 8, 4], # jr $t9 152 | [b"\x08\x00\xe0\x03[\x00-\xff]{4}", 8, 4] # jr $ra 153 | ] 154 | elif arch == CS_ARCH_PPC: gadgets = [] # PPC architecture doesn't contains reg branch instruction 155 | elif arch == CS_ARCH_SPARC: 156 | gadgets = [ 157 | [b"\x81\xc0[\x00\x40\x80\xc0]{1}\x00", 4, 4] # jmp %g[0-3] 158 | ] 159 | arch_mode = CS_MODE_BIG_ENDIAN 160 | elif arch == CS_ARCH_ARM64: 161 | gadgets = [ 162 | [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00\x01\x02]{1}\x1f\xd6", 4, 4], # br reg 163 | [b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00\x01\x02]{1}\x5C\x3f\xd6", 4, 4] # blr reg 164 | ] 165 | arch_mode = CS_MODE_ARM 166 | elif arch == CS_ARCH_ARM: 167 | if self.__options.thumb or self.__options.rawMode == "thumb": 168 | gadgets = [ 169 | [b"[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}\x47", 2, 2], # bx reg 170 | [b"[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}\x47", 2, 2], # blx reg 171 | [b"[\x00-\xff]{1}\xbd", 2, 2] # pop {,pc} 172 | ] 173 | arch_mode = CS_MODE_THUMB 174 | else: 175 | gadgets = [ 176 | [b"[\x10-\x19\x1e]{1}\xff\x2f\xe1", 4, 4], # bx reg 177 | [b"[\x30-\x39\x3e]{1}\xff\x2f\xe1", 4, 4], # blx reg 178 | [b"[\x00-\xff]{1}\x80\xbd\xe8", 4, 4] # pop {,pc} 179 | ] 180 | arch_mode = CS_MODE_ARM 181 | else: 182 | print("Gadgets().addJOPGadgets() - Architecture not supported") 183 | return None 184 | 185 | if len(gadgets) > 0 : 186 | return self.__gadgetsFinding(section, gadgets, arch, arch_mode) 187 | return gadgets 188 | 189 | def addSYSGadgets(self, section): 190 | 191 | arch = self.__binary.getArch() 192 | arch_mode = self.__binary.getArchMode() 193 | 194 | if arch == CS_ARCH_X86: 195 | gadgets = [ 196 | [b"\xcd\x80", 2, 1], # int 0x80 197 | [b"\x0f\x34", 2, 1], # sysenter 198 | [b"\x0f\x05", 2, 1], # syscall 199 | [b"\x65\xff\x15\x10\x00\x00\x00", 7, 1], # call DWORD PTR gs:0x10 200 | [b"\xcd\x80\xc3", 3, 1], # int 0x80 ; ret 201 | [b"\x0f\x34\xc3", 3, 1], # sysenter ; ret 202 | [b"\x0f\x05\xc3", 3, 1], # syscall ; ret 203 | [b"\x65\xff\x15\x10\x00\x00\x00\xc3", 8, 1], # call DWORD PTR gs:0x10 ; ret 204 | ] 205 | 206 | elif arch == CS_ARCH_MIPS: 207 | gadgets = [ 208 | [b"\x0c\x00\x00\x00", 4, 4] # syscall 209 | ] 210 | elif arch == CS_ARCH_PPC: gadgets = [] # TODO (sc inst) 211 | elif arch == CS_ARCH_SPARC: gadgets = [] # TODO (ta inst) 212 | elif arch == CS_ARCH_ARM64: gadgets = [] # TODO 213 | elif arch == CS_ARCH_ARM: 214 | if self.__options.thumb or self.__options.rawMode == "thumb": 215 | gadgets = [ 216 | [b"\x00-\xff]{1}\xef", 2, 2] # svc 217 | ] 218 | arch_mode = CS_MODE_THUMB 219 | else: 220 | gadgets = [ 221 | [b"\x00-\xff]{3}\xef", 4, 4] # svc 222 | ] 223 | arch_mode = CS_MODE_ARM 224 | else: 225 | print("Gadgets().addSYSGadgets() - Architecture not supported") 226 | return None 227 | 228 | if len(gadgets) > 0 : 229 | return self.__gadgetsFinding(section, gadgets, arch, arch_mode) 230 | return [] 231 | 232 | 233 | def passClean(self, gadgets, multibr): 234 | 235 | arch = self.__binary.getArch() 236 | if arch == CS_ARCH_X86: return self.__passCleanX86(gadgets, multibr) 237 | elif arch == CS_ARCH_MIPS: return gadgets 238 | elif arch == CS_ARCH_PPC: return gadgets 239 | elif arch == CS_ARCH_SPARC: return gadgets 240 | elif arch == CS_ARCH_ARM: return gadgets 241 | elif arch == CS_ARCH_ARM64: return gadgets 242 | else: 243 | print("Gadgets().passClean() - Architecture not supported") 244 | return None 245 | 246 | -------------------------------------------------------------------------------- /ropgadget/gadgets.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/gadgets.pyc -------------------------------------------------------------------------------- /ropgadget/loaders/__init__.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | import ropgadget.loaders.elf 10 | import ropgadget.loaders.macho 11 | import ropgadget.loaders.pe 12 | import ropgadget.loaders.raw 13 | -------------------------------------------------------------------------------- /ropgadget/loaders/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/loaders/__init__.pyc -------------------------------------------------------------------------------- /ropgadget/loaders/elf.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | from capstone import * 10 | from ctypes import * 11 | from struct import unpack 12 | 13 | class ELFFlags: 14 | ELFCLASS32 = 0x01 15 | ELFCLASS64 = 0x02 16 | EI_CLASS = 0x04 17 | EI_DATA = 0x05 18 | ELFDATA2LSB = 0x01 19 | ELFDATA2MSB = 0x02 20 | EM_386 = 0x03 21 | EM_X86_64 = 0x3e 22 | EM_ARM = 0x28 23 | EM_MIPS = 0x08 24 | EM_SPARCv8p = 0x12 25 | EM_PowerPC = 0x14 26 | EM_ARM64 = 0xb7 27 | 28 | class Elf32_Ehdr_LSB(LittleEndianStructure): 29 | _fields_ = [ 30 | ("e_ident", c_ubyte * 16), 31 | ("e_type", c_ushort), 32 | ("e_machine", c_ushort), 33 | ("e_version", c_uint), 34 | ("e_entry", c_uint), 35 | ("e_phoff", c_uint), 36 | ("e_shoff", c_uint), 37 | ("e_flags", c_uint), 38 | ("e_ehsize", c_ushort), 39 | ("e_phentsize", c_ushort), 40 | ("e_phnum", c_ushort), 41 | ("e_shentsize", c_ushort), 42 | ("e_shnum", c_ushort), 43 | ("e_shstrndx", c_ushort) 44 | ] 45 | 46 | class Elf64_Ehdr_LSB(LittleEndianStructure): 47 | _fields_ = [ 48 | ("e_ident", c_ubyte * 16), 49 | ("e_type", c_ushort), 50 | ("e_machine", c_ushort), 51 | ("e_version", c_uint), 52 | ("e_entry", c_ulonglong), 53 | ("e_phoff", c_ulonglong), 54 | ("e_shoff", c_ulonglong), 55 | ("e_flags", c_uint), 56 | ("e_ehsize", c_ushort), 57 | ("e_phentsize", c_ushort), 58 | ("e_phnum", c_ushort), 59 | ("e_shentsize", c_ushort), 60 | ("e_shnum", c_ushort), 61 | ("e_shstrndx", c_ushort) 62 | ] 63 | 64 | class Elf32_Phdr_LSB(LittleEndianStructure): 65 | _fields_ = [ 66 | ("p_type", c_uint), 67 | ("p_offset", c_uint), 68 | ("p_vaddr", c_uint), 69 | ("p_paddr", c_uint), 70 | ("p_filesz", c_uint), 71 | ("p_memsz", c_uint), 72 | ("p_flags", c_uint), 73 | ("p_align", c_uint) 74 | ] 75 | 76 | class Elf64_Phdr_LSB(LittleEndianStructure): 77 | _fields_ = [ 78 | ("p_type", c_uint), 79 | ("p_flags", c_uint), 80 | ("p_offset", c_ulonglong), 81 | ("p_vaddr", c_ulonglong), 82 | ("p_paddr", c_ulonglong), 83 | ("p_filesz", c_ulonglong), 84 | ("p_memsz", c_ulonglong), 85 | ("p_align", c_ulonglong) 86 | ] 87 | 88 | class Elf32_Shdr_LSB(LittleEndianStructure): 89 | _fields_ = [ 90 | ("sh_name", c_uint), 91 | ("sh_type", c_uint), 92 | ("sh_flags", c_uint), 93 | ("sh_addr", c_uint), 94 | ("sh_offset", c_uint), 95 | ("sh_size", c_uint), 96 | ("sh_link", c_uint), 97 | ("sh_info", c_uint), 98 | ("sh_addralign", c_uint), 99 | ("sh_entsize", c_uint) 100 | ] 101 | 102 | class Elf64_Shdr_LSB(LittleEndianStructure): 103 | _fields_ = [ 104 | ("sh_name", c_uint), 105 | ("sh_type", c_uint), 106 | ("sh_flags", c_ulonglong), 107 | ("sh_addr", c_ulonglong), 108 | ("sh_offset", c_ulonglong), 109 | ("sh_size", c_ulonglong), 110 | ("sh_link", c_uint), 111 | ("sh_info", c_uint), 112 | ("sh_addralign", c_ulonglong), 113 | ("sh_entsize", c_ulonglong) 114 | ] 115 | 116 | class Elf32_Ehdr_MSB(BigEndianStructure): 117 | _fields_ = [ 118 | ("e_ident", c_ubyte * 16), 119 | ("e_type", c_ushort), 120 | ("e_machine", c_ushort), 121 | ("e_version", c_uint), 122 | ("e_entry", c_uint), 123 | ("e_phoff", c_uint), 124 | ("e_shoff", c_uint), 125 | ("e_flags", c_uint), 126 | ("e_ehsize", c_ushort), 127 | ("e_phentsize", c_ushort), 128 | ("e_phnum", c_ushort), 129 | ("e_shentsize", c_ushort), 130 | ("e_shnum", c_ushort), 131 | ("e_shstrndx", c_ushort) 132 | ] 133 | 134 | class Elf64_Ehdr_MSB(BigEndianStructure): 135 | _fields_ = [ 136 | ("e_ident", c_ubyte * 16), 137 | ("e_type", c_ushort), 138 | ("e_machine", c_ushort), 139 | ("e_version", c_uint), 140 | ("e_entry", c_ulonglong), 141 | ("e_phoff", c_ulonglong), 142 | ("e_shoff", c_ulonglong), 143 | ("e_flags", c_uint), 144 | ("e_ehsize", c_ushort), 145 | ("e_phentsize", c_ushort), 146 | ("e_phnum", c_ushort), 147 | ("e_shentsize", c_ushort), 148 | ("e_shnum", c_ushort), 149 | ("e_shstrndx", c_ushort) 150 | ] 151 | 152 | class Elf32_Phdr_MSB(BigEndianStructure): 153 | _fields_ = [ 154 | ("p_type", c_uint), 155 | ("p_offset", c_uint), 156 | ("p_vaddr", c_uint), 157 | ("p_paddr", c_uint), 158 | ("p_filesz", c_uint), 159 | ("p_memsz", c_uint), 160 | ("p_flags", c_uint), 161 | ("p_align", c_uint) 162 | ] 163 | 164 | class Elf64_Phdr_MSB(BigEndianStructure): 165 | _fields_ = [ 166 | ("p_type", c_uint), 167 | ("p_flags", c_uint), 168 | ("p_offset", c_ulonglong), 169 | ("p_vaddr", c_ulonglong), 170 | ("p_paddr", c_ulonglong), 171 | ("p_filesz", c_ulonglong), 172 | ("p_memsz", c_ulonglong), 173 | ("p_align", c_ulonglong) 174 | ] 175 | 176 | class Elf32_Shdr_MSB(BigEndianStructure): 177 | _fields_ = [ 178 | ("sh_name", c_uint), 179 | ("sh_type", c_uint), 180 | ("sh_flags", c_uint), 181 | ("sh_addr", c_uint), 182 | ("sh_offset", c_uint), 183 | ("sh_size", c_uint), 184 | ("sh_link", c_uint), 185 | ("sh_info", c_uint), 186 | ("sh_addralign", c_uint), 187 | ("sh_entsize", c_uint) 188 | ] 189 | 190 | class Elf64_Shdr_MSB(BigEndianStructure): 191 | _fields_ = [ 192 | ("sh_name", c_uint), 193 | ("sh_type", c_uint), 194 | ("sh_flags", c_ulonglong), 195 | ("sh_addr", c_ulonglong), 196 | ("sh_offset", c_ulonglong), 197 | ("sh_size", c_ulonglong), 198 | ("sh_link", c_uint), 199 | ("sh_info", c_uint), 200 | ("sh_addralign", c_ulonglong), 201 | ("sh_entsize", c_ulonglong) 202 | ] 203 | 204 | """ This class parses the ELF """ 205 | class ELF: 206 | def __init__(self, binary): 207 | self.__binary = bytearray(binary) 208 | self.__ElfHeader = None 209 | self.__shdr_l = [] 210 | self.__phdr_l = [] 211 | 212 | self.__setHeaderElf() 213 | self.__setShdr() 214 | self.__setPhdr() 215 | 216 | """ Parse ELF header """ 217 | def __setHeaderElf(self): 218 | e_ident = self.__binary[:15] 219 | 220 | ei_class = e_ident[ELFFlags.EI_CLASS] 221 | ei_data = e_ident[ELFFlags.EI_DATA] 222 | 223 | if ei_class != ELFFlags.ELFCLASS32 and ei_class != ELFFlags.ELFCLASS64: 224 | print("[Error] ELF.__setHeaderElf() - Bad Arch size") 225 | return None 226 | 227 | if ei_data != ELFFlags.ELFDATA2LSB and ei_data != ELFFlags.ELFDATA2MSB: 228 | print("[Error] ELF.__setHeaderElf() - Bad architecture endian") 229 | return None 230 | 231 | if ei_class == ELFFlags.ELFCLASS32: 232 | if ei_data == ELFFlags.ELFDATA2LSB: self.__ElfHeader = Elf32_Ehdr_LSB.from_buffer_copy(self.__binary) 233 | elif ei_data == ELFFlags.ELFDATA2MSB: self.__ElfHeader = Elf32_Ehdr_MSB.from_buffer_copy(self.__binary) 234 | elif ei_class == ELFFlags.ELFCLASS64: 235 | if ei_data == ELFFlags.ELFDATA2LSB: self.__ElfHeader = Elf64_Ehdr_LSB.from_buffer_copy(self.__binary) 236 | elif ei_data == ELFFlags.ELFDATA2MSB: self.__ElfHeader = Elf64_Ehdr_MSB.from_buffer_copy(self.__binary) 237 | 238 | self.getArch() # Check if architecture is supported 239 | 240 | """ Parse Section header """ 241 | def __setShdr(self): 242 | shdr_num = self.__ElfHeader.e_shnum 243 | base = self.__binary[self.__ElfHeader.e_shoff:] 244 | shdr_l = [] 245 | 246 | e_ident = self.__binary[:15] 247 | ei_data = e_ident[ELFFlags.EI_DATA] 248 | 249 | for i in range(shdr_num): 250 | 251 | if self.getArchMode() == CS_MODE_32: 252 | if ei_data == ELFFlags.ELFDATA2LSB: shdr = Elf32_Shdr_LSB.from_buffer_copy(base) 253 | elif ei_data == ELFFlags.ELFDATA2MSB: shdr = Elf32_Shdr_MSB.from_buffer_copy(base) 254 | elif self.getArchMode() == CS_MODE_64: 255 | if ei_data == ELFFlags.ELFDATA2LSB: shdr = Elf64_Shdr_LSB.from_buffer_copy(base) 256 | elif ei_data == ELFFlags.ELFDATA2MSB: shdr = Elf64_Shdr_MSB.from_buffer_copy(base) 257 | 258 | self.__shdr_l.append(shdr) 259 | base = base[self.__ElfHeader.e_shentsize:] 260 | 261 | # setup name from the strings table 262 | if self.__ElfHeader.e_shstrndx != 0: 263 | string_table = str(self.__binary[(self.__shdr_l[self.__ElfHeader.e_shstrndx].sh_offset):]) 264 | for i in range(shdr_num): 265 | self.__shdr_l[i].str_name = string_table[self.__shdr_l[i].sh_name:].split('\0')[0] 266 | 267 | """ Parse Program header """ 268 | def __setPhdr(self): 269 | pdhr_num = self.__ElfHeader.e_phnum 270 | base = self.__binary[self.__ElfHeader.e_phoff:] 271 | phdr_l = [] 272 | 273 | e_ident = self.__binary[:15] 274 | ei_data = e_ident[ELFFlags.EI_DATA] 275 | 276 | for i in range(pdhr_num): 277 | if self.getArchMode() == CS_MODE_32: 278 | if ei_data == ELFFlags.ELFDATA2LSB: phdr = Elf32_Phdr_LSB.from_buffer_copy(base) 279 | elif ei_data == ELFFlags.ELFDATA2MSB: phdr = Elf32_Phdr_MSB.from_buffer_copy(base) 280 | elif self.getArchMode() == CS_MODE_64: 281 | if ei_data == ELFFlags.ELFDATA2LSB: phdr = Elf64_Phdr_LSB.from_buffer_copy(base) 282 | elif ei_data == ELFFlags.ELFDATA2MSB: phdr = Elf64_Phdr_MSB.from_buffer_copy(base) 283 | 284 | self.__phdr_l.append(phdr) 285 | base = base[self.__ElfHeader.e_phentsize:] 286 | 287 | def getEntryPoint(self): 288 | return self.__e_entry 289 | 290 | def getExecSections(self): 291 | ret = [] 292 | for segment in self.__phdr_l: 293 | if segment.p_flags & 0x1: 294 | ret += [{ 295 | "offset" : segment.p_offset, 296 | "size" : segment.p_memsz, 297 | "vaddr" : segment.p_vaddr, 298 | "opcodes" : bytes(self.__binary[segment.p_offset:segment.p_offset+segment.p_memsz]) 299 | }] 300 | return ret 301 | 302 | def getDataSections(self): 303 | ret = [] 304 | for section in self.__shdr_l: 305 | if not (section.sh_flags & 0x4) and (section.sh_flags & 0x2): 306 | ret += [{ 307 | "name" : section.str_name, 308 | "offset" : section.sh_offset, 309 | "size" : section.sh_size, 310 | "vaddr" : section.sh_addr, 311 | "opcodes" : str(self.__binary[section.sh_offset:section.sh_offset+section.sh_size]) 312 | }] 313 | return ret 314 | 315 | def getArch(self): 316 | if self.__ElfHeader.e_machine == ELFFlags.EM_386 or self.__ElfHeader.e_machine == ELFFlags.EM_X86_64: 317 | return CS_ARCH_X86 318 | elif self.__ElfHeader.e_machine == ELFFlags.EM_ARM: 319 | return CS_ARCH_ARM 320 | elif self.__ElfHeader.e_machine == ELFFlags.EM_ARM64: 321 | return CS_ARCH_ARM64 322 | elif self.__ElfHeader.e_machine == ELFFlags.EM_MIPS: 323 | return CS_ARCH_MIPS 324 | elif self.__ElfHeader.e_machine == ELFFlags.EM_PowerPC: 325 | return CS_ARCH_PPC 326 | elif self.__ElfHeader.e_machine == ELFFlags.EM_SPARCv8p: 327 | return CS_ARCH_SPARC 328 | else: 329 | print("[Error] ELF.getArch() - Architecture not supported") 330 | return None 331 | 332 | def getArchMode(self): 333 | if self.__ElfHeader.e_ident[ELFFlags.EI_CLASS] == ELFFlags.ELFCLASS32: 334 | return CS_MODE_32 335 | elif self.__ElfHeader.e_ident[ELFFlags.EI_CLASS] == ELFFlags.ELFCLASS64: 336 | return CS_MODE_64 337 | else: 338 | print("[Error] ELF.getArchMode() - Bad Arch size") 339 | return None 340 | 341 | def getFormat(self): 342 | return "ELF" 343 | 344 | -------------------------------------------------------------------------------- /ropgadget/loaders/elf.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/loaders/elf.pyc -------------------------------------------------------------------------------- /ropgadget/loaders/macho.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | from capstone import * 10 | from ctypes import * 11 | 12 | class MACH_HEADER(Structure): 13 | _fields_ = [ 14 | ("magic", c_uint), 15 | ("cputype", c_uint), 16 | ("cpusubtype", c_uint), 17 | ("filetype", c_uint), 18 | ("ncmds", c_uint), 19 | ("sizeofcmds", c_uint), 20 | ("flags", c_uint) 21 | ] 22 | 23 | class LOAD_COMMAND(Structure): 24 | _fields_ = [ 25 | ("cmd", c_uint), 26 | ("cmdsize", c_uint) 27 | ] 28 | 29 | class SEGMENT_COMMAND(Structure): 30 | _fields_ = [ 31 | ("cmd", c_uint), 32 | ("cmdsize", c_uint), 33 | ("segname", c_ubyte * 16), 34 | ("vmaddr", c_uint), 35 | ("vmsize", c_uint), 36 | ("fileoff", c_uint), 37 | ("filesize", c_uint), 38 | ("maxprot", c_uint), 39 | ("initprot", c_uint), 40 | ("nsects", c_uint), 41 | ("flags", c_uint) 42 | ] 43 | 44 | class SEGMENT_COMMAND64(Structure): 45 | _fields_ = [ 46 | ("cmd", c_uint), 47 | ("cmdsize", c_uint), 48 | ("segname", c_ubyte * 16), 49 | ("vmaddr", c_ulonglong), 50 | ("vmsize", c_ulonglong), 51 | ("fileoff", c_ulonglong), 52 | ("filesize", c_ulonglong), 53 | ("maxprot", c_uint), 54 | ("initprot", c_uint), 55 | ("nsects", c_uint), 56 | ("flags", c_uint) 57 | ] 58 | 59 | class SECTION(Structure): 60 | _fields_ = [ 61 | ("sectname", c_ubyte * 16), 62 | ("segname", c_ubyte * 16), 63 | ("addr", c_uint), 64 | ("size", c_uint), 65 | ("offset", c_uint), 66 | ("align", c_uint), 67 | ("reloff", c_uint), 68 | ("nreloc", c_uint), 69 | ("flags", c_uint), 70 | ("reserved1", c_uint), 71 | ("reserved2", c_uint) 72 | ] 73 | 74 | class SECTION64(Structure): 75 | _fields_ = [ 76 | ("sectname", c_ubyte * 16), 77 | ("segname", c_ubyte * 16), 78 | ("addr", c_ulonglong), 79 | ("size", c_ulonglong), 80 | ("offset", c_uint), 81 | ("align", c_uint), 82 | ("reloff", c_uint), 83 | ("nreloc", c_uint), 84 | ("flags", c_uint), 85 | ("reserved1", c_uint), 86 | ("reserved2", c_uint) 87 | ] 88 | 89 | class MACHOFlags: 90 | CPU_TYPE_I386 = 0x7 91 | CPU_TYPE_X86_64 = (CPU_TYPE_I386 | 0x1000000) 92 | CPU_TYPE_MIPS = 0x8 93 | CPU_TYPE_ARM = 12 94 | CPU_TYPE_ARM64 = (CPU_TYPE_ARM | 0x1000000) 95 | CPU_TYPE_SPARC = 14 96 | CPU_TYPE_POWERPC = 18 97 | CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | 0x1000000) 98 | LC_SEGMENT = 0x1 99 | LC_SEGMENT_64 = 0x19 100 | S_ATTR_SOME_INSTRUCTIONS = 0x00000400 101 | S_ATTR_PURE_INSTRUCTIONS = 0x80000000 102 | 103 | """ This class parses the Mach-O """ 104 | class MACHO: 105 | def __init__(self, binary): 106 | self.__binary = bytearray(binary) 107 | 108 | self.__machHeader = None 109 | self.__rawLoadCmd = None 110 | self.__sections_l = [] 111 | 112 | self.__setHeader() 113 | self.__setLoadCmd() 114 | 115 | def __setHeader(self): 116 | self.__machHeader = MACH_HEADER.from_buffer_copy(self.__binary) 117 | 118 | if self.getArchMode() == CS_MODE_32: 119 | self.__rawLoadCmd = self.__binary[28:28+self.__machHeader.sizeofcmds] 120 | 121 | elif self.getArchMode() == CS_MODE_64: 122 | self.__rawLoadCmd = self.__binary[32:32+self.__machHeader.sizeofcmds] 123 | 124 | def __setLoadCmd(self): 125 | base = self.__rawLoadCmd 126 | for i in range(self.__machHeader.ncmds): 127 | command = LOAD_COMMAND.from_buffer_copy(base) 128 | 129 | if command.cmd == MACHOFlags.LC_SEGMENT: 130 | segment = SEGMENT_COMMAND.from_buffer_copy(base) 131 | self.__setSections(segment, base[56:], 32) 132 | 133 | elif command.cmd == MACHOFlags.LC_SEGMENT_64: 134 | segment = SEGMENT_COMMAND64.from_buffer_copy(base) 135 | self.__setSections(segment, base[72:], 64) 136 | 137 | base = base[command.cmdsize:] 138 | 139 | def __setSections(self, segment, base, sizeHeader): 140 | for i in range(segment.nsects): 141 | if sizeHeader == 32: 142 | section = SECTION.from_buffer_copy(base) 143 | section.offset = segment.fileoff + section.addr - segment.vmaddr 144 | base = base[68:] 145 | self.__sections_l += [section] 146 | elif sizeHeader == 64: 147 | section = SECTION64.from_buffer_copy(base) 148 | section.offset = segment.fileoff + section.addr - segment.vmaddr 149 | base = base[80:] 150 | self.__sections_l += [section] 151 | 152 | def getEntryPoint(self): 153 | for section in self.__sections_l: 154 | if section.sectname[0:6] == "__text": 155 | return section.addr 156 | 157 | def getExecSections(self): 158 | ret = [] 159 | for section in self.__sections_l: 160 | if section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS or section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS: 161 | ret += [{ 162 | "name" : section.sectname, 163 | "offset" : section.offset, 164 | "size" : section.size, 165 | "vaddr" : section.addr, 166 | "opcodes" : bytes(self.__binary[section.offset:section.offset+section.size]) 167 | }] 168 | return ret 169 | 170 | def getDataSections(self): 171 | ret = [] 172 | for section in self.__sections_l: 173 | if not section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS and not section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS: 174 | ret += [{ 175 | "name" : section.sectname, 176 | "offset" : section.offset, 177 | "size" : section.size, 178 | "vaddr" : section.addr, 179 | "opcodes" : str(self.__binary[section.offset:section.offset+section.size]) 180 | }] 181 | return ret 182 | 183 | def getArch(self): 184 | if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_I386 or self.__machHeader.cputype == MACHOFlags.CPU_TYPE_X86_64: 185 | return CS_ARCH_X86 186 | if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM: 187 | return CS_ARCH_ARM 188 | if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM64: 189 | return CS_ARCH_ARM64 190 | if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_MIPS: 191 | return CS_ARCH_MIPS 192 | else: 193 | print("[Error] MACHO.getArch() - Architecture not supported") 194 | return None 195 | 196 | def getArchMode(self): 197 | if self.__machHeader.magic == 0xfeedface: 198 | return CS_MODE_32 199 | elif self.__machHeader.magic == 0xfeedfacf: 200 | return CS_MODE_64 201 | else: 202 | print("[Error] MACHO.getArchMode() - Bad Arch size") 203 | return None 204 | pass 205 | 206 | def getFormat(self): 207 | return "Mach-O" 208 | 209 | -------------------------------------------------------------------------------- /ropgadget/loaders/macho.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/loaders/macho.pyc -------------------------------------------------------------------------------- /ropgadget/loaders/pe.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | from capstone import * 10 | from ctypes import * 11 | from struct import unpack 12 | from binascii import unhexlify 13 | 14 | class PEFlags: 15 | IMAGE_MACHINE_INTEL_386 = 0x014c 16 | IMAGE_MACHINE_AMD_8664 = 0x8664 17 | IMAGE_FILE_MACHINE_ARM = 0x1c0 18 | IMAGE_FILE_MACHINE_ARMV7 = 0x1c4 19 | IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b 20 | IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b 21 | IMAGE_SIZEOF_SHORT_NAME = 0x8 22 | 23 | class IMAGE_FILE_HEADER(Structure): 24 | _fields_ = [ 25 | ("Magic", c_uint), 26 | ("Machine", c_ushort), 27 | ("NumberOfSections", c_ushort), 28 | ("TimeDateStamp", c_uint), 29 | ("PointerToSymbolTable", c_uint), 30 | ("NumberOfSymbols", c_uint), 31 | ("SizeOfOptionalHeader", c_ushort), 32 | ("Characteristics", c_ushort) 33 | ] 34 | 35 | class IMAGE_OPTIONAL_HEADER(Structure): 36 | _fields_ = [ 37 | ("Magic", c_ushort), 38 | ("MajorLinkerVersion", c_ubyte), 39 | ("MinorLinkerVersion", c_ubyte), 40 | ("SizeOfCode", c_uint), 41 | ("SizeOfInitializedData", c_uint), 42 | ("SizeOfUninitializedData", c_uint), 43 | ("AddressOfEntryPoint", c_uint), 44 | ("BaseOfCode", c_uint), 45 | ("BaseOfData", c_uint), 46 | ("ImageBase", c_uint), 47 | ("SectionAlignment", c_uint), 48 | ("FileAlignment", c_uint), 49 | ("MajorOperatingSystemVersion", c_ushort), 50 | ("MinorOperatingSystemVersion", c_ushort), 51 | ("MajorImageVersion", c_ushort), 52 | ("MinorImageVersion", c_ushort), 53 | ("MajorSubsystemVersion", c_ushort), 54 | ("MinorSubsystemVersion", c_ushort), 55 | ("Win32VersionValue", c_uint), 56 | ("SizeOfImage", c_uint), 57 | ("SizeOfHeaders", c_uint), 58 | ("CheckSum", c_uint), 59 | ("Subsystem", c_ushort), 60 | ("DllCharacteristics", c_ushort), 61 | ("SizeOfStackReserve", c_uint), 62 | ("SizeOfStackCommit", c_uint), 63 | ("SizeOfHeapReserve", c_uint), 64 | ("SizeOfHeapCommit", c_uint), 65 | ("LoaderFlags", c_uint), 66 | ("NumberOfRvaAndSizes", c_uint) 67 | ] 68 | 69 | class IMAGE_OPTIONAL_HEADER64(Structure): 70 | _fields_ = [ 71 | ("Magic", c_ushort), 72 | ("MajorLinkerVersion", c_ubyte), 73 | ("MinorLinkerVersion", c_ubyte), 74 | ("SizeOfCode", c_uint), 75 | ("SizeOfInitializedData", c_uint), 76 | ("SizeOfUninitializedData", c_uint), 77 | ("AddressOfEntryPoint", c_uint), 78 | ("BaseOfCode", c_uint), 79 | ("ImageBase", c_ulonglong), 80 | ("SectionAlignment", c_uint), 81 | ("FileAlignment", c_uint), 82 | ("MajorOperatingSystemVersion", c_ushort), 83 | ("MinorOperatingSystemVersion", c_ushort), 84 | ("MajorImageVersion", c_ushort), 85 | ("MinorImageVersion", c_ushort), 86 | ("MajorSubsystemVersion", c_ushort), 87 | ("MinorSubsystemVersion", c_ushort), 88 | ("Win32VersionValue", c_uint), 89 | ("SizeOfImage", c_uint), 90 | ("SizeOfHeaders", c_uint), 91 | ("CheckSum", c_uint), 92 | ("Subsystem", c_ushort), 93 | ("DllCharacteristics", c_ushort), 94 | ("SizeOfStackReserve", c_ulonglong), 95 | ("SizeOfStackCommit", c_ulonglong), 96 | ("SizeOfHeapReserve", c_ulonglong), 97 | ("SizeOfHeapCommit", c_ulonglong), 98 | ("LoaderFlags", c_uint), 99 | ("NumberOfRvaAndSizes", c_uint) 100 | ] 101 | 102 | class IMAGE_NT_HEADERS(Structure): 103 | _fields_ = [ 104 | ("Signature", c_uint), 105 | ("FileHeader", IMAGE_FILE_HEADER), 106 | ("OptionalHeader", IMAGE_OPTIONAL_HEADER) 107 | ] 108 | 109 | class IMAGE_NT_HEADERS64(Structure): 110 | _fields_ = [ 111 | ("Signature", c_uint), 112 | ("FileHeader", IMAGE_FILE_HEADER), 113 | ("OptionalHeader", IMAGE_OPTIONAL_HEADER64) 114 | ] 115 | 116 | class IMAGE_SECTION_HEADER(Structure): 117 | _fields_ = [ 118 | ("Name", c_ubyte * PEFlags.IMAGE_SIZEOF_SHORT_NAME), 119 | ("PhysicalAddress", c_uint), 120 | ("VirtualAddress", c_uint), 121 | ("SizeOfRawData", c_uint), 122 | ("PointerToRawData", c_uint), 123 | ("PointerToRelocations", c_uint), 124 | ("PointerToLinenumbers", c_uint), 125 | ("NumberOfRelocations", c_ushort), 126 | ("NumberOfLinenumbers", c_ushort), 127 | ("Characteristics", c_uint) 128 | ] 129 | 130 | """ This class parses the PE format """ 131 | class PE: 132 | def __init__(self, binary): 133 | self.__binary = bytearray(binary) 134 | 135 | self.__PEOffset = 0x00000000 136 | self.__IMAGE_FILE_HEADER = None 137 | self.__IMAGE_OPTIONAL_HEADER = None 138 | 139 | self.__sections_l = [] 140 | 141 | self.__getPEOffset() 142 | self.__parsePEHeader() 143 | self.__parseOptHeader() 144 | self.__parseSections() 145 | 146 | def __getPEOffset(self): 147 | self.__PEOffset = unpack("= rangeS and vaddr <= rangeE: 72 | new += [gadget] 73 | self.__gadgets = new 74 | 75 | def __reOption(self): 76 | new = [] 77 | re_strs = [] 78 | 79 | if not self.__options.re: 80 | return 81 | 82 | if '|' in self.__options.re: 83 | re_strs = self.__options.re.split(' | ') 84 | if 1 == len(re_strs): 85 | re_strs = self.__options.re.split('|') 86 | else: 87 | re_strs.append(self.__options.re) 88 | 89 | patterns = [] 90 | for __re_str in re_strs: 91 | pattern = re.compile(__re_str) 92 | patterns.append(pattern) 93 | 94 | for gadget in self.__gadgets: 95 | flag = 1 96 | insts = gadget["gadget"].split(" ; ") 97 | for pattern in patterns: 98 | for ins in insts: 99 | res = pattern.search(ins) 100 | if res: 101 | flag = 1 102 | break 103 | else: 104 | flag = 0 105 | if not flag: 106 | break 107 | if flag: 108 | new += [gadget] 109 | self.__gadgets = new 110 | 111 | def __removeNonCallPreceded(self): 112 | def __isGadgetCallPreceded(gadget): 113 | # Given a gadget, determine if the bytes immediately preceding are a call instruction 114 | prevBytes = gadget["prev"] 115 | # TODO: Improve / Semantically document each of these cases. 116 | callPrecededExpressions = [ 117 | "\xe8[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$", 118 | "\xe8[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$", 119 | "\xff[\x00-\xff]$", 120 | "\xff[\x00-\xff][\x00-\xff]$", 121 | "\xff[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$" 122 | "\xff[\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]$" 123 | ] 124 | return bool(reduce(lambda x,y: x or y, map(lambda x: re.search(x, prevBytes), callPrecededExpressions))) 125 | arch = self.__binary.getArch() 126 | if arch == CS_ARCH_X86: 127 | initial_length = len(self.__gadgets) 128 | self.__gadgets = filter(__isGadgetCallPreceded, self.__gadgets) 129 | print "Options().removeNonCallPreceded(): Filtered out {} gadgets.".format(initial_length - len(self.__gadgets)) 130 | else: 131 | print "Options().removeNonCallPreceded(): Unsupported architecture." 132 | 133 | def __deleteBadBytes(self): 134 | archMode = self.__binary.getArchMode() 135 | if not self.__options.badbytes: 136 | return 137 | new = [] 138 | #Filter out empty badbytes (i.e if badbytes was set to 00|ff| there's an empty badbyte after the last '|') 139 | #and convert each one to the corresponding byte 140 | bbytes = [codecs.decode(bb.encode("ascii"), "hex") for bb in self.__options.badbytes.split("|") if bb] 141 | archMode = self.__binary.getArchMode() 142 | for gadget in self.__gadgets: 143 | gadAddr = pack("{}) blocked: previously rolled this back.".format(register, value) 32 | return False 33 | self.__registers.append(dict(self.__registers[-1], **{register: value, "__delta" : {register: value}})) 34 | self.__deadends.append({}) # no known dead-ends. 35 | return True 36 | 37 | def _rollback(self, blacklist=True): 38 | ''' 39 | If a sequence is unsatisfiable, rolls back the sequence. 40 | 41 | Returns number of sequences rolled back 42 | ''' 43 | if self.__registers: 44 | bad_state, _ = self.__registers.pop(), self.__deadends.pop() 45 | if blacklist: 46 | # prevent this change again. 47 | print "[ropmaker] Rolled back {}, added to blacklist.".format(str(bad_state["__delta"])) 48 | self.__deadends[-1].add(bad_state["__delta"]) 49 | 50 | def _clear(self): 51 | ''' 52 | Clear all compilation memory. 53 | ''' 54 | self.__registers = [{}] 55 | self.__deadends = [] 56 | 57 | def _currentRegisters(self): 58 | ''' 59 | Returns the current registers. 60 | ''' 61 | return self.__registers[-1] 62 | 63 | def _generate(self): 64 | # implement in subclasses. 65 | pass -------------------------------------------------------------------------------- /ropgadget/ropchain/arch/ropmakercfi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | ## -*- coding: utf-8 -*- 3 | ## 4 | ## Justin Brower - 2017-04-14 5 | ## Brown University 6 | 7 | import re 8 | from capstone import * 9 | 10 | 11 | 12 | class ROPMakerCFIX86(ROPMaker): 13 | def _generate(): 14 | # TODO: generate payload. 15 | print "Part 1: Function Calling Primitive" 16 | print "----------------------------------" 17 | function_calling_primitives = self.__findFunctionCallingPrimitive() 18 | 19 | print "Part 2: Null-Byte Writing Primitive" 20 | print "----------------------------------" 21 | 22 | print "Part 3: Sys-Call Primitive." 23 | print "----------------------------------" 24 | pass 25 | 26 | def __named_register_groupre(self, name): 27 | return "(?P<{}>([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))".format(name) 28 | 29 | 30 | def __getGadgetSideEffects(): 31 | 32 | 33 | def __findFunctionCallingPrimitive(self): 34 | ''' 35 | Returns a call-ret pair, for use with the compiler. 36 | ''' 37 | # Step 1: Find register call gadget. 38 | availableRegisterCallGadgets = [] 39 | 40 | for gadget in self.__gadgets: 41 | g = gadget["gadget"].split(" ; ") 42 | regex = re.search("call {}".format(self.__named_register_groupre('reg')), g) 43 | if regex: 44 | # Relative call gadget found. No control flow transfers allowed in these gadgets. 45 | if not "jmp" in gadget["gadget"]: 46 | print "Found register call gadget targeting {}: {}".format(regex.group('reg'), g) 47 | availableRegisterCallGadgets.append((regex.group('reg'), gadget)) 48 | 49 | if not availableRegisterCallGadgets: 50 | print "No register call gadgets were available. You'll need to find another way to call a function." 51 | return [] 52 | 53 | # Step 2: Find register loading gadgets, to set stage for 54 | availableRegisterLoadGadgets = [] 55 | for gadget in self.__gadgets: 56 | g = gadget["gadget"].split(" ; ") 57 | 58 | regex = re.search("pop {}".format(self.__named_register_groupre('reg')), g) 59 | if regex: 60 | # Relative call gadget found. No control flow transfers allowed in these gadgets. 61 | if not "jmp" in gadget["gadget"]: 62 | print "Found register load gadget for {}: {}".format(regex.group('reg'), g) 63 | availableRegisterLoadGadgets.append((regex.group('reg'), gadget)) 64 | 65 | 66 | if not availableRegisterLoadGadgets: 67 | print "No register loads were available." 68 | return [] 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /ropgadget/ropchain/arch/ropmakerx64.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | ## -*- coding: utf-8 -*- 3 | ## 4 | ## Jonathan Salwan - 2014-05-13 5 | ## Florian Meier - 2014-08-31 - The 64b ROP chain generation 6 | ## 7 | ## http://shell-storm.org 8 | ## http://twitter.com/JonathanSalwan 9 | ## 10 | 11 | import re 12 | from capstone import * 13 | 14 | 15 | 16 | class ROPMakerX64: 17 | def __init__(self, binary, gadgets, liboffset=0x0): 18 | self.__binary = binary 19 | self.__gadgets = gadgets 20 | 21 | # If it's a library, we have the option to add an offset to the addresses 22 | self.__liboffset = liboffset 23 | 24 | self.__generate() 25 | 26 | 27 | def __lookingForWrite4Where(self, gadgetsAlreadyTested): 28 | for gadget in self.__gadgets: 29 | if gadget in gadgetsAlreadyTested: 30 | continue 31 | f = gadget["gadget"].split(" ; ")[0] 32 | regex = re.search("mov .* ptr \[(?P([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))\], (?P([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))$", f) 33 | if regex: 34 | lg = gadget["gadget"].split(" ; ")[1:] 35 | try: 36 | for g in lg: 37 | if g.split()[0] != "pop" and g.split()[0] != "ret": 38 | raise 39 | # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer 40 | if g != "ret": 41 | if g.split()[0] == "ret" and g.split()[1] != "": 42 | raise 43 | print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) 44 | return [gadget, regex.group("dst"), regex.group("src")] 45 | except: 46 | continue 47 | return None 48 | 49 | def __lookingForSomeThing(self, something): 50 | for gadget in self.__gadgets: 51 | lg = gadget["gadget"].split(" ; ") 52 | if lg[0] == something: 53 | try: 54 | for g in lg[1:]: 55 | if g.split()[0] != "pop" and g.split()[0] != "ret": 56 | raise 57 | if g != "ret": 58 | # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer 59 | if g.split()[0] == "ret" and g.split()[1] != "": 60 | raise 61 | print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) 62 | return gadget 63 | except: 64 | continue 65 | return None 66 | 67 | def __padding(self, gadget, regAlreadSetted): 68 | lg = gadget["gadget"].split(" ; ") 69 | for g in lg[1:]: 70 | if g.split()[0] == "pop": 71 | reg = g.split()[1] 72 | try: 73 | print("\tp += pack(' mov dword ptr [r32], r32 32 | regex = re.search("mov dword ptr \[(?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))$", f) 33 | if regex: 34 | lg = gadget["gadget"].split(" ; ")[1:] 35 | try: 36 | for g in lg: 37 | if g.split()[0] != "pop" and g.split()[0] != "ret": 38 | raise 39 | # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer 40 | if g != "ret": 41 | if g.split()[0] == "ret" and g.split()[1] != "": 42 | raise 43 | print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) 44 | return [gadget, regex.group("dst"), regex.group("src")] 45 | except: 46 | continue 47 | return None 48 | 49 | def __lookingForSomeThing(self, something): 50 | for gadget in self.__gadgets: 51 | lg = gadget["gadget"].split(" ; ") 52 | if lg[0] == something: 53 | try: 54 | for g in lg[1:]: 55 | if g.split()[0] != "pop" and g.split()[0] != "ret": 56 | raise 57 | # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer 58 | if g != "ret": 59 | if g.split()[0] == "ret" and g.split()[1] != "": 60 | raise 61 | print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) 62 | return gadget 63 | except: 64 | continue 65 | return None 66 | 67 | def __padding(self, gadget, regAlreadSetted): 68 | lg = gadget["gadget"].split(" ; ") 69 | for g in lg[1:]: 70 | if g.split()[0] == "pop": 71 | reg = g.split()[1] 72 | try: 73 | print("\tp += pack('[\d])", d).group("value") 31 | minorVersion = re.search("MINOR_VERSION.+=.+(?P[\d])", d).group("value") 32 | webVersion = int("%s%s" %(majorVersion, minorVersion)) 33 | curVersion = int("%s%s" %(MAJOR_VERSION, MINOR_VERSION)) 34 | if webVersion > curVersion: 35 | print("The version %s.%s is available. Currently, you use the version %d.%d." %(majorVersion, minorVersion, MAJOR_VERSION, MINOR_VERSION)) 36 | else: 37 | print("Your version is up-to-date.") 38 | 39 | -------------------------------------------------------------------------------- /ropgadget/updateAlert.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/updateAlert.pyc -------------------------------------------------------------------------------- /ropgadget/version.py: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | ## 3 | ## Jonathan Salwan - 2014-05-12 - ROPgadget tool 4 | ## 5 | ## http://twitter.com/JonathanSalwan 6 | ## http://shell-storm.org/project/ROPgadget/ 7 | ## 8 | 9 | MAJOR_VERSION = 5 10 | MINOR_VERSION = 5 11 | PYROPGADGET_VERSION = "ROPgadget v%d.%d" %(MAJOR_VERSION, MINOR_VERSION) 12 | 13 | -------------------------------------------------------------------------------- /ropgadget/version.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/ropgadget/version.pyc -------------------------------------------------------------------------------- /samples/sample.rop: -------------------------------------------------------------------------------- 1 | /* ASLR enabled? */ 2 | let aslrOffset = 0; 3 | 4 | /****************************/ 5 | /* Declare functions + vars */ 6 | /****************************/ 7 | 8 | let kill = 0x08048870 + aslrOffset; 9 | let system = 0x080484bc + aslrOffset; 10 | let getpid = 0x080484ee + aslrOffset; 11 | let exit = 0x080400b4 + aslrOffset; 12 | 13 | let pid = getpid(); 14 | 15 | /****************************/ 16 | /* Read / Write From Memory */ 17 | /****************************/ 18 | 19 | let val = mem_read(0x0804ffd3); // val := *((int *)0x0804ffd3); 20 | mem_write(0x0804ffd3, val); // *((int *)0x0804ffd3) = 14; 21 | 22 | /******************/ 23 | /* Do some things */ 24 | /******************/ 25 | 26 | /* Maybe call system! */ 27 | let shellcode = "cat /etc/passwd"; 28 | system(shellcode); 29 | 30 | /* Kill the process */ 31 | kill(pid, 9); 32 | 33 | /* Exit to the null address. */ 34 | exit(0); 35 | -------------------------------------------------------------------------------- /samples/sample1.rop: -------------------------------------------------------------------------------- 1 | /* Declare functions + vars */ 2 | // kill(10,id); 3 | 4 | 5 | // func kill = 0x08048870 : int; 6 | kill(20 + 5); 7 | 8 | let kill = 0x08048870; 9 | let system = 0x080484bc; 10 | let getpid = 0x080484ee; 11 | 12 | let pid = getpid(); 13 | 14 | let shellcode = "cat /etc/passwd"; 15 | system(shellcode); 16 | 17 | kill(pid, 9) 18 | -------------------------------------------------------------------------------- /symbols.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | # Taken from 4 | class TERM_COLORS: 5 | HEADER = '\033[95m' 6 | OKBLUE = '\033[94m' 7 | OKGREEN = '\033[92m' 8 | WARNING = '\033[93m' 9 | FAIL = '\033[91m' 10 | ENDC = '\033[0m' 11 | BOLD = '\033[1m' 12 | UNDERLINE = '\033[4m' 13 | 14 | def text_blue(text): 15 | return TERM_COLORS.OKBLUE + text + TERM_COLORS.ENDC 16 | 17 | def text_red(text): 18 | return TERM_COLORS.WARNING + text + TERM_COLORS.ENDC 19 | 20 | def text_green(text): 21 | return TERM_COLORS.OKGREEN + text + TERM_COLORS.ENDC 22 | 23 | def text_underline(text): 24 | return TERM_COLORS.UNDERLINE + text + TERM_COLORS.ENDC 25 | 26 | def text_bold(text): 27 | return TERM_COLORS.BOLD + text + TERM_COLORS.ENDC 28 | 29 | 30 | ## Symbol vs. Immediate References 31 | def getRef(reftype, val, loc): 32 | return {"type" : reftype, "val" : val, "loc" : loc} 33 | 34 | def rts(ref): 35 | desc = "(unused)" if "unused" in ref and ref["unused"] else ("(constant)" if "constant" in ref and ref["constant"] else "") 36 | if ref["type"] == "action": 37 | if ref["action"] == "apply": 38 | if ref["sym"]["type"] == "sym": 39 | return "{}({}) {}".format(rts(ref["sym"]), ",".join(map(rts, ref["args"])), desc) 40 | else: 41 | return "(*{})({}) {}".format(rts(ref["sym"]), ",".join(map(rts, ref["args"])), desc) 42 | if ref["action"] == "bind": 43 | return "{} := {} {}".format(rts(ref["sym"]), rts(ref["rvalue"]), desc) 44 | elif ref["type"] == "imm": 45 | if ref["dtype"] == "constant_hexadecimal": 46 | return "{}".format(hex(ref["val"])) 47 | elif ref["dtype"] == "constant_string": 48 | return "\"{}\"".format(ref["val"]) 49 | else: 50 | return "{}".format(ref["val"]) 51 | elif ref["type"] == "sym": 52 | return ref["val"] 53 | 54 | def rtsse(se): 55 | ropdata = "{} [{}]".format(se["roptype"], rts(se["ropdata"])) if "ropdata" in se else se["roptype"] 56 | return "[{: <15} {: >20}]".format(rts(se), ropdata) 57 | 58 | def rtsse_short(se): 59 | ropdata = "{}({})".format(se["roptype"], rts(se["ropdata"])) if "ropdata" in se else se["roptype"] 60 | return "{}".format(ropdata) 61 | 62 | def getImmRef(val, dtype, loc): 63 | base = getRef("imm", val, loc) 64 | base["dtype"] = dtype 65 | return base 66 | 67 | def getSymRef(val, loc): 68 | return getRef("sym", val.strip(), loc) 69 | 70 | def isImm(ref): 71 | return "type" in ref and ref["type"] == "imm" 72 | 73 | def isSym(ref): 74 | return "type" in ref and ref["type"] == "sym" 75 | 76 | def immIsType(ref, dtype): 77 | return "dtype" in ref and ref["dtype"] == dtype 78 | 79 | def immIsTypes(ref, dtypes): 80 | return "dtype" in ref and ref["dtype"] in dtypes 81 | 82 | def immsAreTypes(refs, dtypes): 83 | return reduce(lambda x,y: x and y, map(lambda ref: immIsTypes(ref, dtypes), refs)) 84 | 85 | def refRequiresTemp(ref): 86 | # ref will need to be moved to a temporary variable if function application used. 87 | return ref["type"] == "action" and ref["action"] == "apply" 88 | 89 | def str2words(s, WORD_SIZE): 90 | ''' 91 | Implements PKCS#7-style padding, for storing strings on the stack. 92 | 93 | Pads with 01 94 | 02 02 95 | 03 03 03 96 | 04 04 04 04 97 | Maximum padding is 0k 0k 0k 0k, for WORD_SIZE=k, since block size is one word. 98 | ''' 99 | remainder = WORD_SIZE - (len(s) % WORD_SIZE) 100 | if remainder == 0: 101 | s = s + (' ' * WORD_SIZE) # Add a word of padding. 102 | remainder = WORD_SIZE # if it matched perfectly, add a whole block of padding. 103 | else: 104 | s = s + (' ' * remainder) # padd to be an exact multiple. 105 | s = s[:len(s) - remainder] + (chr(ord('1') - 1 + remainder) * remainder) 106 | # Assume each character is a byte, and each cell is a word. 107 | assert WORD_SIZE > 0 and WORD_SIZE % 4 == 0, "Word size must be a multiple of 4 and non-zero." 108 | return [s[0+i:WORD_SIZE+i] for i in range(0, len(s), WORD_SIZE)] 109 | 110 | def printStackPayload(payload, ESP=-1): 111 | ''' 112 | Prints a stack payload, with %esp 113 | as an index into the payload. 114 | 115 | %esp set to 0 will start from the bottom of the payload, 116 | with the max value of esp at len(payload) 117 | ''' 118 | stack_entries = payload["stack"] 119 | data_divider = payload["data_begins"] 120 | print "---------{}----------------------".format(text_bold("payload")) 121 | i = 0 122 | for se in reversed(stack_entries): 123 | print rtsse(se), 124 | if ESP == (len(stack_entries) - i - 1): 125 | print text_green("<---- (%esp) "), 126 | if data_divider == (len(stack_entries) - i - 1): 127 | print text_blue("<---- (data region) "), 128 | print "" 129 | i = i + 1 130 | print "--------------------------------------" 131 | 132 | ## Function applications 133 | def makeAction(action, loc): 134 | return {"type" : "action", "action" : action, "loc" : loc} 135 | 136 | def makeBindAction(sym, rvalue, loc): 137 | base = makeAction("bind", loc) 138 | base["sym"] = getSymRef(sym.strip(), loc) 139 | base["rvalue"] = rvalue 140 | return base 141 | 142 | def makeBindActionRaw(sym, rvalue, loc): 143 | base = makeAction("bind", loc) 144 | base["sym"] = sym 145 | base["rvalue"] = rvalue 146 | return base 147 | 148 | def makeApplyActionRaw(sym, args, loc, argc=-1): 149 | base = makeAction("apply", loc) 150 | if argc == -1: argc = len(args) 151 | base["sym"] = sym 152 | base["argc"] = argc 153 | base["args"] = args 154 | return base 155 | 156 | def makeTemporaryBindAction(rvalue, loc): 157 | base = makeAction("bind", loc) 158 | varname = "tvar-{}".format(str(uuid.uuid4())[0:10]) 159 | base["sym"] = getSymRef(varname, None) 160 | base["rvalue"] = rvalue 161 | return base 162 | 163 | def makeApplyAction(sym, args, loc, argc=-1): 164 | base = makeAction("apply", loc) 165 | if argc == -1: argc = len(args) 166 | base["sym"] = getSymRef(sym.strip(), loc) 167 | base["argc"] = argc 168 | base["args"] = args 169 | return base 170 | 171 | 172 | def removeVerboseEntries(action): 173 | ''' 174 | Removes annoying entries like 'loc'. 175 | ''' 176 | action = deepcopy(action) 177 | if "loc" in action: 178 | del action["loc"] 179 | for key in action: 180 | if type(action[key]) is dict: 181 | action[key] = removeVerboseEntries(action[key]) 182 | if type(action[key]) is list: 183 | action[key] = [removeVerboseEntries(x) for x in action[key]] 184 | return action 185 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | def enumerateTests(indir): 5 | try: 6 | test_dir = "tests/{}".format(indir) 7 | files = os.listdir(test_dir) 8 | return set([f.split(".")[0] for f in files]) 9 | except OSError as e: 10 | print "[test-suite] Error enumerating tests in {}: {}".format(indir, e) 11 | 12 | def run_test_suite(): 13 | print "[test-suite] Testing tokenizer..." 14 | tests = enumerateTests("tokenizer") 15 | 16 | print "[test-suite] Testing processor..." 17 | tests = enumerateTests("process") 18 | 19 | print "[test-suite] Testing flattener..." 20 | tests = enumerateTests("flatten") 21 | 22 | print "[test-suite] Testing compiler..." 23 | tests = enumerateTests("ropcompile") 24 | 25 | 26 | 27 | def test_tokenizer(tests): 28 | pass 29 | 30 | def test_process(tests): 31 | pass 32 | 33 | def test_flatten(tests): 34 | pass 35 | 36 | def test_ropcompile(tests): 37 | pass -------------------------------------------------------------------------------- /tests/tokenizer/SIMPLE-0.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/tests/tokenizer/SIMPLE-0.out -------------------------------------------------------------------------------- /tests/tokenizer/SIMPLE-0.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrower95/crop/115d342deb2a9fa34b93b24bf8caeca74619274b/tests/tokenizer/SIMPLE-0.test -------------------------------------------------------------------------------- /tokenize.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from process import ROPSyntaxError 3 | import re 4 | 5 | def linesToTokens(): 6 | pass 7 | 8 | def has(data, offset, pattern): 9 | ''' 10 | Returns true if, at the given offset, you can read 'pattern' 11 | from data. 12 | ''' 13 | if offset + len(pattern) > len(data): return False 14 | return data[offset:offset+len(pattern)] == pattern 15 | 16 | def grabWhileIn(data, offset, pattern): 17 | ''' 18 | Returns a variable length substring of data, starting from offset, 19 | containing only the characters in pattern. 20 | ''' 21 | return grabUntil(data, offset, lambda data,i: data[i] not in pattern) 22 | 23 | def grabUntil(data, offset, fun): 24 | ''' 25 | Returns a variable length substring of data, starting from offset, 26 | containing only the characters in pattern. 27 | ''' 28 | max_len = len(data)-offset 29 | end = len(data) 30 | for i in range(offset, offset + max_len): 31 | if fun(data, i): 32 | end = i 33 | break 34 | return data[offset:end] 35 | 36 | def mktok(tok, char, location=None, value=None,force=False): 37 | base = {"name" : tok, "chr" : char, } 38 | if location: base["location"] = {"line" : location[0], "char" : location[1]} 39 | if value or force: base["value"] = value 40 | return base 41 | 42 | IDENTIFIER_TOKENS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" 43 | NUMBER_TOKENS = "0123456789" 44 | HEX_TOKENS = NUMBER_TOKENS + "abcdefABCDEF" 45 | 46 | def tokenize(corpus): 47 | # Load tokens, in order. 48 | collected_tokens = [] 49 | 50 | i = 0 51 | line = 0 52 | char = 0 53 | while i < len(corpus): 54 | if corpus[i] == '\n': 55 | char, line = 0, line + 1 56 | i = i + 1 57 | elif corpus[i] == ";": 58 | collected_tokens.append(mktok("EOL", i, location=(line,char))) 59 | i = i + 1 60 | char = char + 1 61 | elif corpus[i] == "$": 62 | collected_tokens.append(mktok("deref", i, location=(line,char))) 63 | i = i + 1 64 | char = char + 1 65 | elif corpus[i] == '(': 66 | collected_tokens.append(mktok("start_apply", i, location=(line,char))) 67 | i = i + 1 68 | char = char + 1 69 | elif corpus[i] == ')': 70 | collected_tokens.append(mktok("end_apply", i, location=(line,char))) 71 | i = i + 1 72 | char = char + 1 73 | elif corpus[i] == ',': 74 | collected_tokens.append(mktok("arglist_separator", i, location=(line,char))) 75 | i = i + 1 76 | char = char + 1 77 | elif corpus[i] == "=" and not has(corpus, i, "=="): 78 | collected_tokens.append(mktok("assign", i, location=(line,char))) 79 | i = i + 1 80 | char = char + 1 81 | elif corpus[i] == "+": 82 | collected_tokens.append(mktok("ma_add", i, location=(line,char))) 83 | i = i + 1 84 | char = char + 1 85 | elif corpus[i] == "-": 86 | collected_tokens.append(mktok("ma_subtract", i, location=(line,char))) 87 | i = i + 1 88 | char = char + 1 89 | elif corpus[i] == "*": 90 | collected_tokens.append(mktok("ma_multiply", i, location=(line,char))) 91 | i = i + 1 92 | char = char + 1 93 | elif has(corpus, i, "=="): 94 | collected_tokens.append(mktok("equals", i, location=(line,char))) 95 | i = i + 2 96 | char = char + 2 97 | elif has(corpus, i, "//"): 98 | i = i + 2 # advance past the comment marker. 99 | char = char + 2 100 | val = grabUntil(corpus, i, lambda data,i: data[i] == "\n") 101 | collected_tokens.append(mktok("single_line_comment", i, location=(line,char), value=val)) 102 | i = i + len(val) 103 | char = char + len(val) 104 | elif has(corpus, i, "/*"): 105 | i = i + 2 106 | char = char + 2 107 | val = grabUntil(corpus, i, lambda data,i: data[i] == "/" and data[i-1] == "*")[:-1] 108 | collected_tokens.append(mktok("multi_line_comment", i, location=(line,char), value=val)) 109 | i = i + len(val) + 2 110 | char = char + len(val) + 2 111 | elif has(corpus, i, "let"): 112 | collected_tokens.append(mktok("let",i,location=(line,char))) 113 | i = i + 3 114 | char = char + 3 115 | elif has(corpus, i, "if"): 116 | collected_tokens.append(mktok("if",i,location=(line,char))) 117 | i = i + 2 118 | char = char + 2 119 | elif has(corpus, i, "while"): 120 | collected_tokens.append(mktok("while",i,location=(line,char))) 121 | i = i + 5 122 | char = char + 5 123 | elif not corpus[i].isspace(): 124 | # Read constants + identifiers. 125 | val = None 126 | val_class = None 127 | val_len = 0 128 | if corpus[i] in IDENTIFIER_TOKENS: 129 | val = grabWhileIn(corpus, i, IDENTIFIER_TOKENS) 130 | val_class = "identifier" 131 | val_len = len(val) 132 | elif has(corpus, i, "0x"): 133 | i = i + 2 134 | val = grabWhileIn(corpus, i, HEX_TOKENS) 135 | val_class = "constant_hexadecimal" 136 | val_len = len(val) 137 | val = int(val, 16) 138 | elif corpus[i] in NUMBER_TOKENS: 139 | val = grabWhileIn(corpus, i, NUMBER_TOKENS) 140 | val_len = len(val) 141 | val = int(val) 142 | val_class = "constant_numerical" 143 | elif corpus[i] == "\"": 144 | i = i + 1 145 | char = char + 1 146 | val = grabUntil(corpus, i, lambda data, i: data[i] == '"' and data[i-1] != "\\") 147 | val_len = len(val) 148 | val_class = "constant_string" 149 | i = i + 1 150 | char = char + 1 151 | else: 152 | raise ROPSyntaxError("Unexpected token {}".format(corpus[i]), {"line":line,"char":char}) 153 | i = i + val_len 154 | collected_tokens.append(mktok(val_class,i,location=(line,char),value=val,force=True)) 155 | char = char + val_len 156 | else: 157 | i = i + 1 158 | char = char + 1 159 | collected_tokens.append(mktok("EOF", i, location=(line,char))) 160 | return collected_tokens -------------------------------------------------------------------------------- /validate.py: -------------------------------------------------------------------------------- 1 | from process import ROPSyntaxError 2 | from symbols import * 3 | from primitives import primitives 4 | 5 | DEBUG = True 6 | 7 | def validate(actions): 8 | # one, global scope. 9 | scope = set() 10 | 11 | # add all primitive functions. 12 | for region in primitives: 13 | scope.update((primitives[region].keys())) 14 | 15 | for action in actions: 16 | if action["action"] == "bind": 17 | # Bind affects scope, so handle here. 18 | validate_rval(action["rvalue"], scope) 19 | # Assuming the rvalue here is OK, add this binding to scope. 20 | scope.add(action["sym"]["val"]) 21 | else: 22 | # Validate the action as an rval. 23 | validate_rval(action, scope) 24 | 25 | def validate_rval(action, scope): 26 | if action["type"] == "action" and action["action"] == "apply": 27 | # Validate function name 28 | throwIfAbsent(action, scope) 29 | for argument in action["args"]: 30 | validate_rval(argument, scope) 31 | elif action["type"] == "sym": 32 | throwIfAbsent(action, scope) 33 | 34 | def throwIfAbsent(action, scope): 35 | if action["type"] == "sym" or (action["type"] == "action" and action["action"] == "apply"): 36 | if "sym" in action and not action["sym"]["val"] in scope: 37 | raise ROPSyntaxError("Unknown symbol '{}'.".format(rts(action["sym"])), action["loc"]) 38 | --------------------------------------------------------------------------------