1

I have made a batch game where users can log in / register. But there is no point in having passwords if a person standing nearby can peep at the password. Normal password fields mask the characters with asterisks (*).

How can mask characters on a batch file?

I've seen this done on cmd before but I have no clue how.

Carl479
  • 389
  • 2
  • 5
  • 16
  • if you're using the default command line, I'd say no way... unless you quickly save the entered character and replace it with an asterisk to appear like that's what's being entered! – user1306322 Mar 09 '14 at 08:51
  • @user1306322 How would you do that? – Carl479 Mar 09 '14 at 08:54
  • 1
    Possible duplicate of http://stackoverflow.com/questions/664957/can-i-mask-an-input-text-in-a-bat-file – Kei Minagawa Mar 09 '14 at 09:07
  • 1
    You may get a hint on [Hiding input into a command window with a character or blank space](http://www.computerhope.com/forum/index.php?topic=99880.0) – Kei Minagawa Mar 09 '14 at 09:14
  • possible duplicate of [batch - color input by user](http://stackoverflow.com/questions/15140980/batch-color-input-by-user) – jeb Mar 09 '14 at 12:10

4 Answers4

13

You can use XCOPY for a hidden input, it can handle nearly all characters and you can also implement a backspace logic.

@echo off
setlocal EnableDelayedExpansion
call :input
echo(
echo '!input!'
if "!input!"=="password" echo OK
exit /b

:input
for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do (
  set "\b=%%a"
)

set "input="
:keyLoop
call :GetKey
if not defined key exit /b
if "!key!"=="!\b!" (
    if defined input (
        set "input=!input:~0,-1!"
        <nul set /p ".=!\b! !\b!"
    )
) ELSE (
    <nul set /p ".=*"
    set "input=!input!!key!"
)
goto :keyLoop


:GetKey
setlocal DisableDelayedExpansion
set "key="
for /F "usebackq delims=" %%L in (`xcopy /L /w "%~f0" "%~f0" 2^>NUL`) do (
  if not defined key set "key=%%L"
)
(
endlocal
set "key=^%key:~-1%" !
exit /b
)

This code should be able to handle all characters, like ^!&%<>.
It's also possible to use backspace to delete the last entered character.

The line set "key=^%key:~-1%" ! seems odd, but it's used to escape the ! character with set "key=^!" ! in the delayed expansion context.
And to avoid problems for all other characters the last ! removes the caret, like in set "key=^A" ! will be evaluated to ``set "key=A"`

jeb
  • 78,592
  • 17
  • 171
  • 225
  • You're brilliant! This should be accepted as the answer. The "input" label actually handles all characters even '&' '<' and '>' the problems come up at the 'if' as usual in a batch script. – Ir Relevant Jul 21 '14 at 04:20
  • @jeb, Awesome! I am always amazed at your insight. Just wondering why you used setlocal EnableDelayedExpansion. It doesn't seem necessary and it by using it exclamation points are not allowed in the password. – RGuggisberg Apr 08 '16 at 20:13
  • @RGuggisberg Thanks, I simply spent much to much time with batch. And yes, it can be improved, but normally I only build a proof of concept. But now I will change it – jeb Apr 10 '16 at 16:37
2

Ok, this is a bit different to what you may have had in mind, but that's you're fault for choosing batch for game dev.

The way I see it is you have 3 options:

  • Use an external program you self made in C#, C++, Python, [etc.]
    • Howver this requires an application to already do this for you (Which there probably is) or for you to have a knowledge in one of these languages
  • Use the choice command, to continuously take one key input and wait for the user to hit space to signify the end of the password
    • However this limits the password characters choice, and makes the program look ugly
  • Use 2 Batch threads, one that masks and tallies input while the other stores it to a variable.
    • This may be a bit dodgey at times, at would be a bit complicated but may be the only choice you have.

Now, as I was typing this an idea stuck my head on how to achieve this. Since it might take some time to test I thought I'd post the idea (as it seems to be a soloution to this problem, which has been around for a while).

Logic

  • One Batch Thread will simply use set /p to store all the input into a variable and upon completion will communicate to the other batch thread through the use of waitfor or a simple directory file.
  • Another Batch Thread would loop the pause >nul statement and would tally the number of times the pause statement is looped, printing out the appropriate amount of *'s. The other important job of this thread is to sense when the user has finished typing the password, upon which it exits.

Im starting to make this batch program now, but for now I'll just keep you informed of my idea so far.

Code

Login.bat

@echo off
Title Password Please: 

:: This is the main code
REM This code relies on Masker.bat

REM SET password to be first and thrid letter,
REM of the day of the week.
set pass=%Date:~0,1%%Date:~2,1%

REM START Masker in the same window using /b tag and create hold.pass: 
Echo 0 >%temp%\hold.pass
start /b Masker.bat "%pass%" *

REM Simply take input an leave the rest to Masker.bat
set /p pass_input=:
Echo 1 >>%temp%\hold.pass
cls

if /i "%pass%" NEQ "%pass_input%" (
Title Worng Password
Echo Wrong Password... Sorry.
Sleep 5
Exit
)

Pause > nul

REM Rest of Main game code is below or simply
:: START Main.bat & Exit

Masker.bat

@echo off
Title Password Please: 
setlocal enabledelayedexpansion

:: This is not the main code
REM This code is called upon by Login.bat (or the Main.bat game code)

REM CREATE the variables "passlen" and "mask": 
set password=%~1
set passlen=0
:miniloop
set /a passlen+=1
if "!password:~%passlen%,1!" NEQ "" goto :miniloop
set password=

set mask=%~2
if "%mask%" EQU "" set mask=*



REM MAIN loop
:loop
cls
for /l %%a in (1,1,%passlen%) do (<nul set /p=%mask%)
sleep -m 150
for /f "usebackq" %%a in ("%temp%\hold.pass") do (if "%%~a" EQU "1" Del %temp%\hold.pass & Exit)
goto :loop

It still needs some more improvements, but I've spent aroung 30 min on it with little success to make it dynamically tell you how many characters you have typed in.

Anyone cane take this up, be my guest. Everything else works fine

Mona

Monacraft
  • 6,510
  • 2
  • 17
  • 29
  • Very good but... `sleep -m 150` does not work so I replaced it with `ping 192.0.2.2 -n 1 -w 150 > nul` – Carl479 Mar 10 '14 at 02:16
0

This works without pressing enter after input of the password. If you enter the correct password, ok. if you enter a wrong password, it will stop when you enter the 9th character (can be adapted). It does not care about capitalization. Problem: the password is stored as pure text in the code

@echo off
setlocal enabledelayedexpansion
set "s= abcdefghijklmnopqrstuvwxyz"
set p=

:loop
choice /C %s%  /N >nul
set p=%p%!s:~%errorlevel%,1!&set /p =*<nul
if /i "%p%"=="secured" goto :right
if not "%p:~8,1%"=="" goto :wrong
goto :loop
goto :wrong

:right
echo you entered correct password: %p%
goto :eof

:wrong
echo you entered wrong password: %p%
goto :eof
Stephan
  • 53,940
  • 10
  • 58
  • 91
  • However, this doesn't allow the user to use numbers (Which can be included) or punctuation (which can't) furthermore, typing in one character accidentally will force end the password input. – Monacraft Mar 09 '14 at 11:37
  • correct @Monacraft, numbers and Capitals can be easily included; no way for punctuation. But on the other hand, what's the sense in using strong passwords with BATCH? – Stephan Mar 09 '14 at 12:21
0

You may use ReadFormattedLine subroutine for all kind of formatted input. For example, the command below read a password of 8 characters, display asterisks in the screen, and continue automatically with no need to press Enter:

call :ReadFormattedLine password="********" /M "Enter password (8 chars): "

This subroutine is written in pure Batch so it does not require any additional program, and it allows several formatted input operations, like read just numbers, convert letters to uppercase, etc. You may download ReadFormattedLine subroutine from Read a line with specific format.

Aacini
  • 65,180
  • 12
  • 72
  • 108