- Created .gitignore to exclude build artifacts and dependencies. - Added package.json and package-lock.json for project dependencies and scripts. - Included pnpm workspace configuration for managing packages. - Implemented TypeScript configuration in tsconfig.json. - Added README.md with project description and usage instructions. - Introduced native code for DES encryption and decryption in C/C++. - Created initial decoded data structure for handling scan results. - Established basic file structure for decoded outputs and native builds.
100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import fs from "fs";
|
|
import path from "path";
|
|
import { HEADER_MAGICS } from "./constants";
|
|
import { desDecryptBlock, desDecryptFile, hexKeyToBuffer } from "./des";
|
|
|
|
export interface HitRecord {
|
|
cipher: "des";
|
|
password: string;
|
|
derivation: string;
|
|
key_hex: string;
|
|
pad_fill: string;
|
|
first_pt_hex: string | null;
|
|
backend: string;
|
|
payload_ok?: boolean;
|
|
payload_reason?: string;
|
|
first64_ascii?: string;
|
|
decrypt_error?: string;
|
|
}
|
|
|
|
export function payloadLooksReal(first64: Buffer, more?: Buffer): { ok: boolean; reason: string } {
|
|
for (const magic of HEADER_MAGICS) {
|
|
if (first64.subarray(0, magic.length).equals(magic) || first64.includes(magic)) {
|
|
return { ok: true, reason: "magic" };
|
|
}
|
|
}
|
|
let printable = 0;
|
|
for (const b of first64) {
|
|
if ((b >= 32 && b < 127) || b === 9 || b === 10 || b === 13) {
|
|
printable++;
|
|
}
|
|
}
|
|
if (printable >= 48) {
|
|
return { ok: true, reason: "printable_header" };
|
|
}
|
|
const blob = Buffer.concat([first64, more ?? Buffer.alloc(0)]);
|
|
if (blob.includes(Buffer.from("Butcher")) || blob.includes(Buffer.from("Thomas")) ||
|
|
blob.includes(Buffer.from("Pectus")) || blob.includes(Buffer.from("Torso"))) {
|
|
return { ok: true, reason: "patient_string" };
|
|
}
|
|
let inRange = 0;
|
|
for (let i = 0; i + 4 <= first64.length; i += 4) {
|
|
const f = first64.readFloatLE(i);
|
|
if (Number.isFinite(f) && Math.abs(f) < 5000) {
|
|
inRange++;
|
|
}
|
|
}
|
|
if (inRange >= 12) {
|
|
return { ok: true, reason: "float32_scan_range" };
|
|
}
|
|
const count = first64.readUInt32LE(0);
|
|
if (count >= 1000 && count <= 200_000) {
|
|
return { ok: true, reason: `vertex_count_${count}` };
|
|
}
|
|
return { ok: false, reason: "unstructured" };
|
|
}
|
|
|
|
export function asciiPreview(buf: Buffer): string {
|
|
return [...buf].map((b) => (b >= 32 && b < 127 ? String.fromCharCode(b) : ".")).join("");
|
|
}
|
|
|
|
export function saveHit(hit: HitRecord, data: Buffer, outDir: string): string | null {
|
|
fs.mkdirSync(outDir, { recursive: true });
|
|
const key = hexKeyToBuffer(hit.key_hex);
|
|
try {
|
|
const plain = desDecryptFile(key, data);
|
|
const first64 = plain.subarray(0, 64);
|
|
const more = plain.subarray(0, 4096);
|
|
const check = payloadLooksReal(first64, more);
|
|
hit.payload_ok = check.ok;
|
|
hit.payload_reason = check.reason;
|
|
hit.first64_ascii = asciiPreview(first64);
|
|
const stamp = `${hit.cipher}_${hit.derivation}_${hit.key_hex.slice(0, 16)}`;
|
|
const binPath = path.join(outDir, `decrypted_${stamp}.bin`);
|
|
fs.writeFileSync(binPath, plain);
|
|
fs.writeFileSync(path.join(outDir, `hit_${stamp}.json`), JSON.stringify(hit, null, 2) + "\n");
|
|
return binPath;
|
|
} catch (err) {
|
|
hit.decrypt_error = err instanceof Error ? err.message : String(err);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function firstPlainHex(keyHex: string, firstCt: Buffer): string | null {
|
|
try {
|
|
const pt = desDecryptBlock(hexKeyToBuffer(keyHex), firstCt);
|
|
return pt.toString("hex");
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeResults(outDir: string, elapsedSec: number, hits: HitRecord[]): void {
|
|
fs.mkdirSync(outDir, { recursive: true });
|
|
const confirmed = hits.filter((h) => h.payload_ok);
|
|
fs.writeFileSync(
|
|
path.join(outDir, "results.json"),
|
|
JSON.stringify({ elapsed_sec: Math.round(elapsedSec * 1000) / 1000, pad_matches: hits, confirmed }, null, 2) + "\n",
|
|
);
|
|
}
|