const minisrv_service_file = true; const viewers = { 0: "WebTVIntel--1.0.exe", 1: "WebTVIntel--1.1.exe", 2: "WebTVIntel--2.5.exe", 3: "WebTVIntel--1.0-HE.exe", 4: "WebTVIntel--2.5-HE.exe" } const logos = { 0: null, 1: "SuperViewer_Setup.bmp", 2: "HackersEdition_Setup.bmp" } const disksets = { 0: null, 98: "HackTV_min.zip" } const viewer_stock_md5s = { "WebTVIntel--1.0.exe": "d7bde1adbe3549f58dd95425d3ac2af9", "WebTVIntel--1.1.exe": "ce7b6d1734b5e3d1cbd5f068609223d1", "WebTVIntel--2.5.exe": "4c5754bb8b69739b6f414c2d159051da", "WebTVIntel--1.0-HE.exe": "391f303fd70034e69d3a50583de72c89", "WebTVIntel--2.5-HE.exe": "64edab977ec19a663c5842176bec306a" } /* const modpacks = { 0: { "name": "Background Sound", "description": "Enables the Viewer to continue playing sound when it is not the currently active window.", "file": "BackgroundSound.zip", "default": true } } */ const modpacks = {}; const feature_bits = { 2: { 0: { "offset": 2063881, "name": "Background Sound", "description": "Enables the Viewer to continue playing sound when it is not the currently active window.", "value": Buffer.from("\x80", 'ascii') } }, 4: { 0: { "offset": 2063881, "name": "Background Sound", "description": "Enables the Viewer to continue playing sound when it is not the currently active window.", "value": Buffer.from("\x80", 'ascii') } } } const patch_defaults = { "start_url": "client:GoToConn", "default_ip": "10.0.0.1" } const patch_limits = { "start_url": 26, "default_ip": 11, } function getPatchDataType(type, invert = false) { let patch_data = false; if ((type == "wtv-incarnation" && !invert) || (type == "wtv-encryption" && invert)) { patch_data = "wtv-client-serial-number: %s\r\n" patch_data += "wtv-user-requested-upgrade: %s\r\n"; patch_data += "wtv-system-cpuprid: %s\r\n"; patch_data += "wtv-system-version: %s\r\n"; patch_data += "wtv-capability-flags: %s\r\n"; patch_data += "wtv-client-bootrom-version: %s\r\n"; patch_data += "wtv-need-upgrade: %s\r\n"; patch_data += "wtv-used-8675309: %s\r\n"; patch_data += "wtv-client-rom-type: %s\r\n"; patch_data += "wtv-system-chipversion: %s\r\n"; patch_data += "User-Agent: %s\r\n"; } else if ((type == "wtv-encryption" && !invert) || (type == "wtv-incarnation" && invert) ) { patch_data = "wtv-tourist-enabled: %s\r\n"; patch_data += "wtv-demo-enabled: %s\r\n"; patch_data += "wtv-default-client-scriptprops: %s\r\n"; patch_data += "wtv-default-client-useragent: %s\r\n"; patch_data += "wtv-system-cpuspeed: %s\r\n"; patch_data += "wtv-system-sysconfig: %s\r\n"; patch_data += "wtv-my-disk-sucks-sucks-sucks: %s\r\n"; patch_data += "wtv-disk-first-error: %s\r\n"; patch_data += "wtv-disk-size: %s\r\n"; patch_data += "wtv-client-address: %s\r\n"; patch_data += "wtv-viewer: %s\r\n"; } return patch_data; } function getResData(file) { let res_data = null; if (file.slice(-2, 2).toLowerCase() == "gz") { const res_gz_data = wtvshared.getServiceDep("/viewergen/" + file); res_data = zlib.gunzipSync(res_gz_data); } else { res_data = wtvshared.getServiceDep("/viewergen/" + file); } return res_data; } const patch_data = { "WebTVIntel--1.0.exe": { 225: Buffer.from("\xD8", 'ascii'), 273: Buffer.from("\x60", 'ascii'), 332: Buffer.from("\x00\xAA", 'ascii'), 568: Buffer.from("\x00\xA8", 'ascii'), 577: Buffer.from("\xAA", 'ascii'), 624: { length: 383, type: "wtv-incarnation" }, 132022: Buffer.from("\x68\xE8\x65\x5D\x00", 'ascii'), // patch pre-register pt1 157861: Buffer.from("\x68\x70\x02\x40\x00", 'ascii'), // Prepare incarnation hack 1016856: Buffer.from("\x68\xCC\x04\x5E\x00", 'ascii'), 1074080: Buffer.from("\x68\xCC\x04\x5E\x00", 'ascii'), 1263129: Buffer.from("\x68\x70\x6C\x5A\x00", 'ascii'), // Prepare encryption hack 1919952: Buffer.from("scriptless-visit-reason\x00", 'ascii'), // patch pre-register pt2 1919976: Buffer.from("10\x00", 'ascii'), // patch pre-register pt3 1728624: { length: 398, type: "wtv-encryption" }, 1931552: Buffer.from(patch_defaults.default_ip + "\x00", 'ascii'), // patch default service address 1960460: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 1960496: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 1967796: Buffer.from("\x00".repeat(41), 'ascii'), // remove unwanted headers 1967856: Buffer.from("\x00".repeat(24), 'ascii'), // remove unwanted headers 2003968: getResData("ResData--1.0.res.gz") }, "WebTVIntel--1.0-HE.exe": { 624: { length: 383, type: "wtv-incarnation" }, 1728624: { length: 398, type: "wtv-encryption" } }, "WebTVIntel--1.1.exe": { 209: Buffer.from("\xFA", 'ascii'), 257: Buffer.from("\x90", 'ascii'), 316: Buffer.from("\x00\xB6", 'ascii'), 552: Buffer.from("\x00\xB4", 'ascii'), 561: Buffer.from("\xB6", 'ascii'), 620: { length: 383, type: "wtv-incarnation" }, 132118: Buffer.from("\x68\xE4\x75\x5D\x00", 'ascii'), // patch pre-register pt1 157861: Buffer.from("\x68\xCC\x72\x5A\x00", 'ascii'), // Prepare encryption hack 1015384: Buffer.from("\x68\xCC\x14\x5E\x00", 'ascii'), 1073264: Buffer.from("\x68\xCC\x14\x5E\x00", 'ascii'), 1264057: Buffer.from("\x68\x6C\x02\x40\x00", 'ascii'), // Prepare incarnation hack 1730252: { length: 307, type: "wtv-encryption" }, 1921484: Buffer.from("scriptless-visit-reason\x00", 'ascii'), // patch pre-register pt2 1921508: Buffer.from("10\x00", 'ascii'), // patch pre-register pt3 1933064: Buffer.from(patch_defaults.default_ip + "\x00", 'ascii'), // patch default service address 1962188: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 1962224: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 1969540: Buffer.from("\x00".repeat(84), 'ascii'), // remove unwanted headers 2005504: getResData("ResData--1.1.res.gz") }, "WebTVIntel--2.5.exe": { 396: Buffer.from("\x00\x50", 'ascii'), 720: { length: 3356, type: "wtv-encryption" }, 279771: Buffer.from("\x68\xB8\x04\x75\x00", 'ascii'), // patch pre-register pt1 299023: Buffer.from("\x08", 'ascii'), // Change the call location from ecx+16 to ecx+15 to unlock the communication stream 329666: Buffer.from("\x68\x82\x6C\x70\x00", 'ascii'), // Prepare incarnation hack 1893931: Buffer.from("\x71", 'ascii'), // Unlock the wtv- url access from the address bar. 2201731: Buffer.from("\x68\xD0\x02\x40\x00", 'ascii'), // Prepare encryption hack 3173506: { length: 865, type: "wtv-incarnation" }, 3473396: Buffer.from("\x00", 'ascii'), 3474616: Buffer.from("10\x00", 'ascii'), // patch pre-register pt3 3746504: Buffer.from(patch_defaults.default_ip + "\x00", 'ascii'), // patch default service address 3474628: Buffer.from("scriptless-visit-reason\x00", 'ascii'), // patch pre-register pt2 3482832: Buffer.from("\x00".repeat(10), 'ascii'), // remove unwanted headers 3808684: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 3808720: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 3808748: Buffer.from(patch_defaults.start_url + "\x00", 'ascii'), // patch startup url 3826408: Buffer.from("\x00".repeat(66), 'ascii'), // remove unwanted headers 3826356: Buffer.from("\x00".repeat(24), 'ascii'), // remove unwanted headers 3940352: getResData("ResData--2.5.res.gz") }, "WebTVIntel--2.5-HE.exe": { 720: { length: 3356, type: "wtv-encryption" }, 3173506: { length: 865, type: "wtv-incarnation" } } } function getPatchData(fname, client_data_obj, start_url = "client:GoToConn", default_ip = "10.0.0.1") { const customized_patch_data = patch_data[fname]; Object.keys(customized_patch_data).forEach(function (idx) { const val = customized_patch_data[idx]; if (typeof val === 'string') { // start url override if (start_url != patch_defaults.start_url && start_url.length <= patch_limits.start_url) { if (val.slice(0, patch_defaults.start_url.length) == patch_defaults.start_url) customized_patch_data[idx] = start_url + "\x00"; } // default service ip override if (default_ip != patch_defaults.default_ip && default_ip.length <= patch_limits.default_ip) { if (val.slice(0, patch_defaults.default_ip.length) == patch_defaults.default_ip) customized_patch_data[idx] = default_ip + "\x00"; } } else { if (!val.byteLength) { // not a buffer object let patch_data_string = ""; const block_length = val['length']; const patch_data = getPatchDataType(val['type'], (fname.slice(12, 3) != "1.1")); if (patch_data) { const patch_data_array = patch_data.split("\r\n"); Object.keys(patch_data_array).forEach(function (didx) { const header_end = patch_data_array[didx].indexOf(":"); if (header_end) { const patch_data_header = patch_data_array[didx].slice(0, header_end); const client_value = client_data_obj[patch_data_header]; if (client_value) patch_data_string += patch_data_array[didx].replace("%s", client_value) + "\r\n"; } }); } if (fname.slice(12, 3) != "2.5") { const length_difference = block_length - patch_data_string.length; if (length_difference > 0) patch_data_string += "\x00".repeat(length_difference - (val['type'].length + 1)); patch_data_string += val['type'] + "\x00"; } else { patch_data_string += val['type'] + "\x00"; const length_difference = block_length - patch_data_string.length; if (length_difference > 0) patch_data_string += "\x00".repeat(length_difference); } customized_patch_data[idx] = Buffer.from(patch_data_string, 'ascii'); } } }) return customized_patch_data; } function applyPatch(data, patch_data, offset) { const data_length = patch_data.byteLength || patch_data.length; return data.fill(patch_data, offset, data_length + offset); } function patchBinary(patchDataObject) { let patched_file = patchDataObject.data; Object.keys(patchDataObject.patch_data).forEach(function (idx) { idx = parseInt(idx); patched_file = applyPatch(patched_file, patchDataObject.patch_data[idx], idx); }) return patched_file; } function generateSSID() { const ssid_template = "91xxxxxxaeb002"; let ssid = ssid_template; while (ssid.indexOf("x") != -1) { // random hex char from 0-F ssid = ssid.replace("x", Math.floor(Math.random() * 16).toString(16)) } return ssid + wtvshared.getSSIDCRC(ssid); } function buildProfile(build) { let buildProfile = null; switch (build) { case 1235: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "1009c93bef", "wtv-client-bootrom-version": 105, "wtv-client-rom-type": "bf0app", "wtv-system-chipversion": 16842752, "User-Agent": "Mozilla/4.0 WebTV/1.4.2 (compatible; MSIE 3.0)", "wtv-system-cpuspeed": 112790760, "wtv-system-sysconfig": 736935823 } break; case 7181: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "10935ffc8f", "wtv-client-bootrom-version": 2046, "wtv-client-rom-type": "US-LC2-disk-0MB-8MB", "wtv-system-chipversion": 51511296, "User-Agent": "Mozilla/4.0 WebTV/2.2.6.1 (compatible; MSIE 4.0)", "wtv-system-cpuspeed": 166187148, "wtv-system-sysconfig": 4163328, "wtv-disk-size": 8006 } break; case 7253: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "f1d9bdfefef", "wtv-client-bootrom-version": 2243, "wtv-client-rom-type": "US-LC2-disk-0MB-8MB-softmodem-CPU5230", "wtv-system-chipversion": 53608448, "User-Agent": "Mozilla/4.0 WebTV/2.2.6.1 (compatible; MSIE 4.0)", "wtv-system-cpuspeed": 166164434, "wtv-system-sysconfig": 3115520, "wtv-disk-size": 8006 } break; case 71810: buildProfile = { "wtv-capability-flags": "d10094938ef", "wtv-system-version": 7181, "wtv-client-rom-type": "bf0app", "wtv-client-bootrom-version": 105, "wtv-system-chipversion": 16842752, "wtv-system-sysconfig": 736935823, "wtv-system-cpuspeed": 112790760, "User-Agent": "Mozilla/4.0 WebTV/2.5 (compatible; MSIE 4.0)" } break; case 16276: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "1fee0e1d9b1ffdef", "wtv-client-bootrom-version": 2046, "wtv-client-rom-type": "US-LC2-disk-0MB-8MB", "wtv-system-chipversion": 53608448, "User-Agent": "Mozilla/4.0 WebTV/2.8.2 (compatible; MSIE 4.0)", "wtv-system-cpuspeed": 166164662, "wtv-system-sysconfig": 3116068, "wtv-disk-size": 8006 } break; case 17015: buildProfile = { "wtv-capability-flags": "21816935fec8f", "wtv-system-version": 17015, "wtv-client-rom-type": "US-WEBSTAR-disk-0MB-16MB-softmodem-CPU5230", "wtv-client-bootrom-version": 2524, "wtv-system-chipversion": 53608448, "wtv-system-sysconfig": 3130128, "wtv-system-cpuspeed": 166164662, "User-Agent": "Mozilla/4.0 WebTV/2.8.2 (compatible; MSIE 4.0)" } break; case 5792: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "5c9bdfefef", "wtv-client-bootrom-version": 2525, "wtv-client-rom-type": "US-LC2-flashdisk-0MB-16MB-softmodem-CPU5230", "wtv-system-chipversion": 53608448, "User-Agent": "Mozilla/4.0 WebTV/2.8.2 (compatible; MSIE 4.0)", "wtv-system-cpuspeed": 166330740, "wtv-system-sysconfig": 3116068, "wtv-disk-size": 8006 } break; case 57920: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "5499dbafef", "wtv-client-bootrom-version": 2525, "wtv-client-rom-type": "US-BPS-flashdisk-0MB-8MB-softmodem-CPU5230", "wtv-system-chipversion": 84017152, "User-Agent": "Mozilla/4.0 WebTV/2.8.2 (compatible; MSIE 4.0)", "wtv-system-cpuspeed": 148141518, "wtv-system-sysconfig": 3133702, "wtv-disk-size": 3990 } break; case 28220: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "6f217b935dec8e", "wtv-client-bootrom-version": 2545, "wtv-client-rom-type": "US-DTV-disk-0MB-32MB-softmodem-CPU5230", "wtv-system-chipversion": 0x04120000, "User-Agent": "Mozilla/4.0 WebTV/2.8.2 (compatible; MSIE 4.0)", "wtv-system-cpuspeed": 249088032, "wtv-system-sysconfig": 0x034dea33, "wtv-disk-size": 156330720 } break; case 5254: buildProfile = { "wtv-system-version": build, "wtv-capability-flags": "19d4928cf", "wtv-client-bootrom-version": 5254, "wtv-client-rom-type": "JP-Fiji", "User-Agent": "Mozilla/4.0 WebTV/2.8.2 (compatible; MSIE 4.0)" } break; } return buildProfile; } let enable_full_hacktv = false; if (wtvshared.getServiceDep("/viewergen/" + "HackTV.zip", true)) { enable_full_hacktv = true; disksets['99'] = "HackTV.zip"; } if (request_headers.query.viewer && (request_headers.query.client_ssid || request_headers.query.random_ssid)) { let client_ssid = null; if (request_headers.query.client_ssid) client_ssid = request_headers.query.client_ssid; if (request_headers.query.random_ssid) client_ssid = generateSSID(); const viewer_file = viewers[request_headers.query.viewer]; const needs_hacktv_mini = (viewer_file === "WebTVIntel--2.5-HE.exe") ? true : false if (!viewer_file) { const errpage = wtvshared.doErrorPage("500", null, socket.minisrv_pc_mode) headers = errpage[0]; data = errpage[1]; } else { const viewer_gz_data = wtvshared.getServiceDep("/viewergen/" + viewer_file + ".gz"); const viewer_data = zlib.gunzipSync(viewer_gz_data); const viewer_md5 = crypto.createHash('md5').update(viewer_data).digest("hex"); if (viewer_md5 != viewer_stock_md5s[viewer_file]) { console.error(viewer_file, "md5sum error. expected:", viewer_stock_md5s[viewer_file], ", got:", viewer_md5) const errpage = wtvshared.doErrorPage("500", null, socket.minisrv_pc_mode) headers = errpage[0]; data = errpage[1]; } else { const build = request_headers.query.build; let client_data_obj = null if (build) { if (parseInt(build) > 0) { client_data_obj = buildProfile(parseInt(build)); } } // fallback if (!client_data_obj) client_data_obj = buildProfile(7181); const viewer_tag = viewer_file.split('.'); viewer_tag.pop(); client_data_obj['wtv-viewer'] = viewer_tag.join('.'); client_data_obj["wtv-client-serial-number"] = client_ssid; const patchDataObject = { data: viewer_data, patch_data: getPatchData(viewer_file, client_data_obj) } if (!patchDataObject.patch_data) { const errpage = wtvshared.doErrorPage("500", null, socket.minisrv_pc_mode) headers = errpage[0]; data = errpage[1]; } else { let patched_file = patchBinary(patchDataObject); const enabled_feature_bits = []; Object.keys(request_headers.query).forEach((k) => { if (k.slice(0, 12) === "feature_bit_") { enabled_feature_bits.push(parseInt(k.slice(12))); } }); Object.keys(enabled_feature_bits).forEach((k) => { const bit = feature_bits[request_headers.query.viewer][enabled_feature_bits[k]]; if (bit) { patched_file = applyPatch(patched_file, bit.value, bit.offset); } }); headers = `200 OK Content-Type: application/octet-stream Content-Disposition: attachment; filename="${viewer_file.replace(".exe", ".zip")}"` const AdmZip = require("adm-zip"); const zip = new AdmZip(); zip.addZipComment("Viewer SSID: " + client_ssid); console.log(request_headers) let update_str = "http://" + request_headers.host + request_headers.request_url.split('?')[0] + "?ssid=" + client_ssid; Object.keys(request_headers.query).forEach((k) => { if (k != "random_ssid") { update_str += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(request_headers.query[k]); } }); zip.addFile("update_url.txt", update_str); zip.addFile(viewer_file.replace("--", "-" + client_ssid + "-"), patched_file); if (!request_headers.query.viewer_only) { const romset_zip = new AdmZip(wtvshared.getServiceDep("/viewergen/" + viewer_file.replace(".exe", "").replace("WebTVIntel", "AppData") + ".zip", true)); const zipEntries = romset_zip.getEntries(); zipEntries.forEach(function (zipEntry) { if (zipEntry.entryName == "Setup.bmp" && request_headers.query.logo) { const logo_file = logos[parseInt(request_headers.query.logo) || 0]; if (logo_file) { const logo_gz_data = wtvshared.getServiceDep("/viewergen/" + logo_file + ".gz"); const logo_data = zlib.gunzipSync(logo_gz_data); zip.addFile(zipEntry.entryName, logo_data); } else { zip.addFile(zipEntry.entryName, zipEntry.getData()); } } else { zip.addFile(zipEntry.entryName, zipEntry.getData()); } }); if (request_headers.query.diskset || needs_hacktv_mini) { let diskset_file = 0; if (needs_hacktv_mini && request_headers.query.diskset === 0) diskset_file = disksets[98]; else diskset_file = disksets[parseInt(request_headers.query.diskset) || 0]; if (diskset_file) { const diskset_zip = new AdmZip(wtvshared.getServiceDep("/viewergen/" + diskset_file, true)); const zipEntries = diskset_zip.getEntries(); zipEntries.forEach(function (zipEntry) { zip.addFile("Disk/" + zipEntry.entryName, zipEntry.getData()); }); } } const embed_modpacks = []; Object.keys(request_headers.query).forEach((k) => { if (k.slice(0, 8) === "modpack_") { embed_modpacks.push(parseInt(k.slice(8))); } }); if (embed_modpacks.length > 0) { Object.keys(embed_modpacks).forEach((k) => { const modpack_file = wtvshared.getServiceDep("/viewergen/" + modpacks[k].file, true); if (fs.existsSync(modpack_file)) { const modpack_zip = new AdmZip(modpack_file); const zipEntries = modpack_zip.getEntries(); zipEntries.forEach(function (zipEntry) { zip.addFile(zipEntry.entryName, zipEntry.getData()); }); } }); } } data = zip.toBuffer(); } } } } else { headers = `200 OK Content-Type: text/html` data = `
Welcome to the zefie minisrv v${minisrv_config.version} PC Services