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 = ` zefie minisrv v${minisrv_config.version}

Welcome to the zefie minisrv v${minisrv_config.version} PC Services


`; if (modpacks.length > 0) { data += ``; } data += `
Viewer Version:
SSID:
Viewer clients should use SSIDs starting with 91,
unless you are intentionally trying to spoof a box.
Startup Logo
Disk Set
Build Spoof
This legacy option has little impact on minisrv servers,
although certain advanced server operators may use these flags
to determine what your "box" can do, and as such, may offer
features that do not work in the Viewer, especially older ones
Feature Bits
None available
Mod Packs `; Object.keys(modpacks).forEach((k) => { data += `${modpacks[k].name}
     ${modpacks[k].description}
` }) data += `
Other Flags: Let the server choose the SSID (Ignores SSID above)
Only include Viewer EXE, not ROM files or Logos (Advanced Users Only)

`; }