Blog. Just Blog

Ввод пароля в консоли

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Ввод пароля в консоли
Ввод пароля в консоли

Не знаю как вам, а мне очень не нравится метод ввода пароля в никсовой консоли. Введенные символы не показываются даже замаскированными, ввод не реагирует на вставку пароля из буфера обмена, а главное, нельзя удалять ошибочно введенный символ. Я понимаю, безопасность, все дела, но неудобно же! И решил я сделать свой вариант ввода пароля для консольных приложений. Чтобы и "звездочки" показывались, и вставка работала, и символы можно было удалять. Вот что у меня получилось.

Для меня, как и для вас, уже не будет неожиданностью, что сперва надо метнуться в MSDN и самостоятельно описать некоторые структуры и константы, потому что FASM их просто не знает. Структура CONSOLE_SCREEN_BUFFER_INFO уже была, а вот INPUT_RECORD надо описать. Хотя тут используется только клавиатура, я опишу и другие структуры для ввода, вдруг пригодится на будущее. Кстати, INPUT_RECORD в MSDN описана с ошибкой, там неправильно учитывается смещение union. Или поле EventType должно иметь размер DWORD, или надо добавить еще два байта для корректной адресации. Спасибо дизассемблеру IDA, он показывает корректную структуру.
  1. struct  COORD
  2.     X dw ?
  3.     Y dw ?
  4. ends
  5.  
  6. struct KEY_EVENT_RECORD
  7.     bKeyDown          dd ?
  8.     wRepeatCount      dw ?
  9.     wVirtualKeyCode   dw ?
  10.     wVirtualScanCode  dw ?
  11.     union
  12.         UnicodeChar   dw ?
  13.         AsciiChar     db ?
  14.     ends
  15.     dwControlKeyState dd ?
  16. ends
  17.  
  18. struct WINDOW_BUFFER_SIZE_RECORD
  19.     dwSize COORD
  20. ends
  21.  
  22. struct MOUSE_EVENT_RECORD
  23.     dwMousePosition   COORD
  24.     dwButtonState     dd ?
  25.     dwControlKeyState dd ?
  26.     dwEventFlags      dd ?
  27. ends
  28.  
  29. struct MENU_EVENT_RECORD
  30.      dwCommandId dd ?
  31. ends
  32.  
  33. struct INPUT_RECORD
  34.     EventType dw ?
  35.     Reserved  dw ?
  36.     union
  37.         KeyEvent   KEY_EVENT_RECORD
  38.         MouseEvent MOUSE_EVENT_RECORD
  39.         WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD
  40.         MenuEvent  MENU_EVENT_RECORD
  41.     ends
  42. ends
  43.  
  44. KEY_EVENT   = 0x0001
  45. MOUSE_EVENT = 0x0002
  46. WINDOW_BUFFER_SIZE_EVENT = 0x0004
  47. MENU_EVENT  = 0x0008
  48.  
  49. SHIFT_PRESSED      = 0x0010
  50. RIGHT_CTRL_PRESSED = 0x0004
  51. LEFT_CTRL_PRESSED  = 0x0008
Теперь сам код. Консоль реагирует на вставку комбинациями Ctrl+V и Shift+Ins, введенные символы можно стирать клавишами Backspace и Del, а сам введенный пароль показывается звездочками. Больше не знаю, какой функционал сюда еще можно добавить. Длина вводимого пароля проверяется, она не может превышать заданной длины.
  1.         ; Получить дескриптор stdout
  2.         invoke  GetStdHandle,STD_OUTPUT_HANDLE
  3.         ; Сохранить дескриптор stdout
  4.         mov     [stdout],eax
  5.         ; Получить дескриптор stdin
  6.         invoke  GetStdHandle,STD_INPUT_HANDLE
  7.         ; Сохранить дескриптор stdin
  8.         mov     [stdin],eax
  9.  
  10.         ; Сбросить буфер ввода консоли
  11.         invoke  FlushConsoleInputBuffer,[stdin]
  12.  
  13.         ; Строка для получения пароля
  14.         mov     edi,passw
  15.         ; Длина пароля
  16.         xor     ebx,ebx
  17. loc_loop:
  18.         ; Прочитать ввод с консоли
  19.         invoke  ReadConsoleInput,[stdin],ir,1,tmp
  20.         ; Это ввод с клавиатуры?
  21.         cmp     [ir.EventType],KEY_EVENT
  22.         jne     loc_loop
  23.  
  24.         ; Нажатие клавиши?
  25.         cmp     [ir.KeyEvent.bKeyDown],0
  26.         je      loc_loop
  27.  
  28.         ; BackSpace?
  29.         cmp     [ir.KeyEvent.wVirtualKeyCode],8
  30.         je      loc_del
  31.         ; Del?
  32.         cmp     [ir.KeyEvent.wVirtualKeyCode],46
  33.         je      loc_del
  34.         ; Enter?
  35.         cmp     [ir.KeyEvent.wVirtualKeyCode],13
  36.         je      loc_done
  37.         ; Shift+Ins?
  38.         cmp     [ir.KeyEvent.wVirtualKeyCode],45
  39.         jne     @f
  40.         test    [ir.KeyEvent.dwControlKeyState],SHIFT_PRESSED
  41.         jnz     loc_paste
  42. @@:
  43.         ; Ctrl+V?
  44.         cmp     [ir.KeyEvent.wVirtualKeyCode],'V'
  45.         jne     @f
  46.         test    [ir.KeyEvent.dwControlKeyState],\
  47.                 LEFT_CTRL_PRESSED+RIGHT_CTRL_PRESSED
  48.         jnz     loc_paste
  49. @@:
  50.         ; Введенный символ
  51.         mov     al,[ir.KeyEvent.AsciiChar]
  52.         ; Буквенно-цифровая кнопка?
  53.         cmp     al,20h
  54.         jb      loc_loop
  55.  
  56.         ; Достигнута максимальная длина пароля?
  57.         cmp     ebx,MAX_PASSWORD_LENGTH
  58.         jae     loc_loop
  59.  
  60.         ; Записать символ в строку пароля
  61.         stosb
  62.         ; Увеличить длину пароля
  63.         inc     ebx
  64.  
  65.         ; Вывести на экран символ-заменитель
  66.         invoke  WriteConsole,[stdout],szPassw,1,tmp,NULL
  67.         jmp     loc_loop
  68.  
  69.         ; Удалить последний введенный символ
  70. loc_del:
  71.         ; Пароль пустой?
  72.         or      ebx,ebx
  73.         jz      loc_loop
  74.  
  75.         ; Затереть последний символ в строке
  76.         dec     edi
  77.         mov     byte [edi],0
  78.         ; Уменьшить длину пароля
  79.         dec     ebx
  80.  
  81.         ; Получить координаты курсора
  82.         invoke  GetConsoleScreenBufferInfo,[stdout],sbi
  83.         ; Передвинуть на символ влево
  84.         dec     [sbi.dwCursorPosition.X]
  85.         invoke  SetConsoleCursorPosition,[stdout],[sbi.dwCursorPosition]
  86.         ; Записать символ пробела
  87.         invoke  WriteConsole,[stdout],szSpace,1,tmp,NULL
  88.         invoke  SetConsoleCursorPosition,[stdout],[sbi.dwCursorPosition]
  89.  
  90.         jmp     loc_loop
  91.  
  92.         ; Вставка строки из буфера обмена
  93. loc_paste:
  94.         ; Получить формат буфера обмена
  95.         invoke  IsClipboardFormatAvailable,CF_TEXT
  96.         or      eax,eax
  97.         jz      loc_loop
  98.  
  99.         ; Открыть буфер обмена
  100.         invoke  OpenClipboard,NULL
  101.         invoke  GetClipboardData,CF_TEXT
  102.         mov     edx,eax
  103.         invoke  GlobalLock,eax
  104.  
  105.         ; Поочередно передать все символы из буфера обмена
  106.         mov     esi,eax
  107. @@:
  108.         ; Достигнута максимальная длина пароля?
  109.         cmp     ebx,MAX_PASSWORD_LENGTH
  110.         jae     @f
  111.  
  112.         lodsb
  113.         or      al,al
  114.         jz      @f
  115.  
  116.         ; Буквенно-цифровой символ?
  117.         cmp     al,20h
  118.         jb      @b
  119.  
  120.         ; Записать символ в строку пароля
  121.         stosb
  122.         inc     ebx
  123.  
  124.         ; Вывести на экран символ-заменитель
  125.         invoke  WriteConsole,[stdout],szPassw,1,tmp,NULL
  126.         jmp     @b
  127. @@:
  128.         ; Закрыть буфер обмена
  129.         invoke  GlobalUnlock,edx
  130.         invoke  CloseClipboard
  131.  
  132.         jmp     loc_loop
  133.  
  134.         ; Окончание ввода пароля
  135. loc_done:
  136.         ; Пароль пустой?
  137.         or      ebx,ebx
  138.         jz      loc_loop
  139.  
  140.         ; passw -> введенный пароль
Вместо обычной для таких случаев ReadConsole тут используется функция ReadConsoleInput, что дает возможность получать состояние служебных клавиш и клавиш-переключателей. В остальном ничего нестандартного не происходит.

Теперь при вводе пароля консоль по удобству использования вплотную приблизилась к оконным приложениям. Также код можно легко доработать для работы с юникодными символами. С минимальными изменениями этот код можно применять вообще для любого ввода данных с консоли, в том числе и специализированного, например, чтобы вводить только цифры, только hex-значения или типа того.

В приложении пример программы с исходным текстом, которая запрашивает у пользователя пароль, а затем отображает введенную строку.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Console.Password.Demo.zip (3,353 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 1828 | Комментариев: 4

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Владимир (09.01.2021 в 00:27):
Добрый вечер, не получается скачать архив "Console.Password.Demo.zip". В Хроме пишет "Ошибка: Файлы отсутствуют".

Спасибо! Всё ок.
hatmaster (14.07.2020 в 14:43):
Я юзаю ConEmu, из которого либо через OpenSSH идет коннект напрямую в консоль Линукса, либо через вызов Putty.
Как прикрутить данную тулзу?
ManHunter (14.07.2020 в 11:20):
Да, тут в уме держалась центосовская консолька. Постоянно испытываю боль во время ввода пароля при логине на сервер. А внутри все нормально, к консоли никаких претензий.
Александр (14.07.2020 в 08:40):
Возможно здесь речь идёт про какую-то конкретную консоль, но большинство виденных мной консолей и терминалов поддерживают выше описанные действия за исключением отображения символов и сочетания Ctrl+V, в *nix системах это сочетание исторически выполняет действие вставки управляющих символов и вместо него для вставки из буфера обычно работает Shift+Ctrl+V.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0062 сек.) / Память: 4.5 Mb
Наверх