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

       

Способ 3. Бряк на сообщения


Любая завершенная дисциплина имеет свои штампы, свои модели, свое влияние на обучающихся.

Френк Херберт "Дюна"

Если у Вас еще не закружилась голова от количества выпитого во время хака пива, с вашего позволения мы продолжим. Каждый, кто хоть однажды программировал под Windows, наверняка знает, что в Windows все взаимодействие с окнами завязано на сообщениях. Практически все оконные API-функции на самом деле представляют собой высокоуровневые "обертки", посылающие окну сообщения. Не является исключением и GetWindowTextA, – аналог сообщения WM_GETTEXT.

Отсюда следует – чтобы считать текст из окна вовсе не обязательно обращаться к GetWindowTextA, - можно сделать это через SendMessageA(hWnd, WM_GETTEXT, (LPARAM) &buff[0]). Именно так и устроена защита в примере "crack 02". Попробуйте загрузить его и установить бряк на GetWindowTextA (GetDlgItemTextA). Что, не срабатывает? Подобная мера используется разработчиками для запутывания совсем уж желторотых новичков, бегло изучивших пару faq по хаку и тут же бросившихся в бой.

Так может, поставить бряк на SendMessageA? В данном случае в принципе можно, но бряк на сообщение WM_GETTEXT – более универсальное решение, срабатывающее независимо от того, как читают окно.

Для установки бряка на сообщение в Айсе предусмотрена специальная команда – "BMSG", которой мы и пользовались в первом издании этой книги. Но не интереснее ли сделать это своими руками?

Как известно, с каждым окном связана специальная оконная процедура, обслуживающая это окно, т.е. отвечающая за прием и обработку сообщений. Вот если бы узнать ее адрес, да установить на него бряк! И это действительно можно сделать! Специальная команда "HWND" выдает всю информацию об окнах указанного процесса.

<Ctrl-D>

:addr crack02

:hwnd crack02

Handle    Class                         WinProc    TID  Module

 050140    #32770 (Dialog)              6C291B81    2DC crack02

  05013E    Button                      77E18721    2DC crack02


  05013C    Edit                        6C291B81    2DC crack02

  05013A    Static                      77E186D9    2DC crack02



Быстро обнаруживает себя окно редактирования, с адресом оконной процедуры 0x6C291B81. Поставим сюда бряк? Нет, еще не время – ведь оконная процедура вызывается не только при чтении текста, а гораздо чаще. Как бы установить бряк на то, что нам нужно, отсеяв все остальные сообщения? Для начала изучим прототип этой функции:

LRESULT CALLBACK WindowProc(

  HWND hwnd,      // handle to window

  UINT uMsg,      // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

);

Как нетрудно подсчитать, в момент вызова функции, аргумент uMsg – идентификатор сообщения будет лежать по смещению 8 относительно указателя вершины стека ESP. Если он равен WM_GETTEXT (непосредственное значение 0xD) – недурно бы всплыть!

Вот и настало время познакомиться с условными бряками. Подробнее об их синтаксисе рассказано в прилагаемой к отладчику документации. А, впрочем, программисты, знакомые Си вряд ли к ней обратится, ибо синтаксис лаконичен и интуитивно - понятен.

:bpx 6C291B81 IF (esp->8)==WM_GETTEXT

:x

Выходим их отладчика, вводим какой-нибудь текст в качесвте пароля, скажем "Hello", нажимаем <ENTER>, отладчик тут же "всплывает"

Break due to BPX #0008:6C291B81  IF ((ESP->8)==0xD) (ET=2.52 seconds)

Вот, он хвост Тигры и уши плюшевого медведя! Остается определить адрес буфера, в который возвращается считанная строка. Начинаем соображать: указатель на буфер передается через аргумент lParam (см. в SDK описание WM_GETTEXT), а сам lParam размещается в стеке по смещению 0x10, относительно ESP:

адрес возврата  ß ESP

hwnd            ß ESP + 0x4

uMsg            ß ESP + 0x8

wParam          ß ESP + 0xC

lParam          ß ESP + 0x10

Даем команду вывода этого буфера в окно данных, выходим из оконной процедуры по P RET и… видим только что введенный нами текст "Hello"



:d *(esp+10)

:p ret

0023: 0012EB28 48 65 6C 6C 6F 00 05 00-0D 00 00 00 FF 03 00 00  Hello...........

0023:0012EB38 1C ED 12 00 01 00 00 00-0D 00 00 00 FD 86 E1 77  ...............w

0023:0012EB48 70 3C 13 00 00 00 00 00-00 00 00 00 00 00 00 00  p<..............

0023:0012EB58 00 00 00 00 00 00 00 00-98 EB 12 00 1E 87 E1 77  ...............w

:bpm 23:12EB28

Установив точку останова, мы ловим одно откровенно "левое" всплытие отладчика (это видно по явно не "юзерскому" значению селектора CS, равного 8) и, уже тянем руку, чтобы нажать 'x' продолжив отслеживание нашего бряка, как вдруг краем глаза замечаем….

0008:A00B017C  8A0A                MOV     CL,[EDX]

0008:A00B017E  8808                MOV     [EAX],CL

0008:A00B0180  40                  INC     EAX

0008:A00B0181  42                  INC     EDX

0008:A00B0182  84C9                TEST    CL,CL

0008:A00B0184  7406                JZ      A00B018C

0008:A00B0186  FF4C2410            DEC     DWORD PTR [ESP+10]

0008:A00B018A  75F0                JNZ     A00B017C

Эге, буфер-то не "сквозной", - система не отдает его "народу", а копирует в другой буфер. Это видно потому, как из указателя на "наш" буфер EDX символ копируется в CL (то, что EDX – указатель на "наш" буфер следует из того, что он вызвал всплытие отладчика), а из CL он копируется в [EAX], где EAX – какой-то указатель (о котором пока мы еще не можем сказать ничего определенного). Далее – оба указателя увеличиваются на единицу и CL (последний считанный символ) проверяется на равенство нулю – если конец строки не достигнут, то все повторяется. Что ж, суждено нам следить сразу за двумя буферами – ставим еще один бряк.

:bpm EAX

:x

На втором бряке отладчик вскорости всплывает, и мы узнаем нашу родную процедуру сравнения. Ну, а дальнейшее – дело техники.

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

001B:004013F4  8ACA                MOV     CL,DL    



001B:004013F6  3AD3                CMP     DL,BL

001B:004013F8  751E                JNZ     00401418

001B:004013FA  84C9                TEST    CL,CL

001B:004013FC  7416                JZ      00401414

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

001B:00401401  8A5E01              MOV     BL,[ESI+01]

  В Windows 9x обработка сообщений реализована несколько иначе, чем в NT. В частности, оконная процедура окна редактирования находится в 16-разрядном коде. А это – сегментная модель памяти (треска хвостом вперед под хвост Тигре) a la сегмент : смещение. Представляется любопытным механизм передачи адреса – в какой же параметр засунут сегмент? Чтобы ответить на это, взглянем на отчет Айса:

 Break due to BMSG 0428 WM_GETTEXT  (ET=513.11 milliseconds) hWnd=0428 wParam=0666 lParam=28D70000 msg=000D WM_GETTEXT        ^           ^        ^--^^--^        |           |  сегмент/    \смещение  дескриптор окна  |                   |              макс. кол-во символов для чтения

Адрес целиком умещается в 32-разрядном аргументе lParam – 16-разрядный сегмент и 16-разрядное смещение. Посему, точка останова должна выглядеть так: "bpm 28D7:0000"


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