Dnscrypt-proxy: Help needed to improve the Windows control scripts (service-*.bat)

Created on 16 Apr 2018  Â·  6Comments  Â·  Source: DNSCrypt/dnscrypt-proxy

MESSAGE:

We're need for a professional coder, who knows how to operate properly with Windows System Services using cmd, including a correct rights utilization inside the OS.

ISSUE:

Users on Windows were experiencing different installation issues during a lot amount of dnscrypt-proxy releases. Most likely, this relative to the cmd-commands represented in the *.bat-files, not to dnscrypt-proxy.exe itself.

The *.bat-files from the v2.0.9 and v2.0.10 (last for now) are broken and have critical loop, don't use it.
The last working *.bat-files are coming from the v2.0.8. They are functional, but probably, not ideal.
For example, there is an old-known "problem": after launching *.bat-files, the strange message appear at the beginning ("FAR"):

No items in the list.

Then, the usual scenarios run and finishes as expected.

Known incidents:
https://github.com/jedisct1/dnscrypt-proxy/issues/353
https://github.com/jedisct1/dnscrypt-proxy/issues/290#issuecomment-378194987
https://github.com/jedisct1/dnscrypt-proxy/issues/246
https://github.com/jedisct1/dnscrypt-proxy/issues/193
...and many others

WHAT WE HAVE

A quick look into the working "service-install.bat" from v2.0.8:

@NET session
@IF %ERRORLEVEL% NEQ 0 GOTO ELEVATE
@GOTO ADMINTASKS

:ELEVATE
ECHO Elevated privileges are temporarily required, just to register or remove the dnscrypt-proxy service
CD /d %~dp0
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1); close();"
EXIT

:ADMINTASKS

@CD /d %~dp0

.\dnscrypt-proxy.exe -service install
.\dnscrypt-proxy.exe -service start

@ECHO ""
@SET /P _=Thank you for using dnscrypt-proxy! Hit [RETURN] to finish

EXIT

TODO:

  • *.bat-files Auditing - wanted:

    service-install.bat
    service-uninstall.bat
    service-restart.bat

  • A proper cmd code for Windows System Service manipulating - wanted.

    Note: There are 2 places controlling the "Services" & "Task Manager" in Windows:

1) (Run -> Services.msc) : DNSCrypt client proxy (dnscrypt-proxy)
2) (Ctrl+Shift+Esc -> Processes) : dnscrypt-proxy.exe

Both must have been taken under control and none of them have to get stuck after Un/Re/Installations.
  • Correct rights utilization for the OS - wanted:

    • for the Service manipulation (probably, the main issue)

    • for the EventLog (take care, this is very important also)

a) Windows Registry:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\dnscrypt-proxy]

b) dnscrypt-proxy feature in the *.toml settings:

# use_syslog = true

(Note: You can use or Windows EventLog, or dnscrypt-proxy.log. Not a both at once!)


Looks like this task is not too complicated for professional Windows coder.
All people passioned by dnscript-proxy will be very thankful to you, if you can resolve this issue forever.

help wanted windows

Most helpful comment

So it looks like current elevation check using SFC didn't work for @iWARR for some reason, because the elevation itself is the same between v2.0.8 and v2.0.9.

The proper way of checking the elevation state would be using whoami system tool to find out the current Integrity Level (IL) and compare it to High IL (which is the default IL after UAC elevation and the actual thing we need here):

whoami /groups | find "S-1-16-12288" >nul && goto :AdminTasks

S-1-16-12288 is the well-known constant SID for High IL.


Also, please never do scripting like the current one with infinite recursion. Always check first whether you already started a new instance and put a fallback. E.g. by checking a command-line argument before the elevation:

:elevate
if "%~1"=="RunAsAdmin" goto :error

along with passing it in the elevated call:

shell.ShellExecute('%~nx0', 'RunAsAdmin', '', 'runas', 1);

There are more problems.

Full path to our batch file may contain "bad" characters.

Windows till 10 still has a bug because of which by default you can't run as administrator a batch file containing special characters in its name _or in the path_ to it. This is because the "open" verb command resolves to cmd.exe /c ""<path to batch file>" <arguments>", whereas the "runas" verb (used for running as admin) command is cmd.exe /C "<path>" <arguments> — so MS missed the second pair of enclosing quotes, which cmd needs. Btw one can fix it in his Windows in "runas" command registry key for "cmdfile".

To fix it without touching the user registry, we should manually run cmd the right way, so we should use the full path to our file. Which we can't just paste in HTA one-liner as a CMD reference, we should prepare it first to escape (double) all backslashes... But then one can have an apostrophe in his path and he will encounter a JavaScript error.

So I think the best way would be completely get away from popuping up HTA and use another technique of combining CMD batch code and JScript code in one file (using multiline comments and conditional compilation). Here is my version:

Click here to see an example

@set @_cmd=1 /*
@echo off
setlocal EnableExtensions
title DNSCrypt-Proxy

whoami /groups | find "S-1-16-12288" >nul && goto :admin
if "%~1"=="RunAsAdmin" goto :error

echo Requesting privileges elevation for managing the dnscrypt-proxy service . . .
cscript /nologo /e:javascript "%~f0" || goto :error
exit /b

:error
echo.
echo Error: Administrator privileges elevation failed,
echo        please manually run this script as administrator.
echo.
goto :end

:admin
pushd "%~dp0"
dnscrypt-proxy.exe -service install
dnscrypt-proxy.exe -service start
popd
echo.
echo Thank you for using DNSCrypt-Proxy!

:end
set /p =Press [Enter] to exit . . .
exit /b */

// JScript, restart batch script as administrator
var objShell = WScript.CreateObject('Shell.Application');
var ComSpec = WScript.CreateObject('WScript.Shell').ExpandEnvironmentStrings('%ComSpec%');
objShell.ShellExecute(ComSpec, '/c ""' + WScript.ScriptFullName + '" RunAsAdmin"', '', 'runas', 1);





Besides the above-mentioned it is also using:

  • pushd & popd to restore a user current dir;
  • exit /b to prevent closing user console window if the user opened it himself e.g. to check for possible errors, and to prevent exiting from any parent batch script;
  • "%~dp0" must be placed in quotes for "bad" characters in path.

P.S. Script is edited, pushd/popd moved to one place where they are needed.

All 6 comments

So it looks like current elevation check using SFC didn't work for @iWARR for some reason, because the elevation itself is the same between v2.0.8 and v2.0.9.

The proper way of checking the elevation state would be using whoami system tool to find out the current Integrity Level (IL) and compare it to High IL (which is the default IL after UAC elevation and the actual thing we need here):

whoami /groups | find "S-1-16-12288" >nul && goto :AdminTasks

S-1-16-12288 is the well-known constant SID for High IL.


Also, please never do scripting like the current one with infinite recursion. Always check first whether you already started a new instance and put a fallback. E.g. by checking a command-line argument before the elevation:

:elevate
if "%~1"=="RunAsAdmin" goto :error

along with passing it in the elevated call:

shell.ShellExecute('%~nx0', 'RunAsAdmin', '', 'runas', 1);

There are more problems.

Full path to our batch file may contain "bad" characters.

Windows till 10 still has a bug because of which by default you can't run as administrator a batch file containing special characters in its name _or in the path_ to it. This is because the "open" verb command resolves to cmd.exe /c ""<path to batch file>" <arguments>", whereas the "runas" verb (used for running as admin) command is cmd.exe /C "<path>" <arguments> — so MS missed the second pair of enclosing quotes, which cmd needs. Btw one can fix it in his Windows in "runas" command registry key for "cmdfile".

To fix it without touching the user registry, we should manually run cmd the right way, so we should use the full path to our file. Which we can't just paste in HTA one-liner as a CMD reference, we should prepare it first to escape (double) all backslashes... But then one can have an apostrophe in his path and he will encounter a JavaScript error.

So I think the best way would be completely get away from popuping up HTA and use another technique of combining CMD batch code and JScript code in one file (using multiline comments and conditional compilation). Here is my version:

Click here to see an example

@set @_cmd=1 /*
@echo off
setlocal EnableExtensions
title DNSCrypt-Proxy

whoami /groups | find "S-1-16-12288" >nul && goto :admin
if "%~1"=="RunAsAdmin" goto :error

echo Requesting privileges elevation for managing the dnscrypt-proxy service . . .
cscript /nologo /e:javascript "%~f0" || goto :error
exit /b

:error
echo.
echo Error: Administrator privileges elevation failed,
echo        please manually run this script as administrator.
echo.
goto :end

:admin
pushd "%~dp0"
dnscrypt-proxy.exe -service install
dnscrypt-proxy.exe -service start
popd
echo.
echo Thank you for using DNSCrypt-Proxy!

:end
set /p =Press [Enter] to exit . . .
exit /b */

// JScript, restart batch script as administrator
var objShell = WScript.CreateObject('Shell.Application');
var ComSpec = WScript.CreateObject('WScript.Shell').ExpandEnvironmentStrings('%ComSpec%');
objShell.ShellExecute(ComSpec, '/c ""' + WScript.ScriptFullName + '" RunAsAdmin"', '', 'runas', 1);





Besides the above-mentioned it is also using:

  • pushd & popd to restore a user current dir;
  • exit /b to prevent closing user console window if the user opened it himself e.g. to check for possible errors, and to prevent exiting from any parent batch script;
  • "%~dp0" must be placed in quotes for "bad" characters in path.

P.S. Script is edited, pushd/popd moved to one place where they are needed.

This is not the end of my sentence, final post is coming soon...

Interesting. I was going to echo >%temp%\myscript.vbs some vbscript to call ShellExecute and use, like you, a parameter to stop the looping (this is how I used to do before finding out we could use Javascript) but you manage to comment out all the command script using /* ... */ and keep using Javascript :-) Looks fine, cannot test just yet, I am at work. PUSHD/POPD is not strictly required, since SETLOCAL saves the current directory (which is why we were using CD before,) but it's fine.

EDIT: Works fine. Aren't we supposed to use double slashes with CScript, e.g. //Nologo //E ? Didn't know about @set or @if etc, but I don't know JavaScript. Well done!

v2.0.11 works nice, all *.bat scripts looking healthy. I do not expect any troubles.
dnscrypt-proxy becomes better and better.
Respect goes @Tremerix for his help resolving this long-time issue.
And thank you all for the feedback and participating.

I think, making a new issue for the new discussion/request should be a better decision.
Since issue was resolved, I don't see any barriers to close this.

Works for me too, and the script is easy enough to read and nothing looks bad, but there might still be some issue, see this comment.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dongjuanyong picture dongjuanyong  Â·  3Comments

atfreddy picture atfreddy  Â·  3Comments

Marco-vW picture Marco-vW  Â·  5Comments

EstherMoellman picture EstherMoellman  Â·  5Comments

JayBrown picture JayBrown  Â·  6Comments