!BAT

:: Call the subroutine specified as first parameter of the script
if "%1"=="findDLL" goto :findDLL
if "%1"=="findWinVer" goto :findWinVer
if "%1"=="isWinDir" goto :isWinDir
if "%1"=="patchUnpatch" goto :patchUnpatch
if "%1"=="patchDLL" goto :patchDLL
if "%1"=="unPatchDLL" goto :unPatchDLL
if "%1"=="help" goto :help

:: HISTORY
:: SSi 2013-06-13  - added Win8 patches
:: SSi 2013-06-15  - backup added
:: SSi 2013-06-16  - errorcheck on for forcedetect
:: SSi 2013-06-19  - few tweaks!
:: SSi 2014-09-01  - Win 8.1 x64 added (x86 not working - patch causes RPC error on user login)
:: SSi 2014-09-02  - Win 8.1 64 & 32-bit added 
:: SSi 2014-10-27  - reduce number of variables and tidy up on exit as can exceed variables in E2B

#if "%1"=="" pause About to run setlocal
#if "%1"=="" setlocal && set *

delmod -l wenv > nul || insmod /wenv > nul || pause ERROR: Cannot find file wenv! && configfile /menu.lst
delmod -l %~nx0 > nul  || insmod %0 > nul  || pause WARNING: %~nx0 name too long for insmod && pause && configfile /menu.lst


# not used because eats into variables allowance (max 60 in grub4dos)
#set dll = /system32/msv1_0.dll
#set osDrv = (md)0x19000+2
#set patchDrv = (md)0x19300+2
#set devDrv = (md)0x19500+2
#set dirDrv = (md)0x19800+80
#set tmpmem = (md)0x19f00+1

#clear memory in case it contains bytes that look like a compressed file header! - 50MB area
echo ffffffffffffff > (md)0x19000+2
echo ffffffffffffff > (md)0x19300+2
echo ffffffffffffff > (md)0x19500+2
echo ffffffffffffff > (md)0x19800+80
echo ffffffffffffff > (md)0x19f00+1

echo -e \x0 > (md)0x19000+2		## Initialize mem drive

:: Find Windows installations

:: Attempt to auto-detect Windows installation
if not "%1"=="" goto :forceDetect
:: If debug is off, nothing is written to output
:: If debug is on, output is too verbose
:: So, set debug to normal and turn it off immediately after
debug normal
find --devices=h > (md)0x19500+2
#debug off
set /a skip = 0 > nul
:autoLoop
cat --locate=\x0a --number=1 --skip=%skip% (md)0x19500+2 > nul || goto :displayMenu
set /a length = %?% - %skip% > nul
cat --skip=%skip% --length=%length% (md)0x19500+2 | set dev =
set /a skip = %length% + %skip% + 1  > nul
if not "%?_BOOT:~0,4%"=="%dev:~0,4%" echo Checking %dev%... && call %~nx0 findDLL %dev%
goto :autoLoop


:: Force detection of Windows installation
:forceDetect
errorcheck off
wenv for /L %p in (0,1,%1) do for /L %q in (0,1,%2) do exec %0 findDLL %p %q		## Iterate over HD partitions
errorcheck on

:displayMenu
if "%os%"==""  echo No Windows installation found!	&& pause && goto :TIDY
set length=

## Display Windows installations found
set cc=set skip= \&\& set dllRoot= \&\& set dllPath= \&\& set dev= \&\& set file= \&\& set string= \&\& set offbase= \&\& set counter= \&\& set offset= \&\& set majmin= \&\& set patt= \&\& set os= \&\& set version= \&\& set rpatt= \&\& set n1= \&\& set ask= \&\& set r=
echo -e \ntitle Back to Main Menu \\n Return to Main Menu\n%cc%\nset MFOLDER=/_ISO/MAINMENU\nconfigfile (md)0xA000+0x50 \|\| configfile /menu.lst >> (md)0x19000+2
set cc=
echo -e \x0 >> (md)0x19000+2	## EOF marker for configfile
configfile (md)0x19000+2

:TIDY
set file= && set string= && set offbase= && set counter= && set offset= && set majmin= && set patt= && set os= && set version= && set rpatt= && set n1= && set ask= && set r=
goto :EOF


:: =======================  SUBROUTINES  =======================


:: Searches for msv1_0.dll, %1 = findDLL, %2 = Disk#, %3 = Partition#
:findDLL
set dllRoot = %2
if not "%dllRoot:~0,1%"=="(" set dllRoot = (hd%2,%3)
errorcheck off
wenv dir %dllRoot%/ > (md)0x19800+80
errorcheck on
wenv for /f %i in ( (md)0x19800+80 ) do exec %0 isWinDir %i
goto :EOF


:: Checks if the directory contains Windows installation, %1 = isWinDir, %2 = Directory
:isWinDir
set dllPath = %dllRoot%/%2/system32/msv1_0.dll
cat --length=0 %dllPath%  > nul || goto :EOF	## Don't proceed further for non-existent partitions
:: Start forming menu items
set grubMenu = \ntitle Windows
:: Check for Windows version based on size of msv1_0.dll
call %0 findWinVer %dllPath%
set grubMenu = %grubMenu% %os% at %dllRoot%\\\\%2 \\n Patch or UnPatch Windows on %dllRoot%\ncall %0 patchUnpatch %dllRoot% %2 \npause \nconfigfile (md)0x19300+2
echo -e %grubMenu% >> (md)0x19000+2			## Write configfile to mem drive
set grubMenu=
goto :EOF



:: Find version of Windows installed
:: This batch should print the version of
:: .dll's tested on 32-bit ONLY
:: You must provide a fully qualified path as parameter.
:: Example: (hd0,0)/Windows/System32/msv1_0.dll
:: or a valid relative path from current ROOT:
:: /Windows/System32/msv1_0.dll
:findWinVer
# String is "FileV" in Unicode
set file=%~dpnx2
set string=\x01\x00\x46\x00\x69\x00\x6C\x00\x65\x00\x56\x00
set version=
set char=

cat --hex --locate=%string% %file% > nul
set /a offbase = %?% + 27 > nul

set /a counter=1 > nul
:loop
set /a figure=0 > nul
set /a offset=%offbase%+%counter% > nul
cat --skip=%offset% --length=1 %file% | set char=

:floop
if "%char%"=="%figure%" set version=%version%%%char%
set /a figure=%figure%+1 > nul
if "%figure%"=="10" goto :fnext
goto :floop

:fnext
if "%char%"=="." set version=%version%%%char%
set /a counter=%counter%+1 > nul
if "%counter%"=="30" goto :next
goto :loop

:next
set majmin=%version:~0,3%
set os=(Unknown version = %majmin%)
if "%majmin%"=="5.0" set os=2000
if "%majmin%"=="5.1" set os=XP
if "%majmin%"=="5.2" set os=XP 64-bit or Server 2003
if "%majmin%"=="6.0" set os=Vista or Server 2008
if "%majmin%"=="6.1" set os=7 or Server 2008 R2
if "%majmin%"=="6.2" set os=8 or Server 2012
if "%majmin%"=="6.3" set os=8.1 or Server 2012 R2

set figure=
set counter=
set char=
set offset=
set offbase
goto :EOF

:: Patch or Unpatch?
:patchUnpatch
echo -e iftitle [if exist (bd)/PassPass.bak] Backup  %2/%3/system32/msv1_0.dll \\nBackup to \\\\PassPass.bak \ndd if=%2/%3/system32/msv1_0.dll of=(bd)/PassPass.bak \nif not "%^?%"=="0x0" pause \nconfigfile (md)0x19300+2 > (md)0x19300+2
echo -e iftitle [if exist (bd)/PassPass.bak] Restore %2/%3/system32/msv1_0.dll \\nRestore from \\\\PassPass.bak \ndd of=%2/%3/system32/msv1_0.dll if=(bd)/PassPass.bak \nif not "%^?%"=="0x0" pause \nconfigfile (md)0x19300+2 >> (md)0x19300+2
echo -e title Patch Windows on %2\\\\%3\\n \ncall %0 patchDLL %2 %3 \npause \nconfigfile (md)0x19300+2 >> (md)0x19300+2
echo -e title UnPatch Windows on %2\\\\%3\\n \ncall %0 unPatchDLL %2 %3 \npause \nconfigfile (md)0x19300+2 >> (md)0x19300+2
echo -e title Back to OS detection menu\\n \nconfigfile (md)0x19000+2 \npause \nconfigfile (md)0x19000+2 >> (md)0x19300+2
echo -e title Boot from Internal Hdd\\n \nif "%^?_BOOT:~0,3%"=="(hd" map (hd0) (hd1)\nif "%^?_BOOT:~0,3%"=="(hd" map (hd1) (hd0) \nif "%^?_BOOT:~0,3%"=="(hd" map --hook \nchainloader (hd0)+1 >> (md)0x19300+2
echo -e \x0 >> (md)0x19300+2			## EOF marker for configfile
configfile (md)0x19300+2
goto :EOF


:: ----------- PATCH ------------

:: Patches DLL file, %1 = patchDLL, %2 = (hdX,Y), %3 = WinDir

:patchDLL
set ask=
set dllPath = %2/%3/system32/msv1_0.dll
call %0 findWinVer %dllPath%
cat --locate=\x33\xC0\x90 %dllPath% > nul
if not "%@retval%"=="0" goto :warnUser

:: Check for 0x6486 to identify 64-bit PE
cat --locate=\x64\x86 %dllPath% > nul
if "%@retval%"=="1" goto :64BitPatch

:32BitPatch
set patt=\x83\xF8\x10
set rpatt=\x33\xC0\x90
if "%majmin%"=="6.2" set patt=\x3b\xc6\x75\x13 && set rpatt=\x33\xc0\x75\x13
if "%majmin%"=="6.3" set patt=\x4d\x3b\xc6\x0F\x85 && set rpatt=\x4d\x33\xc0\x0F\x85
echo 32-Bit Win%majmin% - Patch bytes %patt% with %rpatt%

:DoPatch
:: check we can unpatch
cat --locate=%rpatt% %dllPath% > nul
if not "%@retval%"=="0" goto :warnUser

cat --locate=%patt% %dllPath% > (md)0x19f00+1
set n1=%@retval%
if %n1%==0 goto :warnUser
set ask=
if %n1%>=2 set /p /u ask=WARNING: %n1% instances found in %file%! Press S to skip : 
if not "%ask%"=="S" cat --hex --locate=%patt% --replace=%rpatt% %dllPath% > nul 
set r=%@retval%
if "%ask%"=="S" echo -e \n\n User aborted - press a key... && goto :EOF
if "%n1%"=="0" goto :warnUser
goto :patchMessage

:64BitPatch
set patt=\x48\x3B\xC6\x0F\x85
set rpatt=\x33\xC0\x90\x0F\x85

if "%majmin%"=="6.2" set patt=\x49\x3B\xC6\x0F\x85 && set rpatt=\x33\xC0\x90\x0F\x85
if "%majmin%"=="6.3" set patt=\x49\x3B\xC6\x0F\x85 && set rpatt=\x33\xC0\x90\x0F\x85
echo 64-Bit Win%majmin% - Patch bytes %patt% with %rpatt%
goto :DoPatch

:patchMessage
echo
echo DLL at %2\%3 patched (in %n1% places)
goto :EOF

:warnUser
echo
pause The DLL has been already patched (or is not compatible)
configfile (md)0x19300+2
goto :EOF

:: ----------- UNPATCH ------------

:: Unpatches DLL file, %1 = patchDLL, %2 = (hdX,Y), %3 = WinDir
:unPatchDLL
set dllPath = %2/%3/system32/msv1_0.dll
call %0 findWinVer %dllPath%
:: Check for 0x6486 to identify 64-bit PE
cat --locate=\x64\x86 %dllPath% > nul
if not "%@retval%"=="0" goto :64BitUnpatch

:32BitUpatch
set patt=\x83\xF8\x10
set rpatt=\x33\xC0\x90
if "%majmin%"=="6.2" set patt=\x3b\xc6\x75\x13 && set rpatt=\x33\xc0\x75\x13
if "%majmin%"=="6.3" set patt=\x4d\x3b\xc6\x0F\x85 && set rpatt=\x4d\x33\xc0\x0F\x85
echo 32Bit Win%majmin% - Unpatch bytes %rpatt% with %patt%

:DoUPatch
cat --locate=%rpatt% %dllPath% > (md)0x19f00+1
set n1=%@retval%
set ask=
if %n1%>=2 set /p /u ask=WARNING: %n1% instance found in %file%! Press S to skip : 
if /i not "%ask%"=="S" cat --hex --locate=%rpatt% --replace=%patt% %dllPath% > nul 
set r=%@retval%
if "%ask%"=="S" echo -e \n\n User aborted - press a key... && goto :EOF
if "%r%"=="0" goto :warnUserU
goto :unpatchMessage

:64BitUnpatch
set patt=\x48\x3B\xC6\x0F\x85
set rpatt=\x33\xC0\x90\x0F\x85
if "%majmin%"=="6.2" set patt=\x49\x3B\xC6\x0F\x85 && set rpatt=\x33\xC0\x90\x0F\x85
if "%majmin%"=="6.3" set patt=\x49\x3B\xC6\x0F\x85 && set rpatt=\x33\xC0\x90\x0F\x85

echo 64Bit Win%majmin% - Unpatch bytes %rpatt% with %patt%
goto :DoUPatch

:warnUserU
echo
pause The DLL has already been Unpatched (or is not compatible)
configfile (md)0x19300+2
goto :EOF

:unpatchMessage
echo
echo DLL at %2\%3 is now Unpatched (in %n1% places)
goto :EOF



:help
echo -e \nPassPass v1.0 - Idea by jaclaz, Coded by Sherlock
echo Released under the jaclaz's CAREWARE license
echo -e \nUsage: %0 <MaxDisk#> <MaxPartition#>\n
echo By default, PassPass tries to autodetect Windows installations
echo If autodetection fails, provide MaxDisk# and MaxPartition# 
echo to forcedetect and guide the script manually 
echo %0 script and WENV binary need to be
echo present on the root of the boot media.