Защищенное поле для ввода пароля
Как-то задумался о том, можно ли защититься от программ, которые показывают пароли за "звездочками"? Ведь это могут быть не только безобидные программы для восстановления забытых паролей, но и "троянские кони", похищающие вашу приватную информацию. Немного поэкспериментировал, оказалось, что защититься можно. Сперва немного теоретической информации о том, каким образом открываются пароли. Первый способ: сначала нужному полю EDIT посылается сообщение EM_SETPASSWORDCHAR с нулевыми параметрами, в результате чего с него снимается атрибут ES_PASSWORD. После этого текст пароля можно прочитать как визуально, так и через GetWindowText, WM_GETTEXT и т.п. Второй способ, более "пробивной", это внедрение в исследуемый процесс своей DLL, после чего с ее помощью текст пароля читается через сообщение WM_GETTEXT. Это делается потому, что в целях безопасности информацию из поля, закрытого "звездочками", через GetWindowText или WM_GETTEXT можно получить только из контекста процесса, который владеет окном.Чтобы защититься от программ первого типа, надо самостоятельно обрабатывать сообщение EM_SETPASSWORDCHAR и в обработчике подавлять его. Защититься от второго варианта сложнее, ведь если мы будем подавлять сообщение WM_GETTEXT, то мы и сами не сможем прочитать текст пароля. Значит надо каким-то образом различать "свои" сообщения WM_GETTEXT и "чужие". Признак "свой" можно сделать, например, указав в качестве длины буфера какое-нибудь заранее определенное уникальное значение, а затем в обработчике пропускать сообщения только с этим параметром. Установить собственный обработчик можно через субклассирование окна ввода, это мы уже разбирали в предыдущих статьях. Теперь от теории перейдем к практике.
Субклассируем поле ввода на этапе инициализации окна. Тут ничего принципиально нового нет, все делается стандартными методами:
Code (Assembler) : Убрать нумерацию
- ...
- ; Субклассирование на этапе инициализации окна
- invoke GetDlgItem,[hwnddlg],ID_PASS
- ; Установить наш собственный обработчик
- invoke SetWindowLong,eax,GWL_WNDPROC,EditWindowProc
- ; Сохранить адрес предыдущего обработчика
- mov [OldProc],eax
- ...
Code (Assembler) : Убрать нумерацию
- proc EditWindowProc hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
- ; Установить или сбросить символ пароля?
- cmp [uMsg],EM_SETPASSWORDCHAR
- ; Подавить сообщение
- je .exit_proc
- ; Получить текст из окна?
- cmp [uMsg],WM_GETTEXT
- je .get_text
- .msg_ok:
- ; Передать управление предыдущему обработчику или пропустить
- ; разрешенное сообщение
- invoke CallWindowProc,[OldProc],[hEdit],[uMsg],[wParam],[lParam]
- ret
- .get_text:
- ; Проверить длину буфера, пропускать только "своих"
- cmp [wParam],103h
- je .msg_ok
- .exit_proc:
- ; Подавить сообщение и вернуть из обработчика FALSE
- xor eax,eax
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ; Прочитать текст из защищенного окна
- ; В качестве идентификатора "свой" используется размер буфера 103h
- invoke GetDlgItem,[hwnddlg],ID_PASS
- invoke SendMessage,eax,WM_GETTEXT,103h,buff
- ...
Скриншот программы Asterisk Key
Похоже, что по второму способу работает еще одна программа Password Viewer, но и она показывает пустое значение. Итак, ни одна из протестированных программ не в состоянии прочитать введенный пароль из окна нашего приложения, в то время как мы имеем к нему полный доступ. Троянов и прочее говно не тестировал за неимением таковых под рукой. Из множества виденных мной программ я встречал пару-тройку, которые пытаются защищать поле ввода пароля, но и то лишь от сообщения EM_SETPASSWORDCHAR, так что Asterisk Key легко пробивает их защиты и показывает пароли.
В приложении готовый пример программы с исходным текстом, создающей окно с защищенным полем ввода пароля, на котором и проводились все описанные испытания.
Просмотров: 6089 | Комментариев: 10
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(19.09.2020 в 00:27):
Нет, мне просто пофиг. Не испытываю ни малейшего интереса к этой проблеме, и уж тем более не считаю нужным тратить свое время на ее решение.
FFFF
(18.09.2020 в 22:52):
Вы целенаправленно не отвечаете, думаете или забыли?
FFFF
(13.09.2020 в 23:18):
Или я плохо ищу, или одно из двух. Снятие пароля просто посылкой сообщения:
(Извините за многабукаф)
PasswordDialog.cpp:
bool CPasswordDialog::OnButtonClicked(int buttonID, HWND buttonHWND)
{
if (buttonID == IDX_PASSWORD_SHOW)
{
ReadControls();
SetTextSpec();
return true;
}
return CDialog::OnButtonClicked(buttonID, buttonHWND);
}
void CPasswordDialog::SetTextSpec()
{
_passwordEdit.SetPasswordChar(ShowPassword ? 0: TEXT('*'));
_passwordEdit.SetText(Password);
}
Edit.h:
void SetPasswordChar(WPARAM c) { SendMsg(EM_SETPASSWORDCHAR, c); }
Window.h:
LRESULT SendMsg(UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
{ return ::SendMessage(_window, message, wParam, lParam); }
И ни намёка на защиту. Но не пробиться же!
(Извините за многабукаф)
PasswordDialog.cpp:
bool CPasswordDialog::OnButtonClicked(int buttonID, HWND buttonHWND)
{
if (buttonID == IDX_PASSWORD_SHOW)
{
ReadControls();
SetTextSpec();
return true;
}
return CDialog::OnButtonClicked(buttonID, buttonHWND);
}
void CPasswordDialog::SetTextSpec()
{
_passwordEdit.SetPasswordChar(ShowPassword ? 0: TEXT('*'));
_passwordEdit.SetText(Password);
}
Edit.h:
void SetPasswordChar(WPARAM c) { SendMsg(EM_SETPASSWORDCHAR, c); }
Window.h:
LRESULT SendMsg(UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
{ return ::SendMessage(_window, message, wParam, lParam); }
И ни намёка на защиту. Но не пробиться же!
ManHunter
(13.09.2020 в 22:12):
7zip же в исходниках. Почему бы не посмотреть там?
FFFF
(12.09.2020 в 08:01):
По данным на сейчас, 7zip походу научился выдерживать оба способа. Причём там есть ещё и способ снять звёздочки, хотя в вашем примере сообщение перехвачено и подавлено совсем. Выяснять не пробовал, но, как вы думаете, как это там? Тоже отличение свой-чужой по лишнему полю?
ManHunter
(16.03.2012 в 13:40):
Решил нахаляву получить решение своей лабораторной работы? Ты ошибся сайтом.
Alexey
(16.03.2012 в 13:38):
хотелось бы увидеть пример чтоб веденную строку edit сохранялся в текстовый файл
invoke CreateFile,addr file,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
то есть мы для начала создаем файл а потом туда и записываем с веденного нашего поля
invoke CreateFile,addr file,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
то есть мы для начала создаем файл а потом туда и записываем с веденного нашего поля
chak_xakep
(22.12.2010 в 09:25):
ManHunter,
"Если затачивать прогу именно под эту защиту, зная ее реализацию, то написать можно. Например, с внедрением dll и циклическим перебором разных размеров буфера, пока не будет получен результат."
я тоже об этом задумался))) а если будет перебор буфера, то защита не устоит, хотя если сделать размер буфера*на ID железа )))))))))) ну это изврат конечно же, но я думаю проще будет подменять каждый чар шифруя на ходу каким нибудь ключем а в этот едит виндоу подставлять звездочку)) ну и потом когда астериск попробует глянуть на пасс, то увидит облом! У него будут просто звёздочки...
"Если затачивать прогу именно под эту защиту, зная ее реализацию, то написать можно. Например, с внедрением dll и циклическим перебором разных размеров буфера, пока не будет получен результат."
я тоже об этом задумался))) а если будет перебор буфера, то защита не устоит, хотя если сделать размер буфера*на ID железа )))))))))) ну это изврат конечно же, но я думаю проще будет подменять каждый чар шифруя на ходу каким нибудь ключем а в этот едит виндоу подставлять звездочку)) ну и потом когда астериск попробует глянуть на пасс, то увидит облом! У него будут просто звёздочки...
ManHunter
(23.08.2010 в 08:37):
Если затачивать прогу именно под эту защиту, зная ее реализацию, то написать можно. Например, с внедрением dll и циклическим перебором разных размеров буфера, пока не будет получен результат.
Isaev
(23.08.2010 в 04:58):
cool
А теперь прогу, которая сможет его считать :)
А теперь прогу, которая сможет его считать :)
Добавить комментарий
Заполните форму для добавления комментария