diff --git a/.gitignore b/.gitignore index 9491a2fd..4a8a2dea 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,5 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +/hacktv_updsrv/ServiceLogPost/1626307222_warning_812bf30600b002bb diff --git a/hacktv_updsrv/.gitignore b/hacktv_updsrv/.gitignore new file mode 100644 index 00000000..9e06e212 --- /dev/null +++ b/hacktv_updsrv/.gitignore @@ -0,0 +1,366 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# ServiceLogPost Posted Logs +ServiceLogPost/*_* + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd +/hacktv_updsrv/ServiceLogPost/1626307222_warning_812bf30600b002bb diff --git a/hacktv_updsrv/README.md b/hacktv_updsrv/README.md index 19772a65..6d21e995 100644 --- a/hacktv_updsrv/README.md +++ b/hacktv_updsrv/README.md @@ -1,3 +1,23 @@ -# hacktv_updsrv +# wtv minisrv node.js +The ***wtv minisrv***, or "***hacktv_updsrv***" project is an ambitious node.js project to have a mini WebTV server that supports wtv-encryption for advanced level access. +This open source server is not yet ready for public use, but is available for anyone wanting to try to help advance it. + +Current status: +- Can encrypt and decrypt SECURE ON and arbitrary encrypted data +- Can handle psuedo encryption (box sends SECURE ON but does not encrypt) +- Can handle client "relogin" and "reconnect" events + +Current issues: +- Probably can't handle more than one box at a time +- Power cycling box and re-connecting via ConnectSetup may invalidate encryption until server is restarted +- wtv-update:/update does not yet function as intended + +Feature Todo: +- Test and enable flashrom flashing functionality (at least for LC2 and higher) +- Proper wtv-star (generic service error page) support. +- (maybe) implement HTTP proxy (needs to be able to defluff most of the web, think retro WAP converter) +- (maybe) enable "internet mode" (let user outside of minisrv) +- (maybe) wtvchat stuff +- (probably not) url tokenizer \ No newline at end of file diff --git a/hacktv_updsrv/ServiceLogPost/client-posted wtv-log requests will appear here.txt b/hacktv_updsrv/ServiceLogPost/client-posted wtv-log requests will appear here.txt new file mode 100644 index 00000000..e69de29b diff --git a/hacktv_updsrv/ServiceVault/htv-update/upd/update.txt b/hacktv_updsrv/ServiceVault/htv-update/upd/update.txt deleted file mode 100644 index 4a771cc2..00000000 --- a/hacktv_updsrv/ServiceVault/htv-update/upd/update.txt +++ /dev/null @@ -1,7 +0,0 @@ -GROUP name=hacktv version=!VERS! root=file://Disk/Browser/ service-owned - display Updating HackTV Files... - sync Games/Games.html Games.html - sync Games/cSetup.html cSetup.html - sync Games/updater.html updater.html - sync MattMan/Tricks/tricks.html tricks.html -END-GROUP diff --git a/hacktv_updsrv/ServiceVault/htv-update/upd/updater.html b/hacktv_updsrv/ServiceVault/htv-update/upd/updater.html deleted file mode 100644 index bd4058be..00000000 --- a/hacktv_updsrv/ServiceVault/htv-update/upd/updater.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - Retrieving Files - - - - - - - -
- - - - - - -
- Retrieving Files -
-
-
-
- - - -
- - - Your HackTV Box is downloading updates from zefie's server. -

This may take a while. - -

- -

- - - -
- - diff --git a/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js b/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js index 1e607636..325faf39 100644 --- a/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js +++ b/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js @@ -1,7 +1,7 @@ if (socket_session_data[socket.id].ssid != null && !sec_session[socket_session_data[socket.id].ssid]) { - sec_session[socket_session_data[socket.id].ssid] = new WTVNetworkSecurity(); + sec_session[socket_session_data[socket.id].ssid] = new WTVSec(); sec_session[socket_session_data[socket.id].ssid].IssueChallenge(); - sec_session[socket_session_data[socket.id].ssid].set_incarnation(initial_headers['wtv-incarnation']); + sec_session[socket_session_data[socket.id].ssid].set_incarnation(request_headers['wtv-incarnation']); } var contype = "text/tellyscript"; @@ -20,9 +20,10 @@ Connection: Keep-Alive wtv-initial-key: ` + issueWTVInitialKey(socket) + ` Content-Type: `+ contype + ` wtv-service: reset -`+getServiceString('wtv-star')+` -`+getServiceString('wtv-head-waiter')+` -`+getServiceString('wtv-flashrom')+` +` + getServiceString('wtv-1800') + ` +` + getServiceString('wtv-star') + ` +` + getServiceString('wtv-head-waiter') + ` +` + getServiceString('wtv-flashrom') + ` wtv-boot-url: wtv-1800:/preregister?relogin=true wtv-visit: wtv-head-waiter:/login? wtv-client-time-zone: GMT -0000 diff --git a/hacktv_updsrv/ServiceVault/wtv-1800/preregister.js b/hacktv_updsrv/ServiceVault/wtv-1800/preregister.js index 41ef0928..bd9909f4 100644 --- a/hacktv_updsrv/ServiceVault/wtv-1800/preregister.js +++ b/hacktv_updsrv/ServiceVault/wtv-1800/preregister.js @@ -2,33 +2,13 @@ var gourl = "wtv-1800:/finish-prereg?"; if (query['relogin']) gourl += "relogin=true"; -if (query['reconnect']) { - headers = `200 OK +if (request_headers['wtv-ticket']) { + gourl = "wtv-head-waiter:/login-stage-two?"; +} + +headers = `200 OK Connection: Keep-Alive wtv-expire-all: wtv- -wtv-expire-all: htv-` - - if (sec_session[initial_headers['wtv-client-serial-number']].ticket_b64) { - headers += "wtv-encrypted: true\n"; - headers += "wtv-ticket: " + sec_session[initial_headers['wtv-client-serial-number']].ticket_b64 + "\n"; - } - - headers += `wtv-client-time-zone: GMT -0000 -wtv-client-time-dst-rule: GMT -wtv-client-date: `+ strftime("%a, %d %b %Y %H:%M:%S", new Date(new Date().toUTCString())) + ` GMT -Content-type: text/html`; -} else { - - if (initial_headers['wtv-ticket']) { - gourl = "wtv-head-waiter:/login-stage-two?"; - } - - headers = `200 OK -Connection: Keep-Alive -wtv-expire-all: wtv- -wtv-expire-all: htv- wtv-open-isp-disabled: false wtv-visit: `+ gourl + ` Content-type: text/html`; - -} \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-flashrom/get-lc2-page.js b/hacktv_updsrv/ServiceVault/wtv-flashrom/get-lc2-page.js new file mode 100644 index 00000000..dd5ff305 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-flashrom/get-lc2-page.js @@ -0,0 +1,98 @@ +headers = `200 OK +Content-type: text/html` + +data = ` + + +Updating + + + + + + + + + +
+ + + + + +
+Updating now... +
+
+
+
+
+ + + + + + + + + + + +
+
+ + + +Your Internet Receiver is being
updated automagically. +

+This will take forever, and then
your WebTV will reboot.

+
+

+ + + + + +
    Receiving part `+flashrom_part+` of `+totalprts+`v`+flashrom_version+` (`+flashrom_type+`)    
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +` \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-flashrom/lc2-download-failed.html b/hacktv_updsrv/ServiceVault/wtv-flashrom/lc2-download-failed.html new file mode 100644 index 00000000..19abc99a --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-flashrom/lc2-download-failed.html @@ -0,0 +1,50 @@ + + + +Updating failed + + + + + + + + + +
+ + + + + + + + +
+Updated failed +
+
+
+
+ +
+ + + + + + + + + + + +
+
+ + + +Update failed, gomennasai. + + \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-flashrom/lc2-download-failed.js b/hacktv_updsrv/ServiceVault/wtv-flashrom/lc2-download-failed.js new file mode 100644 index 00000000..de953f1f --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-flashrom/lc2-download-failed.js @@ -0,0 +1,53 @@ +headers = `200 OK +Content-type: text/html` + +data =` + + +Updating failed + + + + + + + + + +
+ + + + + + + + +
+Updated failed +
+
+
+
+ +
+ + + + + + + + + + + + +
+ + + +Update failed, gomennasai. + +` \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.js b/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.js new file mode 100644 index 00000000..2e5eef92 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.js @@ -0,0 +1,20 @@ +// willie is just a graphical frontend to a list of ROMs +// the rest of the scripts should work if you manually link to a ROM, and actually have it. + + +const options = new URL('http://wtv.zefie.com/willie.php?flash='+getSessionData(socket_session_data[socket.id].ssid, 'wtv-client-rom-type')) +var data_ready = false; +data = ''; +const req = http.request(options, res => { + console.log(`statusCode: ${res.statusCode}`) + + res.on('data', d => { + data += d; + }) + + res.on('end', function () { + data_ready = true; + }); +}); + +headers = "200 OK\nContent-type: text/html"; \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js index ea99b6e5..ec678b8c 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js @@ -2,10 +2,10 @@ var challenge_response, challenge_header = ''; var gourl; if (socket_session_data[socket.id].ssid !== null) { - if (initial_headers['wtv-ticket']) { - if (initial_headers['wtv-ticket'].length > 8) { - DecodeTicket(initial_headers['wtv-ticket']); - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = initial_headers['wtv-ticket']; + if (request_headers['wtv-ticket']) { + if (request_headers['wtv-ticket'].length > 8) { + DecodeTicket(request_headers['wtv-ticket']); + sec_session[socket_session_data[socket.id].ssid].ticket_b64 = request_headers['wtv-ticket']; //socket_session_data[socket.id].secure == true; } } else if (sec_session[socket_session_data[socket.id].ssid].ticket_b64 == null) { diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js index 9c2f99be..27d81232 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js @@ -3,15 +3,15 @@ var gourl; if (socket_session_data[socket.id].ssid !== null) { if (sec_session[socket_session_data[socket.id].ssid].ticket_b64 == null) { - if (initial_headers['wtv-ticket']) { - if (initial_headers['wtv-ticket'].length > 8) { - sec_session[socket_session_data[socket.id].ssid].DecodeTicket(initial_headers['wtv-ticket']); - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = initial_headers['wtv-ticket']; + if (request_headers['wtv-ticket']) { + if (request_headers['wtv-ticket'].length > 8) { + sec_session[socket_session_data[socket.id].ssid].DecodeTicket(request_headers['wtv-ticket']); + sec_session[socket_session_data[socket.id].ssid].ticket_b64 = request_headers['wtv-ticket']; //socket_session_data[socket.id].secure = true; } } else { challenge_response = sec_session[socket_session_data[socket.id].ssid].challenge_response; - var client_challenge_response = initial_headers['wtv-challenge-response'] || null; + var client_challenge_response = request_headers['wtv-challenge-response'] || null; if (challenge_response && client_challenge_response) { //if (challenge_response.toString(CryptoJS.enc.Base64).substring(0,85) == client_challenge_response.substring(0,85)) { if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) { diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js index 7ae2ae77..8c8ead24 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js @@ -1,16 +1,16 @@ var challenge_response, challenge_header = ''; if (socket_session_data[socket.id].ssid !== null) { - if (initial_headers['wtv-ticket']) { + if (request_headers['wtv-ticket']) { if (sec_session[socket_session_data[socket.id].ssid].ticket_b64 == null) { - if (initial_headers['wtv-ticket'].length > 8) { - sec_session[socket_session_data[socket.id].ssid].DecodeTicket(initial_headers['wtv-ticket']); - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = initial_headers['wtv-ticket']; + if (request_headers['wtv-ticket'].length > 8) { + sec_session[socket_session_data[socket.id].ssid].DecodeTicket(request_headers['wtv-ticket']); + sec_session[socket_session_data[socket.id].ssid].ticket_b64 = request_headers['wtv-ticket']; } } } else { challenge_response = sec_session[socket_session_data[socket.id].ssid].challenge_response; - var client_challenge_response = initial_headers['wtv-challenge-response'] || null; + var client_challenge_response = request_headers['wtv-challenge-response'] || null; if (challenge_response && client_challenge_response) { if (challenge_response.toString(CryptoJS.enc.Base64).substring(0,85) == client_challenge_response.substring(0,85)) { console.log(" * wtv-challenge-response success for "+socket_session_data[socket.id].ssid); @@ -25,9 +25,9 @@ if (socket_session_data[socket.id].ssid !== null) { } /* -if (initial_headers) { +if (request_headers) { var cookiedata = {}; - Object.keys(initial_headers).forEach(function (k) { + Object.keys(request_headers).forEach(function (k) { switch (k) { case "wtv-capability-flags": case "wtv-system-version": @@ -36,7 +36,7 @@ if (initial_headers) { case "wtv-system-chipversion": case "wtv-system-sysconfig": case "wtv-system-cpuspeed": - cookiedata[k] = initial_headers[k]; + cookiedata[k] = request_headers[k]; break; } }); diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/test.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/test.js index 93abf3d0..14640344 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/test.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/test.js @@ -1,2 +1,2 @@ -var wtvtest = new WTVNetworkSecurity(); +var wtvtest = new WTVSec(); wtvtest.Test(); \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-home/home.js b/hacktv_updsrv/ServiceVault/wtv-home/home.js index b8abbdb7..42cc642e 100644 --- a/hacktv_updsrv/ServiceVault/wtv-home/home.js +++ b/hacktv_updsrv/ServiceVault/wtv-home/home.js @@ -4,7 +4,7 @@ wtv-expire-all: wtv-home:/splash wtv-expire-all: htv- Content-type: text/html` -if (initial_headers['psuedo-encryption']) { +if (getSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption')) { var cryptstatus = "Psuedo-encrypted"; } else { var cryptstatus = ((socket_session_data[socket.id].secure === true) ? "Encrypted" : "Not Encrypted") @@ -13,7 +13,8 @@ if (initial_headers['psuedo-encryption']) { data =` - +Home for minsrv + -

Encryption Status: `+cryptstatus+`

` +

Welcome to `+ z_title + `

+

Encryption Status: `+cryptstatus+`

` if (socket_session_data[socket.id].secure) { data += 'Encryption Key (Server): ' + sec_session[socket.id].session_key2.toString(CryptoJS.enc.Hex)+'
'; data += 'Encryption Key (Client): ' + sec_session[socket.id].session_key1.toString(CryptoJS.enc.Hex)+'


'; } -data += `client:relog (via text/url)
-client:relog (direct)
-HackTV Updater Test
- +data += `client:relog (direct)
+Clear Cache
+HackTV Updater Test
+Ultra Willies
+Disconnect and go to HackTV Home
+ ` \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-home/splash.txt b/hacktv_updsrv/ServiceVault/wtv-home/splash.txt index c97f3d67..123e8313 100644 --- a/hacktv_updsrv/ServiceVault/wtv-home/splash.txt +++ b/hacktv_updsrv/ServiceVault/wtv-home/splash.txt @@ -4,8 +4,9 @@ wtv-expire-all: htv- Content-type: text/html +Engaging zefie... - +
- +

diff --git a/hacktv_updsrv/ServiceVault/wtv-log/log.js b/hacktv_updsrv/ServiceVault/wtv-log/log.js index 9a515fec..ec6b8c30 100644 --- a/hacktv_updsrv/ServiceVault/wtv-log/log.js +++ b/hacktv_updsrv/ServiceVault/wtv-log/log.js @@ -1,4 +1,13 @@ -// dummy page, we could handle the logs here. +// write posted log data to disk. should be decrypted by this point (if it was encrypted) if the crypto stream didn't break + +if (request_headers['post_data']) { + var fullpath = __dirname + "/ServiceLogPost/" + Math.floor(new Date().getTime() / 1000) + "_" + query['type']; + if (socket_session_data[socket.id].ssid) fullpath += "_" + socket_session_data[socket.id].ssid; + + fullpath = fullpath.replace(/\\/g, "/"); + fs.writeFileSync(fullpath, request_headers['post_data'].toString(CryptoJS.enc.Hex), "Hex"); + console.log("Wrote POST log data from", socket_session_data[socket.id].ssid, "to", fullpath, "on", socket.id); +} headers = `200 OK Connection: Keep-Alive diff --git a/hacktv_updsrv/ServiceVault/wtv-log/phone-log.js b/hacktv_updsrv/ServiceVault/wtv-log/phone-log.js deleted file mode 100644 index 78cd7415..00000000 --- a/hacktv_updsrv/ServiceVault/wtv-log/phone-log.js +++ /dev/null @@ -1,10 +0,0 @@ -// dummy page, we could handle the logs here. - -headers = `200 OK -Connection: Keep-Alive -wtv-visit: wtv-home:/splash? -Content-length: 0`; - -data = ''; - - diff --git a/hacktv_updsrv/ServiceVault/wtv-star/images/HackTVLogo.gif b/hacktv_updsrv/ServiceVault/wtv-star/images/HackTVLogo.gif new file mode 100644 index 00000000..7df20035 Binary files /dev/null and b/hacktv_updsrv/ServiceVault/wtv-star/images/HackTVLogo.gif differ diff --git a/hacktv_updsrv/ServiceVault/wtv-star/images/HackTVLogoJewel.gif b/hacktv_updsrv/ServiceVault/wtv-star/images/HackTVLogoJewel.gif new file mode 100644 index 00000000..07a0f4b8 Binary files /dev/null and b/hacktv_updsrv/ServiceVault/wtv-star/images/HackTVLogoJewel.gif differ diff --git a/hacktv_updsrv/ServiceVault/wtv-star/images/WebTVLogoJewel.gif b/hacktv_updsrv/ServiceVault/wtv-star/images/WebTVLogoJewel.gif new file mode 100644 index 00000000..ec3e51dd Binary files /dev/null and b/hacktv_updsrv/ServiceVault/wtv-star/images/WebTVLogoJewel.gif differ diff --git a/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.txt b/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.txt new file mode 100644 index 00000000..d25940c4 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.txt @@ -0,0 +1,14 @@ +200 OK +wtv-noback-all: wtv- +wtv-expire-all: wtv- +Content-type: text/html + + + + + + + \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-tricks/go-offline.js b/hacktv_updsrv/ServiceVault/wtv-tricks/go-offline.js new file mode 100644 index 00000000..f43dad67 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-tricks/go-offline.js @@ -0,0 +1,42 @@ +headers = `200 OK +wtv-noback-all: wtv- +wtv-expire-all: wtv- +Content-type: text/html +wtv-service: reset +`+getServiceString('wtv-1800'); + + +// HackTV Homepage is default +var url="file://Disk/Browser/Games/Games.html"; + +if (query['url']) { + url = query['url']; +} + +data = ` + +Going offline... + + + +

+` +if (query['title']) { + data += "Going offline and loading "+decodeURI(query['title'])+", please wait!"; +} else { + data += "Please wait a moment."; +} + +data += "\n"; diff --git a/hacktv_updsrv/ServiceVault/wtv-update/content/diskmaps/htvupdate.txt b/hacktv_updsrv/ServiceVault/wtv-update/content/diskmaps/htvupdate.txt new file mode 100644 index 00000000..d2d97268 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-update/content/diskmaps/htvupdate.txt @@ -0,0 +1,10 @@ +GROUP name=Browser version=!VERS! root=file://Disk/Browser/Games/ + display Updating HackTV Files... + sync Games.html content/htvupdate/Games/Games.html + sync cSetup.html content/htvupdate/Games/cSetup.html +END-GROUP + +GROUP name=Browser version=!VERS! root=file://Disk/Browser/MattMan/ + display Updating HackTV Files... + sync Tricks/tricks.html content/htvupdate/MattMan/Tricks/tricks.html +END-GROUP \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/htv-update/upd/Games.html b/hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/Games/Games.html similarity index 95% rename from hacktv_updsrv/ServiceVault/htv-update/upd/Games.html rename to hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/Games/Games.html index 1acd2e95..a76fca83 100644 --- a/hacktv_updsrv/ServiceVault/htv-update/upd/Games.html +++ b/hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/Games/Games.html @@ -81,7 +81,7 @@ if (document.images) {
  • HTML Viewer
  • HackTV Tricks
  • Connect Setup -
  • Test +
  • NEW! Check for Updates
  • diff --git a/hacktv_updsrv/ServiceVault/htv-update/upd/cSetup.html b/hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/Games/cSetup.html similarity index 100% rename from hacktv_updsrv/ServiceVault/htv-update/upd/cSetup.html rename to hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/Games/cSetup.html diff --git a/hacktv_updsrv/ServiceVault/htv-update/upd/tricks.html b/hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/MattMan/Tricks/tricks.html similarity index 100% rename from hacktv_updsrv/ServiceVault/htv-update/upd/tricks.html rename to hacktv_updsrv/ServiceVault/wtv-update/content/htvupdate/MattMan/Tricks/tricks.html diff --git a/hacktv_updsrv/ServiceVault/wtv-update/sync.js b/hacktv_updsrv/ServiceVault/wtv-update/sync.js new file mode 100644 index 00000000..55195a58 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-update/sync.js @@ -0,0 +1,36 @@ +var content_dir = service_dir + '/content/'; +var diskmap_dir = content_dir + '/diskmaps/'; + + +if (query['diskmap']) { + if (fs.lstatSync(diskmap_dir + query['diskmap'] + ".txt")) { + var diskmap_data = fs.readFileSync(diskmap_dir + query['diskmap'] + ".txt").toString(); + // try to parse diskmap and get an accurate timestamp for webtv versioning + // check all files in the diskmap and return the timestamp of the most recently modified + + data = ''; + var latest_file_ts = 0; + diskmap_data.split("\n").forEach(function (v) { + if (v.indexOf(" sync ") != -1) { + v = v.trim(); + var vcon = v.substring(v.indexOf("content/")); + vcon = vcon.replace("content/", content_dir) + var vconstat = Math.floor(fs.lstatSync(vcon).mtimeMs / 1000); + if (vconstat > latest_file_ts) { + latest_file_ts = vconstat + } + // todo read client post and only give whats needed + // instead of all that is available + // vconstat has the mtime of each file, we need to parse the post_data + data += v + "\n"; + } else { + data += v + "\n"; + } + }); + //data = diskmap_data.replace("!VERS!", latest_file_ts); + } +} + +headers = `200 OK +Content-type: text/download-list` + diff --git a/hacktv_updsrv/ServiceVault/htv-update/update.html b/hacktv_updsrv/ServiceVault/wtv-update/update.html similarity index 69% rename from hacktv_updsrv/ServiceVault/htv-update/update.html rename to hacktv_updsrv/ServiceVault/wtv-update/update.html index b07096b7..7c2cf193 100644 --- a/hacktv_updsrv/ServiceVault/htv-update/update.html +++ b/hacktv_updsrv/ServiceVault/wtv-update/update.html @@ -2,10 +2,10 @@ - - Retrieving Files + + HackTV Updater @@ -18,7 +18,7 @@
    - Retrieving Files + Download HackTV Updates
    @@ -31,7 +31,7 @@ @@ -40,7 +40,7 @@

    - Your HackTV Box is downloading updates from zefie's server. + Your HackTV Unit is downloading updates.

    This may take a while.

    diff --git a/hacktv_updsrv/ServiceVault/htv-update/updatesuccess.txt b/hacktv_updsrv/ServiceVault/wtv-update/updatesuccess.txt similarity index 76% rename from hacktv_updsrv/ServiceVault/htv-update/updatesuccess.txt rename to hacktv_updsrv/ServiceVault/wtv-update/updatesuccess.txt index 5d3cf0cf..9655cb3d 100644 --- a/hacktv_updsrv/ServiceVault/htv-update/updatesuccess.txt +++ b/hacktv_updsrv/ServiceVault/wtv-update/updatesuccess.txt @@ -1,3 +1,4 @@ +200 OK Content-Type: text/url client:ShowAlert?message=HackTV%20Update%20was%20successful%21&buttonlabel2=Go%20to%20HackTV&action2=file%3A%2F%2FDisk%2FBrowser%2FGames%2FGames.html&buttonlabel1=Okay&buttonaction1=client:goback&image=file://disk/browser/Games/hacktv2.gif&noback=true \ No newline at end of file diff --git a/hacktv_updsrv/app.js b/hacktv_updsrv/app.js index 40c8a5e3..dce6d78f 100644 --- a/hacktv_updsrv/app.js +++ b/hacktv_updsrv/app.js @@ -7,13 +7,16 @@ const strftime = require('strftime'); const net = require('net'); const CryptoJS = require('crypto-js'); const mime = require('mime-types'); -var WTVNetworkSecurity = require('./wtvsec.js'); +const crc16 = require('node-crc16'); +var WTVSec = require('./wtvsec.js'); -var zdebug = true; +var zdebug = false; var pubip = "192.168.11.8"; var ports = []; +var service_vault_dir = __dirname + "/ServiceVault"; + //pubip = getPublicIP(); function getServiceString(service) { @@ -37,6 +40,8 @@ var sec_session = new Array(); var socket_buffer = new Array(); var socket_session_data = new Array(); +var script_processing_timeout = 10; // seconds + var overrides = new Array(); //overrides['initial_key'] = "CC5rWmRUE0o="; //overrides['challenge'] = "0kjyqIYAu0ziFBbSERN6DGaZ6S0fT+DBUCtpHCJ4lpuM7CbXdAm+x83BIDoJYztd1Z+5KFZ7ghmb3LJCT/6mhWUYkqqKOyfPRW8ZIdbICK/CV+Kxm8EUjRXZSk/97tsmFpH3hcCJ7C2TBw+TX38uQQ=="; @@ -108,8 +113,8 @@ function doErrorPage(code) { var headers, data = null; switch (code) { case 404: - data = "The resource you requested could not be found."; - headers = "HTTP/1.1 404 Not Found\r\n"; + data = "The service could not find the requested page."; + headers = "404 "+data+"\r\n"; headers += "Content-Type: text/html\r\n"; break; case 400: @@ -128,7 +133,15 @@ function doErrorPage(code) { return new Array(headers, data); } -function processPath(socket, path, initial_headers = new Array(), query = new Array()) { +function getConType(path) { + // custom contype for flashrom + if (path.indexOf("wtv-flashrom") && (path.substring(path.length - 4, path.length) == ".rom" || path.substring(path.length - 5, path.length) == ".brom")) { + return "binary/x-wtv-flashblock"; + } + return mime.lookup(path); +} + +async function processPath(socket, path, request_headers = new Array(), query = new Array(), service_name) { var headers, data = null; var request_is_direct_file = false; path = path.replace(/\\/g, "/"); @@ -145,32 +158,36 @@ function processPath(socket, path, initial_headers = new Array(), query = new Ar if (request_is_direct_file) { // file exists, read it and return it console.log(" * Found " + path + " to handle request (Direct File Mode) [Socket " + socket.id +"]"); - var contype = mime.lookup(path); + var contype = getConType(path); data = fs.readFileSync(path).buffer; headers = "200 OK\n" headers += "Content-Type: " + contype; } else if (fs.existsSync(path + ".txt")) { // raw text format, entire payload expected (headers and content) console.log(" * Found " + path + ".txt to handle request (Raw TXT Mode) [Socket " + socket.id +"]"); - var fdat = fs.readFileSync(path + ".txt").toString(); - if (fdat.indexOf("\n\n") > 0) { - var fdata = fdat.split("\n\n"); - headers = fdata[0]; - fdata.shift(); - data = fdata.join("\n"); - } else if (fdat.indexOf("\r\n\r\n") > 0) { - var fdata = fdat.split("\r\n\r\n"); - headers = fdata[0].replace(/\r/g, ""); - fdata.shift(); - data = fdata.join("\r\n"); + var file_raw = fs.readFileSync(path + ".txt").toString(); + if (file_raw.indexOf("\n\n") > 0) { + var file_raw_split = file_raw.split("\n\n"); + headers = file_raw_split[0]; + file_raw_split.shift(); + data = file_raw_split.join("\n"); + } else if (file_raw.indexOf("\r\n\r\n") > 0) { + var file_raw_split = file_raw.split("\r\n\r\n"); + headers = file_raw_split[0].replace(/\r/g, ""); + file_raw_split.shift(); + data = file_raw_split.join("\r\n"); + } else { + headers = fdat; } } else if (fs.existsSync(path + ".js")) { // js scripting, process with vars, must set 'headers' and 'data' appropriately. // loaded script will have r/w access to any JavaScript vars this function does. // any query args are in an array named 'query' - console.log(" * Found " + path + ".js to handle request (JS Interpreter mode) [Socket "+socket.id+"]"); - var fdat = fs.readFileSync(path + ".js").toString(); - eval(fdat); + console.log(" * Found " + path + ".js to handle request (JS Interpreter mode) [Socket " + socket.id + "]"); + var service_dir = service_vault_dir.replace(/\\/g,"/") + "/" + service_name; + socket_session_data[socket.id].starttime = Math.floor(new Date().getTime() / 1000); + var jscript_eval = fs.readFileSync(path + ".js").toString(); + eval(jscript_eval); } else if (fs.existsSync(path + ".html")) { // Standard HTML with no headers, WTV Style console.log(" * Found " + path + ".html to handle request (HTML Mode) [Socket " + socket.id +"]"); @@ -194,6 +211,8 @@ function processPath(socket, path, initial_headers = new Array(), query = new Ar var errpage = doErrorPage(400); headers = errpage[0]; data = errpage[1]; + console.log(" * Scripting or Data error: Headers were not defined. (headers,data) as follows:") + console.log(headers,data) } if (data === null) { data = ''; @@ -214,53 +233,56 @@ function processPath(socket, path, initial_headers = new Array(), query = new Ar return new Array(headers, data); } -function processURL(socket, initial_headers) { - if (initial_headers === null) { +async function processURL(socket, request_headers) { + if (request_headers === null) { return; } var shortURL, headers, data = ""; var query = new Array(); - if (initial_headers['request_url']) { - if (initial_headers['request_url'].indexOf('?') >= 0) { - shortURL = initial_headers['request_url'].split('?')[0]; - var qraw = initial_headers['request_url'].split('?')[1]; + if (request_headers['request_url']) { + if (request_headers['request_url'].indexOf('?') >= 0) { + shortURL = request_headers['request_url'].split('?')[0]; + var qraw = request_headers['request_url'].split('?')[1]; if (qraw.length > 0) { qraw = qraw.split("&"); for (let i = 0; i < qraw.length; i++) { - query[qraw[i].split("=")[0]] = qraw[i].split("=")[1]; - } - if (zdebug) { - console.log("URL Request has query arguments:") - console.log(query); + var k = qraw[i].split("=")[0]; + if (k) { + query[k] = qraw[i].split("=")[1]; + } } + + console.log(" * Request query:", query); } } else { - shortURL = initial_headers['request_url']; + shortURL = request_headers['request_url']; } if (shortURL.indexOf(':/') >= 0) { var ssid = socket_session_data[socket.id].ssid; if (ssid == null) { - ssid = initial_headers['wtv-client-serial-number']; + ssid = request_headers['wtv-client-serial-number']; } var reqverb = "Request"; - if (initial_headers['encrypted'] || initial_headers['secure']) { + if (request_headers['encrypted'] || request_headers['secure']) { reqverb = "Encrypted " + reqverb; } - if (initial_headers['psuedo-encryption']) { + if (request_headers['psuedo-encryption']) { reqverb = "Psuedo-encrypted " + reqverb; } if (ssid != null) { - console.log(" * "+reqverb+" for " + initial_headers['request_url'] + " from WebTV SSID " + ssid, 'on', socket.id); + console.log(" * "+reqverb+" for " + request_headers['request_url'] + " from WebTV SSID " + ssid, 'on', socket.id); } else { - console.log(" * "+reqverb+" for " + initial_headers['request_url'], 'on', socket.id); + console.log(" * "+reqverb+" for " + request_headers['request_url'], 'on', socket.id); } // assume webtv since there is a :/ in the GET - var urlToPath = __dirname + "/ServiceVault/" + shortURL.split(':/')[0] + "/" + shortURL.split(':/')[1]; - if (zdebug) console.log(initial_headers); - var result = processPath(socket, urlToPath, initial_headers, query); + var service_name = shortURL.split(':/')[0]; + var urlToPath = service_vault_dir.replace(/\\/g, "/") + "/" + service_name + "/" + shortURL.split(':/')[1]; + console.log(request_headers); + var result = await processPath(socket, urlToPath, request_headers, query, service_name); if (result[0] == null) { + // error processing path var errpage = doErrorPage(404); headers = errpage[0]; data = errpage[1]; @@ -278,6 +300,7 @@ function processURL(socket, initial_headers) { } } } else { + // error reading headers (no request_url provided) var errpage = doErrorPage(400); headers = errpage[0]; data = errpage[1]; @@ -315,7 +338,8 @@ function processURL(socket, initial_headers) { headers_obj = moveObjectElement('Connection','http_response', headers_obj); } - if (initial_headers['psuedo-encryption']) { + if (request_headers['psuedo-encryption'] || getSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption')) { + request_headers['psuedo-encryption'] = true; headers_obj['wtv-encrypted'] = true; headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj); } @@ -411,20 +435,24 @@ function headersAreStandard(string, verbose) { // we can better process it with the raw base64 data in processHeaders() below. var test = /^([A-Za-z0-9\+\/\=\-\.\,\ \;\:\?\&\r\n\(\)\%\<\>\_]{8,})$/.test(string); if (verbose) { - if (zdebug) console.log("request is ascii: " + test); - if (zdebug) console.log("request is SECURE ON: " + /^SECURE ON/.test(string)); + if (zdebug) console.log(" # Request is ascii: " + test); + if (zdebug) console.log(" # Request is SECURE ON: " + /^SECURE ON/.test(string)); } return test; } -function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, encryptedRequest = false) { +async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, encryptedRequest = false) { var url = ""; var data = CryptoJS.enc.Latin1.stringify(CryptoJS.enc.Hex.parse(data_hex)); var headers = new Array(); if (typeof data === "string") { if (data.length > 1) { - data = data.split("\r\n\r\n")[0]; + if (data.indexOf("\r\n\r\n") != -1) { + data = data.split("\r\n\r\n")[0]; + } else { + data = data.split("\n\n")[0]; + } if (headersAreStandard(data, (!returnHeadersBeforeSecure && !encryptedRequest))) { data.split('\n').forEach(function (d) { if (d.length > 0) { @@ -444,29 +472,29 @@ function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, enc } }); } else if (!returnHeadersBeforeSecure) { + // if its a POST request, assume its a binary blob and not encrypted (dangerous) if (!encryptedRequest) { - // failed the headersAreStandard test, so we think this is a binary blob + // its not a POST and it 1failed the headersAreStandard test, so we think this is an encrypted blob if (socket_session_data[socket.id].secure != true) { // first time so reroll sessions - sec_session[socket.id] = new WTVNetworkSecurity(); + sec_session[socket.id] = new WTVSec(); sec_session[socket.id].IssueChallenge(); - console.log(" [ UNEXPECTED BINARY BLOCK ] First sign of encryption, re-creating RC4 sessions for socket id",socket.id); + if (zdebug) console.log(" # [ UNEXPECTED BINARY BLOCK ] First sign of encryption, re-creating RC4 sessions for socket id",socket.id); sec_session[socket.id].SecureOn(); socket_session_data[socket.id].secure = true; } var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); if (enc_data.sigBytes > 0) { var dec_data = CryptoJS.lib.WordArray.create(sec_session[socket.id].Decrypt(0,enc_data)); - var dec_data_text = dec_data.toString(CryptoJS.enc.Latin1); - var secure_headers = processHeaders(socket, dec_data.toString(CryptoJS.enc.Hex), true, true); + var secure_headers = await processHeaders(socket, dec_data.toString(CryptoJS.enc.Hex), true, true); headers['encrypted'] = true; - console.log("Encrypted Request (Decrypted):", dec_data.toString(CryptoJS.enc.Latin1),"on",socket.id); Object.keys(secure_headers).forEach(function (k, v) { headers[k] = secure_headers[k]; }); } } } + if (headers['wtv-client-serial-number'] != null) { socket_session_data[socket.id].ssid = headers['wtv-client-serial-number']; } @@ -484,13 +512,14 @@ function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, enc } if (returnHeadersBeforeSecure) { + headers = await checkForPostData(socket, headers, data, data_hex, returnHeadersBeforeSecure); return headers; } if (headers['secure'] === true) { if (!sec_session[socket.id]) { - console.log("Starting new WTVNetworkSecurity instance on socket", socket.id); - sec_session[socket.id] = new WTVNetworkSecurity(); + console.log(" * Starting new WTVSec instance on socket", socket.id); + sec_session[socket.id] = new WTVSec(); sec_session[socket.id].DecodeTicket(headers['wtv-ticket']); sec_session[socket.id].ticket_b64 = headers['wtv-ticket']; if (getSessionData(socket_session_data[socket.id].ssid, 'incarnation')) { @@ -500,7 +529,7 @@ function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, enc } if (socket_session_data[socket.id].secure != true) { // first time so reroll sessions - console.log(" [ SECURE ON BLOCK ("+socket.id+")]"); + if (zdebug) console.log(" # [ SECURE ON BLOCK ("+socket.id+")]"); socket_session_data[socket.id].secure = true; } if (!headers['request_url']) { @@ -516,16 +545,18 @@ function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, enc if (enc_data.sigBytes > 0) { if (headersAreStandard(enc_data.toString(CryptoJS.enc.Latin1), (!returnHeadersBeforeSecure && !encryptedRequest))) { // some builds (like our targeted 3833), send SECURE ON but then unencrypted headers - console.log("Psuedo-encrypted Request (SECURE ON)", "on", socket.id); + if (zdebug) console.log(" # Psuedo-encrypted Request (SECURE ON)", "on", socket.id); // don't actually encrypt output headers['psuedo-encryption'] = true; + setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', true); socket_session_data[socket.id].secure = false; - var secure_headers = processHeaders(socket, enc_data.toString(CryptoJS.enc.Hex), true); + var secure_headers = await processHeaders(socket, enc_data.toString(CryptoJS.enc.Hex), true); } else { // SECURE ON and detected encrypted data + setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', false); var dec_data = CryptoJS.lib.WordArray.create(sec_session[socket.id].Decrypt(0, enc_data)) - var secure_headers = processHeaders(socket, dec_data.toString(CryptoJS.enc.Hex), true); - console.log("Encrypted Request (SECURE ON)", "on", socket.id); + var secure_headers = await processHeaders(socket, dec_data.toString(CryptoJS.enc.Hex), true); + if (zdebug) console.log(" # Encrypted Request (SECURE ON)", "on", socket.id); } // Merge new headers into existing headers object Object.keys(secure_headers).forEach(function (k, v) { @@ -534,6 +565,7 @@ function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, enc } } } + headers = await checkForPostData(socket, headers, data, data_hex); return headers; } else { // socket error, terminate it. @@ -543,9 +575,59 @@ function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, enc return null; } +async function checkForPostData(socket, headers, data, data_hex) { + if (headers['request']) { + if (headers['request'].substring(0, 4) == "POST") { + if (data_hex.indexOf("0d0a0d0a") != -1) { + // \r\n\r\n + var header_length = data.length + 4; + } else if (data_hex.indexOf("0a0a") != -1) { + // \n\n + var header_length = data.length + 2; + } + if (socket_session_data[socket.id].secure == true) { + var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); + if (enc_data.sigBytes > 0) { + if (headersAreStandard(enc_data.toString(CryptoJS.enc.Latin1))) { + // some builds (like our targeted 3833), send SECURE ON but then unencrypted headers + if (zdebug) console.log(" # Psuedo-encrypted POST Content (SECURE ON)", "on", socket.id); + // don't actually encrypt output + headers['psuedo-encryption'] = true; + setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', true); + socket_session_data[socket.id].secure = false; + headers['post_data'] = await processHeaders(socket, enc_data.toString(CryptoJS.enc.Hex), true); + } else { + // SECURE ON and detected encrypted data + setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', false); + headers['post_data'] = CryptoJS.lib.WordArray.create(sec_session[socket.id].Decrypt(0, enc_data)) + if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id); + } + } + } else { + if (zdebug) console.log(" # Unencrypted POST Content", "on", socket.id); + headers['post_data'] = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); + } + } + } + return headers; +} -function handleSocket(socket) { - socket.id = Math.floor(Math.random() * 100000); +async function cleanupSocket(socket) { + try { + console.log(" * Destroying old WTVSec instance on disconnected socket", socket.id); + delete socket_buffer[socket.id]; + delete socket_session_data[socket.id]; + delete sec_session[socket.id]; + socket.end(); + } catch (e) { + console.log(" # Could not clean up socket data for socket ID", socket.id, e); + } +}; + + +async function handleSocket(socket) { + // create unique socket id with client address and port + socket.id = parseInt(crc16.checkSum(Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString("hex"),16); socket_session_data[socket.id] = []; socket.setEncoding('hex'); //set data encoding (either 'ascii', 'utf8', or 'base64') socket.on('data', function (data_hex) { @@ -557,28 +639,26 @@ function handleSocket(socket) { } }); - socket.on('timeout', function () { + socket.on('timeout', async function () { socket.setTimeout(0); - processURL(this, processHeaders(this, socket_buffer[socket.id].toString(CryptoJS.enc.Hex))); + var phead = await processHeaders(this, socket_buffer[socket.id].toString(CryptoJS.enc.Hex)); + processURL(this, phead); socket_buffer[socket.id] = null; }); - socket.on('error', (err, socket) => { + socket.on('error', (err) => { console.log(" * Client disconnected unexpectedly"); + cleanupSocket(socket); + }); socket.on('end', function () { - console.log(" * Destroying old WTVNetworkSecurity instance on socket", socket.id); - delete socket_buffer[socket.id]; - delete socket_session_data[socket.id]; - delete sec_session[socket.id]; + cleanupSocket(socket); }); } -var z_version = "0.5.1a"; -var z_title = "zefie's wtv minisrv v" + z_version; +var z_title = "zefie's wtv minisrv v" + require('./package.json').version; console.log("**** Welcome to " + z_title + " ****"); - console.log(" *** Reading service configuration..."); var services_configured = JSON.parse(fs.readFileSync(__dirname + "/services.json")); Object.keys(services_configured.services).forEach(function (k) { @@ -605,7 +685,11 @@ Object.keys(services_configured.services).forEach(function (k) { var initstring = ''; ports.sort(); -ports.forEach(function (v) { + +// de-duplicate ports in case user configured multiple services on same port +const bind_ports = [...new Set(ports)] + +bind_ports.forEach(function (v) { try { var server = net.createServer(handleSocket); server.listen(v, '0.0.0.0'); diff --git a/hacktv_updsrv/hacktv_updsrv.njsproj b/hacktv_updsrv/hacktv_updsrv.njsproj index 0c783daf..4111df51 100644 --- a/hacktv_updsrv/hacktv_updsrv.njsproj +++ b/hacktv_updsrv/hacktv_updsrv.njsproj @@ -28,18 +28,31 @@ true + Code - + + + + + + + + + + + + + + + + Code + + - - - - - - + @@ -48,7 +61,6 @@ - Code @@ -57,12 +69,21 @@ - - + + + + + + + + + + + \ No newline at end of file diff --git a/hacktv_updsrv/package-lock.json b/hacktv_updsrv/package-lock.json index 7af7441a..29ba1225 100644 --- a/hacktv_updsrv/package-lock.json +++ b/hacktv_updsrv/package-lock.json @@ -1,9 +1,14 @@ { "name": "hacktv_updsrv", - "version": "0.0.0", + "version": "0.5.2", "lockfileVersion": 1, "requires": true, "dependencies": { + "buffer-factory": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/buffer-factory/-/buffer-factory-1.0.8.tgz", + "integrity": "sha512-EkUcaWsg7Vw+bfpGrpqzDIV4u3FksbHy0I0x9IZogqtCh9rZ5IHPTs/1QtZNNm/8BOeznUKTXIA72sVXEmFREQ==" + }, "crypto-js": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", @@ -27,6 +32,20 @@ "mime-db": "1.48.0" } }, + "node-addon-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz", + "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==" + }, + "node-crc16": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/node-crc16/-/node-crc16-2.0.7.tgz", + "integrity": "sha512-dbLudH39wvydMXsT5E/jdyEP0t/kO3EOFQawVeBk3cJP23+jL4feoCWF2kMThEvijrEt2isFx2XzYjLkf0h8+g==", + "requires": { + "buffer-factory": "1.0.8", + "node-addon-api": "3.0.0" + } + }, "strftime": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.0.tgz", diff --git a/hacktv_updsrv/package.json b/hacktv_updsrv/package.json index 26eb241a..bf401f16 100644 --- a/hacktv_updsrv/package.json +++ b/hacktv_updsrv/package.json @@ -1,6 +1,6 @@ { "name": "hacktv_updsrv", - "version": "0.0.0", + "version": "0.5.2", "description": "hacktv_updsrv", "main": "app.js", "author": { @@ -10,6 +10,7 @@ "crypto-js": "^4.0.0", "endianness": "^8.0.2", "mime-types": "^2.1.31", + "node-crc16": "^2.0.7", "strftime": "^0.10.0" } } diff --git a/hacktv_updsrv/services.json b/hacktv_updsrv/services.json index 19194250..82d4a766 100644 --- a/hacktv_updsrv/services.json +++ b/hacktv_updsrv/services.json @@ -14,8 +14,9 @@ "flags": "0x00000001", "connections": 1 }, - "htv-update": { + "wtv-update": { "port": 1619, + "flags": "0x04", "connections": 3 }, "wtv-log": { @@ -27,7 +28,7 @@ "flags": "0x00000010" }, "wtv-tricks": { - "port": "1602", + "port": 1602, "flags": "0x00000004" }, "wtv-flashrom": { diff --git a/hacktv_updsrv/wtvsec.js b/hacktv_updsrv/wtvsec.js index f4315de7..c23b2d8c 100644 --- a/hacktv_updsrv/wtvsec.js +++ b/hacktv_updsrv/wtvsec.js @@ -2,7 +2,7 @@ const CryptoJS = require('crypto-js'); const endianness = require('endianness'); var crypto = require('crypto'); -class WTVNetworkSecurity { +class WTVSec { //initial_shared_key = CryptoJS.lib.WordArray.random(8); initial_shared_key_b64 = "CC5rWmRUE0o="; initial_shared_key = null; @@ -19,7 +19,7 @@ class WTVNetworkSecurity { hRC4_Key2 = null; RC4Session = new Array(); - zdebug = true; + zdebug = false; constructor(wtv_incarnation = 1) { this.zdebug = true; @@ -215,7 +215,7 @@ class WTVNetworkSecurity { SecureOn(rc4session = null) { - console.log("Generating RC4 sessions with wtv-incarnation: " + this.incarnation); + if (this.zdebug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation); var buf = new Uint8Array([0xff & this.incarnation, 0xff & (this.incarnation >> 8), 0xff & (this.incarnation >> 16), 0xff & (this.incarnation >> 24)]); endianness(buf, 4); @@ -297,4 +297,4 @@ class WTVNetworkSecurity { } } -module.exports = WTVNetworkSecurity; \ No newline at end of file +module.exports = WTVSec; \ No newline at end of file