Фундаментальные основы хакерства

       

Способ 2. Бряк на функции ввода пароля


Вы боитесь творить, потому что творения ваши отражают вашу истинную суть.

Фрэнк Херберт "Ловец душ"

При всем желании метод прямого поиска пароля в памяти элегантным назвать нельзя, да и практичным тоже. А, собственно, зачем искать сам пароль, спотыкаясь об беспорядочно разбросанные буфера, когда можно поставить бряк непосредственно на функцию, его считывающую? Хм, можно и так… да вот угадать какой именно функцией разработчик вздумал читать пароль, вряд ли будет намного проще.

На самом деле одно и тоже действие может быть выполнено всего лишь несколькими функциями и их перебор не займет много времени. В частности, содержимое окна редактирование обычно добывается либо GetWinodowTextA (что чаще всего и происходит), либо GetDlgItemTextA (а это – значительно реже).

Раз уж речь зашла за окна, запустим наш GUI "крякмис" и установим точку останова на функцию GetWindowTextA ("bpx GetWinodwTextA"). Поскольку, эта функция – системная, точка останова будет глобальной, т.е. затронет все приложения в системе, поэтому, заблаговременно закройте все лишнее от греха подальше. Если установить бряк до запуска "крякмиса", то мы словим несколько ложных всплытий, возникающих вследствие того, что система сама читает содержимое окна в процессе формирования диалога.

Вводим какой-нибудь пароль ("KPNC Kaspersky++" по обыкновению), нажимаем <ENTER> - отладчик незамедливает всплыть:

USER32!GetWindowTextA                                 

001B:77E1A4E2  55                  PUSH    EBP         

001B:77E1A4E3  8BEC                MOV     EBP,ESP

001B:77E1A4E5  6AFF                PUSH    FF

001B:77E1A4E7  6870A5E177          PUSH    77E1A570

001B:77E1A4EC  68491DE677          PUSH    77E61D49

001B:77E1A4F1  64A100000000        MOV     EAX,FS:[00000000]


001B:77E1A4F7  50                  PUSH    EAX

Во многих руководствах по взлому советуется тут же выйти из функции по P RET, мол, что ее анализировать-то, но не стоит спешить! Сейчас самое время выяснить: где расположен буфер вводимой строки и установить на него бряк. Вспомним какие аргументы и в какой последовательности принимает функция (а, если не вспомним, то заглянем в SDK):



int GetWindowText(

  HWND hWnd,        // handle to window or control with text

  LPTSTR lpString,  // address of buffer for text

  int nMaxCount     // maximum number of characters to copy

);

Может показаться, раз программа написана на Си, то и аргументы заносятся в стек по Си-соглашению. А вот и нет! Все API функции Windows всегда вызываются по Паскаль- соглашению, на каком бы языке программа ни была написана. Таким образом, аргументы заносятся в стек слева направо, а последним в стек попадает адрес возврата. В 32-разрядной Windows все аргументы и сам адрес возврата занимают двойное слово (4 байта), поэтому, чтобы добраться до указателя на строку, необходимо к регистру указателю вершины стека (ESP) добавить восемь (одно двойное слово на nMaxCount, другое – на сам lpString). Нагляднее это изображено на рис. 3



Рисунок 3 0х02 Состояние стека на момент вызова GetWindowsText

Получить содержимое ячейки по заданному адресу в Айсе можно с помощью оператора "звездочка", вызов которого в нашем случае выглядит так (подробнее – см. документацию, прилагаемую к отладчику):

:d *(esp+8)

0023:0012F9FC 1C FA 12 00 3B 5A E1 77-EC 4D E1 77 06 02 05 00  ....;Z.w.M.w....

0023:0012FA0C 01 01 00 00 10 00 00 00-01 00 2A C0 10 A8 48 00  ..........*...H.

0023:0012FA1C 10 9B 13 00 0A 02 04 00-E8 3E 2F 00 00 00 00 00  .........>/.....

0023:0012FA2C 01 02 04 00 83 63 E1 77-08 DE 48 00 0A 02 04 00  .....c.w..H.....

В буфере мусор – так и следовало ожидать, ведь строка еще не считана. Давайте выйдем из функции по p ret и посмотрим что произойдет (только потом уже нельзя будет пользоваться конструкцией d *esp+8, т.к.


после выхода из функции аргументы будут вытолкнуты из стека):

: p ret

:d 0012F9FC

0023:0012F9FC 4B 50 4E 43 20 4B 61 73-70 65 72 73 6B 79 2B 2B  KPNC Kaspersky++

0023:0012FA0C 00 01 00 00 0D 00 00 00-01 00 1C 80 10 A8 48 00  ..............H.

0023:0012FA1C 10 9B 13 00 0A 02 04 00-E8 3E 2F 00 00 00 00 00  .........>/.....

0023:0012FA2C 01 02 04 00 83 63 E1 77-08 DE 48 00 0A 02 04 00  .....c.w..H.....

ОК, это действительно тот буфер, который нам нужен. Ставим бряк на его начало и дожидаемся всплытия. Смотрите, с первого же раза мы очутились именно так, где и надо (узнаете код сравнивающей процедуры?):

001B:004013E3  8A10                MOV     DL,[EAX]

001B:004013E5  8A1E                MOV     BL,[ESI]

001B:004013E7  8ACA                MOV     CL,DL

001B:004013E9  3AD3                CMP     DL,BL

001B:004013EB  751E                JNZ     0040140B

001B:004013ED  84C9                TEST    CL,CL

001B:004013EF  7416                JZ      00401407

001B:004013F1  8A5001              MOV     DL,[EAX+01]

Замечательно! Вот так, безо всяких ложных срабатываний, элегантно, быстро и красиво мы победили защиту!

Этот способ – универсален и впоследствии мы еще не раз им воспользуемся. Вся соль – определить ключевую функцию защиты и поставить на нее бряк. Под Windows все "поползновения" (будь то обращения к ключевому файлу, реестру и т.д.) сводятся к вызову API-функций, перечень которых хотя и велик, но все же конечен и известен заранее.


Содержание раздела