You are here

PortableApps Platform on NTFS / VHD: supporting Cmd-Script

1 post / 0 new
bgks
Offline
Last seen: 2 years 6 months ago
Joined: 2010-07-21 18:13
PortableApps Platform on NTFS / VHD: supporting Cmd-Script

For my personal comfort I developed over the years a cmd-script that automates the use of a secured PortableApps platform on a NTFS-VHD, that I'd like to share ...

When used for more than administration purposes the security of the PortableApps platform suffers from not protecting the program directories against malicious writes. This kind of protection is not feasible using FAT/exFAT, while usage of NTFS on pen drives tends to notably slow down and ruin the pen drive, plus, introduces some portability issues. It is feasible by using a USB harddrive or, providing all the comfort of the PortableApps platform, using a virtual harddrive (VHD, VHDx), that can be mounted locally, mounted in virtual machines, easily transported and mounted wherever the user has the rights to mount a virtual harddrive. As for Version 16 the PortableApps platform provides not at all any support for this kind of (mis)usage, though.

Purposes of my cmd-script that automates the use of a secured PortableApps platform on a local NTFS-VHD:

  • A one click solution is provided by a link to the cmd-script !P-mount, sticked to the Task Bar, for
    • mounting the VHD and starting the PortableApps platform.
    • updating the platform whose program directories are write protected against the user land (thanks to User Account Control even if you're logged in with an administrator account) by a 'Run as Administrator'-klick.
  • Additional links provide
    • closing the platform and unmounting the VHD (!P-umount),
    • adjusting the access rights so that exclusively the data directories are user-writable for the user (one initial run of !P-accessData required after the installation of a PortableApp, that is new to the platform and requires write-access to its data directory).

For reasons of security never run a script that you don't understand! The adaption onto the local environment is necessary since hard coded into the script in between the ## marks. The script assumes to be named Install.P-mount.cmd. Assume it published under the PortableApps-License (PortableApps\PortableApps.com\Other\Source\License.txt), i.e., GPL2. Keep in your mind: this is grown code, I'm not an IT-professional, and I cannot grant any warranties. I'm running Windows 10 Pro. Suggestions for a more elegant implementation are welcome.
@setlocal DISABLEDELAYEDEXPANSION 2>nul
@set PS0=%PROMPT%
@set PS1=$E[1A$E[1;37;47m%~n0$L$L$E[0;34;47m&
@set PS2=$L$G
@set PROMPT=%PS1%
@set arg=%1
:: ################################################################################################################
@REM Mount PortableApps and Run Portableapps Platform
:: ================================================================================================================
@set VHD=¤:\¤¤¤¤¤.vhdx
@set Label=¤¤¤¤¤
@set Volume=\\?\Volume{¤¤¤¤¤¤¤-¤¤¤¤-¤¤¤¤-¤¤¤¤-¤¤¤¤¤¤¤¤¤¤¤¤}\
@set D=¤&:: DriveLetter for VHD
@set InstallDir=¤:&:: where to install this script using the following names
@set NameMount=!P-mount
@set NameUmount=!P-umount
@set NameAclData=!P-accessData
:: ================================================================================================================
@set FMS="%TMP%\mountP.%DATE%%RANDOM%.diskpart"
:: ================================================================================================================
@if "%arg%"=="/Q" set Silent=TRUE
@if "%~n0"=="%NameMount%" set arg=mount
@if "%arg%"=="mount" goto MOUNT
@if "%~n0"=="%NameUmount%" set arg=umount
@if "%arg%"=="umount" goto UMOUNT
::###########
@set UserSid=S-1-5-21-¤¤¤¤¤¤¤¤¤¤-¤¤¤¤¤¤¤¤¤¤-¤¤¤¤¤¤¤¤¤¤-¤¤¤¤& REM User that gets access to UserDirs granted.
@set TrustedInstaller=S-1-5-80-¤¤¤¤¤¤¤¤¤-¤¤¤¤¤¤¤¤¤¤-¤¤¤¤¤¤¤¤¤¤-¤¤¤¤¤¤¤¤¤¤-¤¤¤¤¤¤¤¤¤¤
::###########
@set ProgramFiles=Start.exe autorun.inf PortableApps PortableAppsOther
@set UserFiles=Documents PortableDocs
@if "%~n0"=="%NameAclData%" set arg=acldata
@if "%arg%"=="acldata" call :pACLFIX_DATA %D% & goto EXIT
@if "%arg%"=="acluserdir" set /P UserFiles=UserDir based on %D%:\^<^< && call :pACLFIX_USERPROFILE %D% &goto EXIT
@if "%arg%"=="aclfix" call :pACLFIX %D% & goto EXIT
@if "%~n0"=="Install.P-mount" goto INSTALL
REM Unkown Start-Option
goto :EOF
:: ################################################################################################################
:pGetWmiValue
REM %0 Key=%1 WmiPath=%2 Where=%3
:: Not works for bogus reasons: `wmic %2 where %3 get %1 /value^|findstr %1`
:: Works for erronous reasons: `wmic %2 where %3 get %1 /value^|findstr /R ^^.$`
set %1=
@for /F "usebackq tokens=* delims= " %%v in (
`wmic %2 where %3 get %1 /value^|findstr /R ^^.$`
) do @set %%v
@goto :EOF
:: ===========================================================================================================
:MOUNT
REM MOUNT
::
:CHECKS
mountvol %D%: /L && goto START
icacls %VHD% || goto ERROR
:MOUNT
@echo>%FMS% REM ATTACH %VHD%
@echo>>%FMS% select vdisk file=%VHD%
@echo>>%FMS% attach vdisk usefilesd
@echo ### && type %FMS% && echo ### %FMS%
@start /B /WAIT diskpart /s %FMS%
REM ERRORLEVEL %ERRORLEVEL%
mountvol %D%:\ %Volume%
goto INIT
::# ---------------------------
:INIT
@set WMIC=wmic volume where "Label='%Label%'" get ID /value
REM %WMIC%
@for /f "usebackq tokens=1,* delims==" %%v in (`%WMIC%`) do @if "%%v"=="DeviceID" @echo %%w
:START
@icacls %D%:\Start.exe
::# Kill SingleInstance first
@call :pGetWmiValue ProcessID,process,"ExecutablePath='%D%:\\PortableApps\\PortableApps.com\\PortableAppsPlatform.exe'"
REM Running %%ProcessID%%=%ProcessID%
@if defined ProcessID taskkill /PID %ProcessID% /T /F && set ProcessID=
:LOOP_ErrAdminRunning
::# Warn if an Admin-Instance is running (not exposes ExecutablePath)
@call :pGetWmiValue ProcessID,process,"Name='PortableAppsPlatform.exe'"
REM Running Admin: %%ProcessID%%=%ProcessID%
@if defined ProcessID color CE&echo.& choice /C n /N /T 1 /D n /M "Terminate running Admin-Instance!"
@if defined ProcessID @if ERRORLEVEL 1 goto :LOOP_ErrAdminRunning
@if defined ProcessID @if ERRORLEVEL 0 goto :CLEANUP
@color
@if not defined ProcessID start /B %D%:\Start.exe
goto CLEANUP
:::# ---------------------------
:ERROR
@echo ERROR
::# ---------------------------
:UMOUNT
REM UMOUNT
mountvol %D%:\ /D
@echo>%FMS% REM DETACH %VHD%
@echo>>%FMS% select vdisk file=%VHD%
@echo>>%FMS% detach vdisk
@echo ### && type %FMS% && echo ### %FMS%
@start /B /WAIT diskpart /s %FMS%
REM ERRORLEVEL %ERRORLEVEL%
::# ---------------------------
:CLEANUP
@if exist %FMS% del /F /Q %FMS%
:EXIT
@if not defined Silent echo. && pause
@goto :EOF
::# ==========================================================================================================
:pACLFIX
REM %0 DriveLetter: %1
::
:pACLFIX_RECYLCEBIN
REM %0 DriveLetter=%1
::
@set Name=%1:\^$RECYCLE.BIN
REM %1:\^$RECYCLE.BIN
@set icacls=call :pICACLS %Name%
::
::# ^<^< %SYSTEMDRIVE%\$RECYCLE.BIN
:: wmic path Win32_LogicalFileSecuritySetting where Path="%SYSTEMDRIVE%\\$RECYCLE.BIN" assoc /resultrole:owner
:: wmic path Win32_LogicalFileSecuritySetting where Path="%SYSTEMDRIVE%\\$RECYCLE.BIN" call GetSecurityDescriptor
::
::@echo OLD: %Name% && icacls %Name%
@takeown /F %Name% /A
@%icacls% /inheritance:r
@%icacls% /grant:r *S-1-5-32-544:(OI)(CI)(F)
@%icacls% /grant:r *S-1-5-18:(OI)(CI)(F)
@%icacls% /remove *S-1-5-32-545 /grant *S-1-5-32-545:(NP)(RX,AD,WA)
@%icacls% /setintegritylevel (OI)(CI)(IO)L:(NW)
::@echo NEW: %Name% && icacls %Name%
@attrib +h +s %Name%
::
:pACLFIX_PROGRAMFILES
REM %0 DriveLetter=%1
::
@set Name="%1:\%%f"
@set icacls=call :pICACLS %Name%
@for %%f in ( %ProgramFiles% ) do @call :pACLFIX_pPROGRAMFILES %Name%
::
:pACLFIX_ATTRIB
@attrib +h +s %1:\autorun.inf
@for /D %%d in (%1:\PortableApps\*) do @if exist %%d\Desktop.ini attrib +h +a %%d\Desktop.ini
::
::
:pACLFIX_USERPROFILE
REM %0 DriveLetter=%1
::
@set Name="%1:\%%f"
@for %%f in ( %UserFiles% ) do @call :pACLFIX_pUSERPROFILE %Name%
@goto :EOF
:: ----------------------------------------------------------------------------------------------------------
::
:pACLFIX_DATA
REM %0 DriverLetter=%1
@set Name="%1:\%%d"
@for /D %%d in (%1:\PortableApps\*) do @if exist %%d\Data call :pACLFIX_pAPPDATADIR %1 %%~nxd
goto EXIT
::# for PathWmic in `ls -d /mnt/p/PortableApps/*/Data|sed -e's./mnt/p.P:.' -e's./.\\\\\\\\.g'`; do %wmic% ; done
:pACLFIX_pAPPDATADIR
REM %0 DriveLetter=%1 AppName=%2
@set FilePath=%1:\PortableApps\%2\Data
@set PathWmic=%1:^\^\PortableApps^\^\%2^\^\Data
@for /F "usebackq tokens=* delims= " %%v in (
`wmic path Win32_LogicalFileSecuritySetting where "Path='%PathWmic%'" get OwnerPermissions /value^|findstr Owner`
) do @set %%v& if "%OwnerPermissions%"=="FALSE" call :pACLFIX_pUSERPROFILE %FilePath%
@goto :EOF
::# ==========================================================================================================
:pACLFIX_pPROGRAMFILES
REM %0 Path=%1
::# Call %0 FilePath
@set icacls=call :pICACLS %1
::
::# ^<^< %PROGRAMFILES%
:: wmic path Win32_LogicalFileSecuritySetting where Path="c:\\Program Files" assoc /resultrole:owner
:: wmic path Win32_LogicalFileSecuritySetting where Path="c:\\Program Files" call GetSecurityDescriptor
::
::@echo OLD: %1 && icacls %1
::# Since takeown needs quite some time, breakdown for alive-messages.
@takeown /F %1 /A >nul || echo !!!ERR: takeown %1
@for /D %%d in (%~1\*) do @(
echo takeown: %%d
takeown /F %%d /A /R /D Y >nul || echo !!!ERR: takeown %%d
)
::# file /T traverses P of P\file -- for /T make sure we have a (nonempty) Directory
@set T=& for /D %%d in (%~1\*) do @set T=/T
::
@%icacls% /inheritance:e %T%
@%icacls% /inheritance:r
@%icacls% /remove ERSTELLER-BESITZER /grant:r ERSTELLER-BESITZER:(OI)(CI)(IO)(F)
@%icacls% /grant:r *%TrustedInstaller%:(CI)(IO)(F)
@%icacls% /grant *%TrustedInstaller%:(F)
@%icacls% /remove *S-1-5-18 /grant:r *S-1-5-18:(M)
@%icacls% /remove *S-1-5-32-544 /grant:r *S-1-5-32-544:(M)
@%icacls% /grant *S-1-5-32-544:(WO)
@%icacls% /grant *S-1-5-32-544:(OI)(CI)(IO)(F)
@%icacls% /remove *S-1-5-32-545 /grant:r *S-1-5-32-545:(RX)
@%icacls% /grant *S-1-5-32-545:(OI)(CI)(IO)(GR,GE)
@%icacls% /grant:r *S-1-15-2-1:(RX)
@%icacls% /grant *S-1-15-2-1:(OI)(CI)(IO)(GR,GE)
@%icacls% /grant:r *S-1-15-2-2:(RX)
@%icacls% /grant *S-1-15-2-2:(OI)(CI)(IO)(GR,GE)
@%icacls% /setowner *%TrustedInstaller% %T%
::@echo NEW: %1
::@icacls %1 || echo !!!ERR: icacls %1
@goto :EOF
:: ----------------------------------------------------------------------------------------------------------
:pACLFIX_pUSERPROFILE
REM %0 Path=%1
@set icacls=call :pICACLS %1
::
::# ^<^< %USERPROFILE%
:: wmic path Win32_LogicalFileSecuritySetting where Path="c:\\USERS\\björn" assoc /resultrole:owner
:: wmic path Win32_LogicalFileSecuritySetting where Path="c:\\USERS\\kulms" call GetSecurityDescriptor
::
::@echo OLD: && icacls %1 || echo ERR: icacls %1
@takeown /F %1 /A /R /D J /SKIPSL >nul || echo !!!ERR: takeown %1
@%icacls% /inheritance:e /T
@%icacls% /inheritance:r
@%icacls% /remove ERSTELLER-BESITZER /grant ERSTELLER-BESITZER:(OI)(CI)(F)
@%icacls% /remove *S-1-5-18 /grant *S-1-5-18:(OI)(CI)(F)
@%icacls% /remove *S-1-5-32-544 /grant *S-1-5-32-544:(OI)(CI)(F)
@%icacls% /remove *%UserSid% /grant *%UserSid%:(OI)(CI)(F)
@%icacls% /remove *S-1-5-32-545 /T
@%icacls% /remove *%TrustedInstaller% /T
@%icacls% /setowner *%UserSid% /T
::@echo NEW: &icacls %1
::@icacls %1 || echo !!!ERR: icacls %1
@goto :EOF
::
:: ===========================================================================================================
:pICACLS
@REM icacls %*
@icacls %* /C 2>&1 >nul || echo !!!ERR: icacls %*
@goto :EOF
:: ===========================================================================================================
:INSTALL
:: copy "%~f0%" "%InstallDir%\%NameMount%.cmd" &REM # Not works because of bogus reasons.
@set Source="%~f0%"
@set Tgt="%InstallDir%\%NameMount%.cmd"
REM Source: %Source%
REM Target: %Tgt%
@copy %Source% %Tgt%
::
@set Link="%InstallDir%\%NameUmount%.cmd"
REM Link: %Link%
@if exist %Link% del %Link%
@mklink /H %Link% %Tgt%
@set Link="%InstallDir%\%NameAclData%.cmd"
REM Link: %Link%
@if exist %Link% del %Link%
@mklink /H %Link% %Tgt%
@goto EXIT

Hints:

  • Find SIDs using
    • sc showsid %UserName%, such as
    • sc showsid TrustedInstaller or
    • sc showsid Users
  • mountvol shows the Volume{GUID}s
  • In the code some ^< were substituted by ^&lt; on submission to display the script completely. Take a look into the source code of this page if you suspect something still missing.
  • Have fun!