Накануне я нашёл у себя скачанный сто лет назад небольшой исходничок на ассемблере, который вместо всеми не любимых символов "*" показывает человеческий текст. К сожалению, или к счастью, в исходнике небыло коментариев, один сплошной код, который был абсолютно не систематизирован. И мне стало безумно интересно докапаться до самой сути этой технологии. Как же всё-таки обойти эти звёздочки, как это делает OpenPass, чем мы хуже?
Ну и подобная бредятина... которая к нашему делу не имеет отношения... В результате я доработал эту программу и у меня, скажем так, получился более "красивый" код.
Я выяснил не всё, поэтому эта статья не претендует на полноту, это всего-лишь мои доводы после нескольких часов исследования.
На самом деле таких прог куча и нужда в такой проге как таковой практически нулевая, но я наверное такой тормоз, что мне только сейчас захотелось исследовать принцип работы таких прог. Поэтому статья для такиз же тормозов как и я
если кого обидел последним высказыванием, то уж извиняйте братья и сестры... 
Итак, мы начинаем...
Кто программирует тот знает, что текстовое поле имеет свойство PasswordChar, и значит в зависимости от значения этого св-ва вы и имеете возможность видеть или не видеть всё, что находится в текстовом поле! Как правило для полей куда вводится пароль значение этого св-ва = *, для обычных полей оно пустое т.е. после компиляции равно 00.
Напишем небольшую программу, форма и текстовое поле со значением св-ва PasswordChar = *, скомпилируем и получаем:
В уже скомпилированной программе значение этого св-ва зашито, и если мы дизассемблируем программу, то в памяти сможем легко найти этот символ:
Значение по этому смещению равно "2A", в десятичном формате это "42", а 42 это и есть ascii код символа "*".
Кстати, если перебить эти два полубайта на "00", то собсно и всякие звёздочки пропадут сами собой, т.е. при вводе в это текстовое поле вы будете видеть нормальный текст:
>>>
Но нам этот вариант не подходит, нам надо видеть уже введённое значение(хотя в хозяйстве может пригодиться).
Как известно любой объект программы(кнопка, текстовое поле, скролбар, метка) имеет свой уникальный идентификатор - Handle, в народе - хендл! И когда запускается программа каждому объекту присваивается хендл и к хендлу приклепляются соответствующие св-ва объекта прошитые уже в самой программе(TEdit, TButton, TMaskEdit) . Соответственно любой объект и поле для ввода пароля будут отличаться друг от друга минимум одним св-ом - PasswordChar. Вот его-то нам с вами и надо отловить из всей кучи объектов.
Приступаем к кодингу, хочу сказать, что код будет на языке ассемблера, поэтому использовать мы будем одни апи:
Нам надо подумать сначала как же нам получить весь массив объектов для перебора и сравнения каждого объекта по определяющему типу(поле для ввода пароля). Есть такая функция: EnumChildWindows, которая перечисляет все дочерние окна принадлежащие родительскому окну и передаёт дескриптор(хендл) дочернего окна в процедуру обратного вызова(по сути в любую указанную процедуру). Фу мля... помойму я сложно сформулировал, но ничего, по ходу вы всё поймёте!
Что ж, теперь стоит вопрос где найти такое родительское окно, чтобы абсолютно все объекты являлись его дочерними окнами, т.е. объектами создаными после него и имеющие меньший приоритет. Конечно выход один: делать родительским окном наш рабочий стол! Что ж, получим хендл рабочего стола в спомощью функции GetDesktopWindow
На данный момент нашей процедуре был передан хендл объекта, теперь она должна определить не является ли этот объект полем для ввода пароля. Как это сделать? Это собственно и есть самый трудный этап.
Давайте уже непосредственно перейдём к коду, так думаю будет проще и мне и вам:
1. Определяем процедуру и значение hWin как 32 разряда т.е. 4 байта.
2. Вызываем функцию которая и возвратит нам хендл рабочего стола, нашего родительского окна, от которого мы и начнём перебор объектов.
3. Вот это и есть главная функция, которая будет перебирать все дочерние окна относительно десктопа и передавать хендл каждого из них на сравнение процедуре EnumKids, её мы рассмотрим позже.
4. Обнуляет регистр eax
5. Возврат из процедуры
6. Конец процедуры.
Процедура EnumKids:
1. Определяем процедуру и в ней один параметр, 4 байта.
2. Функция которая и получает стиль или тип нашего объекта, возваращает характерную для нашего окна информацию.
3. Вот эта строка будет возвращать два значения, а точнее знака: + и -. В случае если будет возвращён +,т.е. шестой бит в регистре eax равен 0, то программа сделает скачек на строчку 7, это будет означать, что наш объект не будет являться полем со звёздочками, в случае если будет возвращён -, т.е. шестой бит в регистре будет равен 1(тут с этими битами я сам запутался, но это не суть важно
), то объект будет являться именно полем с паролем и будет выполнен код следующий за номерами: 4,5 и 6. Другими словами если стиль не ES_PASSWORD, то идём до строки семь, а если такой стиль принадлежит нашему объекту, то убиваем звёздочки.
4. Вы помните св-во PasswordChar? Помните, что у нас его значение равно символу "*"? Так вот эта строчка устанавливает значение св-ва PasswordChar у объекта с хендлом eHandle равное нулю, т.е. попросту говоря мы убиваем это св-во.
5. Ну и собсно эта функция и выводит результат в объект с хендлом eHandle текст который и был под свёздочками.
6. Конец условного оператора
7. Возвращает хендл, чтобы продолжать перечислять окно.
8. После команды ret мы опять прыгнем на строчку:
Определим следующий хендл и процедура EnumKids повторится!!!
И так будет пока не будут перечислены все дочерние окна и объекты. Потом будет выполнена ret в процедуре main и мы вернёмся в главную процедуру.
Вот как это выглядит в отладчике:
Но для начала я определил хендл текстового поля в нашей небольшой программе:
Handle: 0x0144
Теперь смотрите как это выглядит в отладчике:
Картинка тут
Видите в строке выделенной серым цветом(это команда на которой остановился отладчик после того как программа дошла до хендла который определён нашему тектовому полю со звёздочками) слово hWnd, оно равно 003A0144, три последние цифры совпадают с определённым нами хендлом, значит программа дошла до нашего текстового поля со звёздочками. Далее, в скобочках, отладчик показывает св-ва этого поля.И его класс: ThunderRT6TextBox. Если бы программа была написана на делфи, класс был бы определён как TMaskEdit.
По-сути наша программа использует всего 5 апи функций, ну и ещё 5 строчек полезного кода. Я попытался реализовать это на Visual Basic'e, получился, как это нестранно, гораздо сложночитаемый код, поэтому я решил всё-таки описать программу на ассемблере.
Ну и плюс ко всему прочему размеры готовых ехе программ на ассемблере и VB: 1536 и 16358 байт соответственно.
Я думаю, что многие что-то здесь поняли не до конца или не поняли вообще. Возможно я плохой писатель, а возможно вам стоит ещё немного поднатаскаться в этой области! В любом случае задавайте свои вопросы, я постараюсь ответить, материал действительно довольно сложный, мне самому понадобилось много времени чтобы понять всё до конца.
На самом деле данная технология уже не совсем актуальна, и серьёзные программы не используют такую технологию, но всё-таки такого софта полно.
Например миранда, использует как раз метод защиты который был описан мною ниже(в случае если пароль не хранится на винте), но если пароль уже введён, а кнопка OK не нажата, ну вдруг ваш друган отлить отошёл прежде чем нажать на ОК, а пароль уже ввёл... =), пароль можно узнать используя отладчик, если кому интересно, то эта тема отдельной статьи... =)
Теперь полный листинг кода программы:
Конечно нельзя обойти стороной метод защиты от такого рода программ. Т.к. я из языков высокого уровня хорошо знаю VB, то и реализовывать буду на этом языке, хотя этот метод с лёгкостью можно использовать и в других языках!
Метод немножко ламовский, но зато работает
Я просто отлавливаю символы нажатые в первом текстовом поле(которое доступно пользователю) во второе текстовое поле(которое можно сделать невидимым, отключить и вообще вынести за пределы формы, ну кароче спрятать).
Я сделал это так(проверка на значение 8 идёт потому как 8 - это аски код кнопки BackSpace, соттветственно нам его писать ненадо, он должен выполнять назначеную ему функцию):
Скачать снифер: сЦылка
Скачать не защищённую программу: сЦылка
Скачать программу с защитой: сЦылка
При написании статьи были использованы следующие инструменты:
OllyDBG, HiewDemo, The Customizer, MASM & MS VB 6.0 =)
Оригинал: http://www.k0dery.h15.ru/simbol42/simbol42.htm
Я выяснил не всё, поэтому эта статья не претендует на полноту, это всего-лишь мои доводы после нескольких часов исследования.
На самом деле таких прог куча и нужда в такой проге как таковой практически нулевая, но я наверное такой тормоз, что мне только сейчас захотелось исследовать принцип работы таких прог. Поэтому статья для такиз же тормозов как и я
Итак, мы начинаем...
Кто программирует тот знает, что текстовое поле имеет свойство PasswordChar, и значит в зависимости от значения этого св-ва вы и имеете возможность видеть или не видеть всё, что находится в текстовом поле! Как правило для полей куда вводится пароль значение этого св-ва = *, для обычных полей оно пустое т.е. после компиляции равно 00.
Напишем небольшую программу, форма и текстовое поле со значением св-ва PasswordChar = *, скомпилируем и получаем:
В уже скомпилированной программе значение этого св-ва зашито, и если мы дизассемблируем программу, то в памяти сможем легко найти этот символ:
Значение по этому смещению равно "2A", в десятичном формате это "42", а 42 это и есть ascii код символа "*".
Кстати, если перебить эти два полубайта на "00", то собсно и всякие звёздочки пропадут сами собой, т.е. при вводе в это текстовое поле вы будете видеть нормальный текст:
>>>
Но нам этот вариант не подходит, нам надо видеть уже введённое значение(хотя в хозяйстве может пригодиться).
Как известно любой объект программы(кнопка, текстовое поле, скролбар, метка) имеет свой уникальный идентификатор - Handle, в народе - хендл! И когда запускается программа каждому объекту присваивается хендл и к хендлу приклепляются соответствующие св-ва объекта прошитые уже в самой программе(TEdit, TButton, TMaskEdit) . Соответственно любой объект и поле для ввода пароля будут отличаться друг от друга минимум одним св-ом - PasswordChar. Вот его-то нам с вами и надо отловить из всей кучи объектов.
Приступаем к кодингу, хочу сказать, что код будет на языке ассемблера, поэтому использовать мы будем одни апи:
Нам надо подумать сначала как же нам получить весь массив объектов для перебора и сравнения каждого объекта по определяющему типу(поле для ввода пароля). Есть такая функция: EnumChildWindows, которая перечисляет все дочерние окна принадлежащие родительскому окну и передаёт дескриптор(хендл) дочернего окна в процедуру обратного вызова(по сути в любую указанную процедуру). Фу мля... помойму я сложно сформулировал, но ничего, по ходу вы всё поймёте!
Что ж, теперь стоит вопрос где найти такое родительское окно, чтобы абсолютно все объекты являлись его дочерними окнами, т.е. объектами создаными после него и имеющие меньший приоритет. Конечно выход один: делать родительским окном наш рабочий стол! Что ж, получим хендл рабочего стола в спомощью функции GetDesktopWindow
На данный момент нашей процедуре был передан хендл объекта, теперь она должна определить не является ли этот объект полем для ввода пароля. Как это сделать? Это собственно и есть самый трудный этап.
Давайте уже непосредственно перейдём к коду, так думаю будет проще и мне и вам:
Код:
(1) main proc hWin:DWORD
(2) invoke GetDesktopWindow
(3) invoke EnumChildWindows,eax,addr EnumKids,hWin
(4) xor eax,eax
(5) ret
(6) main endp
1. Определяем процедуру и значение hWin как 32 разряда т.е. 4 байта.
2. Вызываем функцию которая и возвратит нам хендл рабочего стола, нашего родительского окна, от которого мы и начнём перебор объектов.
3. Вот это и есть главная функция, которая будет перебирать все дочерние окна относительно десктопа и передавать хендл каждого из них на сравнение процедуре EnumKids, её мы рассмотрим позже.
4. Обнуляет регистр eax
5. Возврат из процедуры
6. Конец процедуры.
Процедура EnumKids:
Код:
(1)EnumKids proc eHandle:DWORD
(2)invoke GetWindowLong,eHandle,GWL_STYLE
(3).if (eax & ES_PASSWORD)
(4)invoke SendMessage,eHandle, EM_SETPASSWORDCHAR, 0, 0
(5)invoke SendMessage,eHandle,WM_SETFONT,eax, 0
(6).endif
(7)mov eax,eHandle
(8)ret
(9)EnumKids endp
1. Определяем процедуру и в ней один параметр, 4 байта.
2. Функция которая и получает стиль или тип нашего объекта, возваращает характерную для нашего окна информацию.
3. Вот эта строка будет возвращать два значения, а точнее знака: + и -. В случае если будет возвращён +,т.е. шестой бит в регистре eax равен 0, то программа сделает скачек на строчку 7, это будет означать, что наш объект не будет являться полем со звёздочками, в случае если будет возвращён -, т.е. шестой бит в регистре будет равен 1(тут с этими битами я сам запутался, но это не суть важно
4. Вы помните св-во PasswordChar? Помните, что у нас его значение равно символу "*"? Так вот эта строчка устанавливает значение св-ва PasswordChar у объекта с хендлом eHandle равное нулю, т.е. попросту говоря мы убиваем это св-во.
5. Ну и собсно эта функция и выводит результат в объект с хендлом eHandle текст который и был под свёздочками.
6. Конец условного оператора
7. Возвращает хендл, чтобы продолжать перечислять окно.
8. После команды ret мы опять прыгнем на строчку:
Код:
invoke EnumChildWindows,eax,addr EnumKids,hWin
Определим следующий хендл и процедура EnumKids повторится!!!
И так будет пока не будут перечислены все дочерние окна и объекты. Потом будет выполнена ret в процедуре main и мы вернёмся в главную процедуру.
Вот как это выглядит в отладчике:
Но для начала я определил хендл текстового поля в нашей небольшой программе:
Handle: 0x0144
Теперь смотрите как это выглядит в отладчике:
Картинка тут
Видите в строке выделенной серым цветом(это команда на которой остановился отладчик после того как программа дошла до хендла который определён нашему тектовому полю со звёздочками) слово hWnd, оно равно 003A0144, три последние цифры совпадают с определённым нами хендлом, значит программа дошла до нашего текстового поля со звёздочками. Далее, в скобочках, отладчик показывает св-ва этого поля.И его класс: ThunderRT6TextBox. Если бы программа была написана на делфи, класс был бы определён как TMaskEdit.
По-сути наша программа использует всего 5 апи функций, ну и ещё 5 строчек полезного кода. Я попытался реализовать это на Visual Basic'e, получился, как это нестранно, гораздо сложночитаемый код, поэтому я решил всё-таки описать программу на ассемблере.
Ну и плюс ко всему прочему размеры готовых ехе программ на ассемблере и VB: 1536 и 16358 байт соответственно.
Я думаю, что многие что-то здесь поняли не до конца или не поняли вообще. Возможно я плохой писатель, а возможно вам стоит ещё немного поднатаскаться в этой области! В любом случае задавайте свои вопросы, я постараюсь ответить, материал действительно довольно сложный, мне самому понадобилось много времени чтобы понять всё до конца.
На самом деле данная технология уже не совсем актуальна, и серьёзные программы не используют такую технологию, но всё-таки такого софта полно.
Например миранда, использует как раз метод защиты который был описан мною ниже(в случае если пароль не хранится на винте), но если пароль уже введён, а кнопка OK не нажата, ну вдруг ваш друган отлить отошёл прежде чем нажать на ОК, а пароль уже ввёл... =), пароль можно узнать используя отладчик, если кому интересно, то эта тема отдельной статьи... =)
Теперь полный листинг кода программы:
Код:
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;Всякая хрень
.486;Использовать набор команд процессора х486
.model flat, stdcall;Плоская модель памяти
option casemap :none;Делаем метки чуствительными к регистру
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\Comctl32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\shell32.inc
include \masm32\include\oleaut32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\Comctl32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\shell32.lib
includelib \masm32\lib\oleaut32.lib
;Хрень кончилась :)
;<<<<<<<<<<<<<<<<<<<<<<
;Объявляем прототипы функций
EnumKids PROTO :DWORD
main PROTO :DWORD
;<<<<<<<<<<<<<<<<<<<<<<
.data
.code
;<<<<<<<<<<<<<<<<<<<<<<
Start:
call main;Вызываем процедуру main
invoke ExitProcess,0;После того как в процедуре main выполнилась команда RET выходим.
;<<<<<<<<<<<<<<<<<<<<<<
main proc hWin:DWORD
invoke GetDesktopWindow
invoke EnumChildWindows,eax,addr EnumKids,hWin
xor eax,eax
ret
main endp
;<<<<<<<<<<<<<<<<<<<<<<
EnumKids proc eHandle:DWORD
invoke GetWindowLong,eHandle,GWL_STYLE
.if (eax & ES_PASSWORD)
invoke SendMessage,eHandle, EM_SETPASSWORDCHAR, 0, 0
invoke SendMessage,eHandle,WM_SETFONT,eax, 0
.endif
mov eax,eHandle
ret
EnumKids endp
;<<<<<<<<<<<<<<<<<<<<<<
end Start
Конечно нельзя обойти стороной метод защиты от такого рода программ. Т.к. я из языков высокого уровня хорошо знаю VB, то и реализовывать буду на этом языке, хотя этот метод с лёгкостью можно использовать и в других языках!
Метод немножко ламовский, но зато работает
Я сделал это так(проверка на значение 8 идёт потому как 8 - это аски код кнопки BackSpace, соттветственно нам его писать ненадо, он должен выполнять назначеную ему функцию):
Код:
Private Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii <> 8 Then
Text2 = Text2 & Chr(KeyAscii)
Text1 = Text1 & "*"
KeyAscii = 0
Else
On Error Resume Next
Text2.Text = Mid(Text2.Text, 1, Len(Text2.Text) - 1)
Text1.Text = Mid(Text1.Text, 1, Len(Text1.Text) - 1)
End If
End Sub
Скачать снифер: сЦылка
Скачать не защищённую программу: сЦылка
Скачать программу с защитой: сЦылка
При написании статьи были использованы следующие инструменты:
OllyDBG, HiewDemo, The Customizer, MASM & MS VB 6.0 =)
Оригинал: http://www.k0dery.h15.ru/simbol42/simbol42.htm