Can embed any file in a PNG/WebM/GIF/JPEG and upload it to a third-party host through 4chan
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

97 lines
3.1 KiB

import { Buffer } from "buffer";
import { BufferWriteStream, concatAB } from "./png";
const netscape = Buffer.from("!\xFF\x0BNETSCAPE2.0\x03\x01\x00\x00\x00");
const magic = Buffer.from("!\xFF\x0B" + "COOMTECH0.1", 'ascii');
const extractBuff = (gif: Buffer) => {
let field = gif.readUInt8(10);
let gcte = !!(field & (1 << 7));
let end = 13;
if (gcte) {
end += 3 * (1 << ((field & 7) + 1));
}
// skip beeg blocks
while (gif.readUInt8(end) == '!'.charCodeAt(0)) {
if (magic.compare(gif, end, end + magic.byteLength) != 0) {
end += 3 + gif.readUInt8(end + 2);
while (1) { // skip sub blocks
let v = gif.readUInt8(end++);
if (!v)
break;
end += v;
}
} else {
let count = end + magic.byteLength;
let t = 0;
let v = 0;
while ((v = gif.readUInt8(count)) != 0) {
t += v;
count += v + 1;
}
let buff = Buffer.alloc(t);
count = end + magic.byteLength;
t = 0;
while ((v = gif.readUInt8(count)) != 0) {
gif.copy(buff, t, count + 1, count + 1 + v)
t += v;
count += v + 1;
}
return buff;
}
}
// metadata ended, nothing...
};
export const extract = async (reader: ReadableStreamDefaultReader<Uint8Array>): Promise<{ filename: string; data: Buffer } | undefined> => {
let total = Buffer.from('');
let chunk: ReadableStreamDefaultReadResult<Uint8Array>;
// todo: early reject
do {
chunk = await reader.read();
if (chunk.value)
total = concatAB(total, Buffer.from(chunk.value));
} while (!chunk.done);
const data = extractBuff(total);
if (!data)
return;
return { filename: 'embedded', data };
};
const write_embedding = async (writer: WritableStreamDefaultWriter<Buffer>, inj: Buffer) => {
await writer.write(magic);
const byte = Buffer.from([0]);
let size = inj.byteLength;
let ws;
let offset = 0;
while (size != 0) {
ws = size >= 255 ? 255 : size;
byte.writeUInt8(ws, 0);
await writer.write(byte);
await writer.write(inj.slice(offset, offset + ws));
size -= ws;
offset += ws;
}
byte.writeUInt8(0, 0);
await writer.write(byte);
};
export const inject = async (container: File, inj: File) => {
const [writestream, extract] = BufferWriteStream();
const writer = writestream.getWriter();
let contbuff = Buffer.from(await container.arrayBuffer());
let field = contbuff.readUInt8(10);
let gcte = !!(field & (1 << 0x7))
let endo = 13;
if (gcte)
endo += 3 * (1 << ((field & 7) + 1));
if (netscape.compare(contbuff, endo, endo + netscape.byteLength) == 0)
endo += netscape.byteLength;
await writer.write(contbuff.slice(0, endo));
await write_embedding(writer, Buffer.from(await inj.arrayBuffer()));
await writer.write(contbuff.slice(endo));
return extract();
};