diff --git a/mercurial/posix.py b/mercurial/posix.py
index 3af293735d0fc5b3e83d634bdc1a4d5066e63d5e_bWVyY3VyaWFsL3Bvc2l4LnB5..5b3513177f2b449ff49f0879d5cf9d81e69a43a6_bWVyY3VyaWFsL3Bvc2l4LnB5 100644
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -381,6 +381,10 @@
     return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
 
 
+def get_password():
+    return encoding.strtolocal(getpass.getpass(''))
+
+
 def setbinary(fd):
     pass
 
diff --git a/mercurial/ui.py b/mercurial/ui.py
index 3af293735d0fc5b3e83d634bdc1a4d5066e63d5e_bWVyY3VyaWFsL3VpLnB5..5b3513177f2b449ff49f0879d5cf9d81e69a43a6_bWVyY3VyaWFsL3VpLnB5 100644
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -11,7 +11,6 @@
 import contextlib
 import datetime
 import errno
-import getpass
 import inspect
 import os
 import re
@@ -1779,7 +1778,7 @@
                         raise EOFError
                     return l.rstrip(b'\n')
                 else:
-                    return encoding.strtolocal(getpass.getpass(''))
+                    return util.get_password()
         except EOFError:
             raise error.ResponseExpected()
 
diff --git a/mercurial/util.py b/mercurial/util.py
index 3af293735d0fc5b3e83d634bdc1a4d5066e63d5e_bWVyY3VyaWFsL3V0aWwucHk=..5b3513177f2b449ff49f0879d5cf9d81e69a43a6_bWVyY3VyaWFsL3V0aWwucHk= 100644
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -106,6 +106,7 @@
 expandglobs = platform.expandglobs
 getfsmountpoint = platform.getfsmountpoint
 getfstype = platform.getfstype
+get_password = platform.get_password
 groupmembers = platform.groupmembers
 groupname = platform.groupname
 isexec = platform.isexec
diff --git a/mercurial/windows.py b/mercurial/windows.py
index 3af293735d0fc5b3e83d634bdc1a4d5066e63d5e_bWVyY3VyaWFsL3dpbmRvd3MucHk=..5b3513177f2b449ff49f0879d5cf9d81e69a43a6_bWVyY3VyaWFsL3dpbmRvd3MucHk= 100644
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -194,6 +194,28 @@
         return False
 
 
+def get_password():
+    """Prompt for password with echo off, using Windows getch().
+
+    This shouldn't be called directly- use ``ui.getpass()`` instead, which
+    checks if the session is interactive first.
+    """
+    pw = ""
+    while True:
+        c = msvcrt.getwch()
+        if c == '\r' or c == '\n':
+            break
+        if c == '\003':
+            raise KeyboardInterrupt
+        if c == '\b':
+            pw = pw[:-1]
+        else:
+            pw = pw + c
+    msvcrt.putwch('\r')
+    msvcrt.putwch('\n')
+    return encoding.strtolocal(pw)
+
+
 class winstdout(object):
     """Some files on Windows misbehave.