Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 12 ноября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

Дневники чайника. Чтива I

Обсудить статью на форуме

Массу крэкерских инструментов, видеоуроков и статей вы сможете найти на видеокурсе от нашего сайта. Подробнее здесь.

Автор: Bitfry <bitfry@land.ru>

   На этом сайте (cracklab), я самый не разбирающийся в теме автор. Но в этом, по-моему, вся фишка: кто лучше чайника знает проблемы чайника?
   Однако есть и обратная сторона: в этой чтиве слишком много заблуждений и неточностей.
   Я мог бы исправить те, что уже осознал, но это не статья, это дневник, а переделывать дневники - только портить.
   Поступим следующим образом: все глупости, в которых я раскаялся, будут выделены, поправленны в сносках и обязательно разобраны в следующих статьях (подчеркнутым - неправда, курсивом - неточность).
   Еще раз повторяю: "я не волшебник, я только учусь". Поэтому не верьте всем моим умозаключениям, проверяйте сами, экспериментируйте, читайте, короче - думайте. Для этого, как мне кажется, вы сюда и пришли.

Все мои статьи будут лежать на сайте bitfry.narod.ru (там этот дневник по-другому оформлен).

В поисках приключений на ассемблере под Windows



   Все, что я (Bitfry) здесь буду излагать, это всего лишь первые шаги чайника. А пишу я это с корыстной целью, надеясь на то, что какой-нибудь гуру ассемблера под win32 прочтет этот дневник и найдет время написать мне, в чем я заблуждаюсь и куда идти дальше для погружения в глубину - без лишних пузырьков в крови.

   Однако если вы, уважаемый читатель, - пустой сосуд вроде меня, вам тоже может пригодиться нижеизложенное. При условии, что вы так же, как и Bitfry, жаждете заполнения не вином, но асмом под вынь32, поэтому я постараюсь описывать совершенно очевидные вещи для опытных дайверов низкоуровневого программирования.
   Но не пытайтесь посылать мне вопросы, что да как, я пока только чайник, и все, что знаю, пишу в этом дневнике. С другой стороны, если вы что-то полезное обсуждаете, например, на форуме, пригласите меня, обсудим вместе.

   Мне хочется изучать ассемблер, потому что он самый распространенный язык программирования в мире. Не верите?
   Доказывать не стану, но. Допустим, вы захотели исследовать некую интересующую вас программу. Вам понадобится отладчик. Если эту программу написали не вы, то в отладчике вы увидите машинный код, преобразованный в ассемблер. Таким образом, получается, что на каком бы языке ни была написана программа, после ее компиляции она доступна для понимания (если вы, конечно, не человек-компьютер) именно на асме. Мало того, не имеет значения, какой комп (PC или еще что) и какая ОС стоит на нем, все равно исполняемый код программ дизассемблируется. И если вы знакомы с ассемблером для этой системы (в нашем случае речь пойдет о Win32), то любой ход программы от вас не утаится.

Первые шаги


   После долгих поисков учебника (минут 15, потом FAQ прочел :) я остановился  на рассылке Калашникова, прошел ее до середины, но, увы, пришлось свернуть с пути, рассылка сильно устарела, однако в ней очень просто и красиво изложены основы асма под x86. А самое главное, я осваиваю асм как свой первый язык программирования, и прочитав 10 уроков, я понял, что дело это небезнадежное.

   Правда, про системы исчисления можно и попроще сказать, мне папа хорошо объяснил. Главное - это основание. Во всех современных системах исчисления оно все объясняет. Основание = количество символов, используемых в системе. Только от нуля обязательно, в вычислениях все от нуля! Например, в нашей родной (от 0 до 9) - десять, в той, которая становится мне второй родной от (0 до F), - 16d. Ну а когда символы кончаются, разряд уходит влево. Почему влево? Потому что арабы, у которых весь мир считать учился, справа налево пишут, и когда мы математику стали изучать, эти умные ребята уже столько полезных книг написали, что проще было считать по ихнему, чем все слева направо переписать. Если б кто знал тогда, что будет компьютер!

   Все-таки - если вы чайник или даже если асм у вас идет с трудом, пройдите как следует 10 уроков Олега Калашникова и возвращайтесь ко мне. К сожалению, вам понадобится DOS, я взял старенький ноутбук и выполнял примеры на нем, подойдет даже тройка, лишь бы монитор был, можно использовать Win95-98 в ДОС-режиме.

   Рассылку я изучал еще зимой, потом не было времени. И казалось, что все уже заброшенно, а сейчас как навалило: не могу без асма жить, вокруг лето, все на море отдыхают, а я за компом ночами сижу.

Ночь первая. 12 августа 2004 года


   Я полон сил, уверенности, и голова моя пуста, как чашка на столе, пойду заполнять чашку китайским красным чаем и прихвачу шоколадку: для ума очень полезно.
   На сайте www.wasm.ru я взял ассемблер MASM32.  И в первую очередь перевод Iczelion-овских уроков, вещь неплохая, однако сразу после Калашникова очень трудно проходится.
   Iczelion и все, кто участвовал в переводе, проложили хорошую тропу для новичков, а я попытаюсь нарисовать карту, чтоб не заблудиться.

   Непривычно EAX вместо АX у Калашникова. Но тут все просто, еще на 386-м 16-разрядные регистры расширили до 32 разрядов. Как в AX есть старшая и младшая половинки, так и в EAX: младшая - это AX, а старшая : дайте-ка подумать, а никак она не называется: есть - и все тут. А как к старшей половине E-регистров обращаться? Ага, понял: на сайте www.cracklab.ru есть "Огромный FAQ по взлому программ", там все написано.
                                                     EAX
+-----------------------------------------------------+------------------------------+------------------------------+
|   31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16   |   15 14 13 12 11 10  9  8    |    7  6  5  4  3  2  1  0    |
+-----------------------------------------------------+------------------------------+------------------------------+
                       E-часть                                      AH                              AL
  
   Я прошел, именно прошел, а не прочитал, первые три урока Win32 API и мало что понял, но родилась идея: как учил Калашников и не только он, лучше всего изучать асм из отладчика.
   В форточках SoftIce - однозначно! Пользоваться им - это тоже наука, сразу же обжегся, поставил себе выдранный кусок из Numega Driver Studio 2.7 на WinXP, и ничего не заработало. Поставил полностью SoftIce Drive Suite 3.0, все работает.
   "Разобрался" с SoftIce за одну ночь, благо что в инете про него все написано и вопросы на форумах уже все заданы и "отвечены" - но не все про новый SoftIce. Например, я никак не мог его вызвать по точке останова (bpx), а на www.cracklab.ru в статье "Исследование программы BlueFace на предмет ее регистрации"  MozgC написал, что нужно сделать процесс текущим. Если бы не эта статья, пришлось бы доку от SI изучать.
   Настроил отладочную машинку на свой вкус (да, кстати SI из DS 3 проще всего настраивать из его settings, а не из файла winice.dat). Чтобы настроить SI по-человечески, пришлось-таки его доку расматривать, а то у меня монитор ну очень большой и точек на экране очень много, а софтайс был маленький такой в центре. Я прописал lines 82; width 160: так еще куда ни шло.
   Все, устал, завтра начну винду дизассемблировать :).

Ночь вторая. Число нехорошее, ну и фиг с ним


   Я остановился на идее покопаться в SI c той незамысловатой прогой, что в уроке N2 у Iczelion'a.
   Активирую SI, жму Ctrl+D и ставлю бряк (он же bpx) на API-функцию. Предполагаю, что API - это что-то вроде ДОС-прерываний (полезные подпрограммки).

bpx messageboxa
F5

   Заметка из будущего: завтра утром я узнаю, что от способа компиляции зависит смещение в памяти. Чтобы не путать tut2 и tut3, я буду компилировать командами
ml /c /coff /nologo tut2.asm
Link /SUBSYSTEM:WINDOWS /MERGE:.rdata=.text tut2.obj > nul
   Хотя в другом компе адреса могут быть и другие (мне так кажется).


   Запускаю собранный exe-шник 2-го урока (я его назвал tut2.exe) и вижу окошко с кнопочкой ОК.
   Я-то ожидал увидеть айс. Нет, айс появится, только если бряк на API был поставлен после установки текущего процесса

addr

   Попробую по-другому: сначала запуск tut2.exe, потом Ctrl+D.
   Снимаю старый bpx
bc 0
addr tut2

   Оп, в окне айса (если это можно назвать окном :) появляется tut2. Иду в то место, куда начало исполняемого кода помещается.
   Печатаю db 401010
   Почему сюда, пока не знаю, но знаю, что это первое смещение в tut2, которое на bpx откликается. Попробуйте сами правой кнопкой мыши на адрес нажать. Появится меню с самыми частыми командами айса, [Brake on text] - это и есть бряк. Если его раньше чем 00401010 ставить, то айс при запуске tut2 не появится, а на этот адрес тут как тут.

00401010     push   00
00401012     push   00402000                     ; нажал [Display] и увидел, зачем этот адрес в стек кладут
00401017     push   00402021                     ; теперь это понятно
0040101C     push   00
0040101E     call   User32!MessageBoxA           ; а вот здесь что-то не так
00401023     push   00
00401025     call   Kernel32!ExitProcess
0040102A     jmp    [User32!MessageBoxA]         ; это и есть вызов API функции?
00401030     jmp    [Kernel32!ExitProcess]
00401036     int    3                            ; это, похоже, уже не выполнится никогда, я поставил бряк, но
00401037     int    3                            ; айса не увидел


   Вот этот ассемблер я узнаю, никаких тебе invoke, никаких addr. Все почти как в ДОС.
   Вообще, softIce для меня в новинку, но, я думаю, скоро привыкну.
Клавиша F8 в моем айс дает возможность трассировать программу с заходом в функции, но когда я нажал F8 на строке "call User32!MessageBoxA", я не отправился в user32, а попал на jmp, из чего следует: во-первых, call не вызывает API, а во-вторых, он зачем-то нужен. Попробую разобраться.
   На самом деле call User32!MessageBoxA в коде выглядит как E807000000, что в чистом асме означает call 00 00 02 2A. Я посмотрел: содержимое строки 22Ah в tut2.exe при загрузке в память уходит аж до 40 10 2А
По этому адресу и лежит то, что в SI выглядит как "jmp [User32!MessageBoxA]".
Но там тоже нет текста User32: Там FF 25 08 10 40 00, что без анализа отладчика или умного дизассемблера выглядит так:
jmp dword ptr ds: [00401008] или jmp d,[00401008].

   Почему 40 10 08? Там не текст и не код, там вообще непонятно что 84 10h. Думаю, это как-то связано со структурой exe в win32. Тут про PE-формат читать надо.
   После долгих исканий я нашел доку на русском на www.wasm.ru. Для себя я решил, что этот вопрос буду решать в другой раз, но  вырезки из доки приведу (не особо вникая), в самом конце чтивы.
   Еще загадочное объяснение есть во 2-м уроке
::
Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
::
   Сплошной туман. Ничего не понимаю, неужели все так запутано? Может, кто-нибудь объяснит? Ладно, время придет, и это покажется ерундой. А сейчас закрою глаза и забуду о 401008.

   Итак, теперь я знаю, как работает tut2, не считая вышеизложенных мелочей и того, чего я вообще еще не знаю и даже не догадываюсь, и : Короче, все просто:

push     00                 ; толкнуть в стек параметр MB_OK
push     00402000           ; туда же заголовок окна
push     00402021           ; теперь текст окна
push     00                 ; последний параметр, он же первый с точки зрения MessageBox
call     0000022A           ; +"начальное смещение" в памяти, получается переход на 4 строки вниз.
                            ; И самое главное: команда call кладет в стек EIP+5, чтобы потом ret вернул
                            ; куда надо. В EIP всегда текущий адрес, а плюс 5 байт - получится адрес следующий команды
push     00                 ; вот сюда ret и вернет, когда MessageBox отработает
call     00000230           ; + наше и т.д. Попросту, прыг в конец
jmp      d, [00401008]      ; вызов из user32 функции MessageBox (каким-то способом)
jmp      d, [00401000]      ; и сам конец посредством ExitProcess из Kernel32.dll (тем же темным способом)

   Ну что, для начала неплохо, опилки в голове потихонечку подсыхают.

   Я сегодня узнал, что современный ассемблер - это что-то мягкое, в него тыкнешь, а внутри еще чего-то булькает, и не всегда надо до сути сразу докапываться, а то прольется мимо.
   Вот зараза! У меня спинка кресла сломалась, так что на сегодня все, я без спинки не хочу сидеть, придется на сварку кресло нести или на помойку или присверлю как-нибудь. Но это уже другой текст :)

Ночь третья. 14 августа


   Прога из "Урок 3. Простое окно" вовсе не так уж проста, поэтому, чтобы успеть за ночь ее поковырять как следует, обойдусь без лишних отступлений.
   Как все-таки неудобно без спинки, и чего я ее сегодня не починил, вот починил бы вот бы: Так о чем это я?

   Начну с незнакомых слов:
Прототип (структур(см. сноску 01) или функций)
Структура
Хендл (Handle)
Класс (окна, а может, еще чего)

   Нет, я, конечно, знаю все эти слова, но я не знаю, что они означают в программировании. Вот хотя бы ПРОТОТИП. Это ведь образец, или, если по-тупому,  определитель формы, но не содержания. Например, формы функции WinMain:
WinMain, затем директива proto, а теперь 4 двойных слова.
   Как я понял, это значит, что перед вызовом функции WinMain в стек кладут 4 параметра по 4 байта каждый. Теперь я знаю о WinMain, что она снимает из стека 4х4 байта параметров (потом посмотрю в отладчике).

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

LOCAL   wc:WNDCLASSEX

   После выполнения этой директивы в памяти будет зарезервировано 12x4 байта. Потому что в MASM32\ INCLUDE\windows.inc лежит прототип(см. сноску 01) структуры WNDCLASSEX, и он состоит из 12 двойных слов, каждое из которых является переменной.
   Получается:
LOCAL - директива MASM'а
wc - экземпляр структуры
WNDCLASSEX -  прототип(см. сноску 01) структуры
А то, что лежит в windows.inc, описывает прототип(см. сноску 01) WNDCLASSEX

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

   Последнее понятие на сегодня КЛАСС. Я, когда ассемблер выбрал в качестве своего первого языка программирования, совсем не ожидал столкнуться с этим словом. Все, кого я знаю, про С++ мне говорили что-то вроде "Объектно ориентированный", всякие "классы" и еще много умных слов, а про асм что угодно говорили, но только не ООП и не классы. Вот и верь после этого людям.
   Так и не нашел толкового определения, поэтому буду фантазировать.
   Допустим, я художник, например Шишкин, и ко мне пришло вдохновение, и сказало оно мне: твоя картина будет прекрасна, а те, кто этого не поймут, будут восхищаться ее точностью и размерами. Вот я беру холст 8 метров в ширину и 5 в высоту. Нет, вдохновение это, конечно, хорошо, но тут нужны эскизы, хотя бы для того, чтобы количество краски и кистей прикинуть. Понятно, что я не Шишкин и пример дурацкий, но  класс - это что-то типа эскиза. Причем в ассемблере никаких(см. сноску 02) классов нет. Классы есть в Windows и без них сложно жить. Поэтому что Си, что не Си - придется разбираться. Я думаю, у меня на это уйдет много ночей или даже недель, а возможно, лет.
   Прочитав 3-й урок в 6-й раз и набив tut3, я начинаю привыкать к длинным строчкам на асме, и оператор invoke уже не кажется таким уж страшным.
Активирую SoftIce и запускаю tut3.exe, Ctrl+D, потом addr tut3, иду смотреть: что по тому же адресу, с которого начинался исполняемый код в tut2.
db 401010, правой кнопкой мыши щелк на 00401010 и щелк в меню [Un-assembly]
И вижу явную ерунду, значит, в tut3 исполнение начинается еще дальше (кстати, Entry Point - это оно и есть, место, откуда начинается код).
   В исходнике после метки начала кода "start:" идет invoke GetModuleHandle, NULL. Это значит, что в айсе первой исполняемой строкой будет push 0.
Интересно: перед этой строкой идут 8 нулей, также было и в tut2, наверное, это закономерность.
   Ставлю бряк на адрес push 00, F5, закрываю tut3 и открываю снова. Ура, я в айсе!
   Чтобы было удобнее анализировать, я в редакторе открыл исходник, F4 в моем айсе прячет и показывает окно. Можно, конечно, Symbol Loader использовать, но заморачиваться неохота и потом, эта штука полезна только если программа написана вами или  автор столь любезен, что распространяет прогу с условием открытого кода. Если бы я под Линухом сидел: А так лучше и не привыкать в айсе исходники расматривать.
   Это будет долго! Очень долго! Не одну ночь. По крайней мере, для меня.

push     00
call     kernel32!GetModuleHandleA
mov      [00402020], EAX                          ; присваиваем переменной hInstance полученный выше handle-модуля
call     kernel32!GetCommandLineA
mov      [00402024], EAX                          ; переменной CommandLine ее значение
push     0A                                       ; параметр SW_SHOWDEFAULT в стек
push     DWORD PTR [00402024]                     ; очень удобно в айсе давать имена понятным адресам (мышкой [Name])
push     00                                       ; ноль, он и в Африке NULL
push     DWORD PTR [00402020]                     ; тоже можно дать имя, например, hInstance
call     00401071

   В моем айсе (из Drive Suite v3) клавиша F10 позволяет выполнять прогу без захода в процедуры. Если ее нажать на строке "call 00401071", то айс не закроется, а поверх него винды прорисуют окно tut3, выглядит это забавно.
Можно предположить, что процедура WinMain начинается с этого смещения, посмотрим, что там

push     EBP                                      ; значит, кому-то потом потребуется то, что в нем было
mov      EBP,ESP                                  ; в EBP загружаем указатель вершины стека

  
   В стеке сейчас сверху EBP, потом SW_SHOWDEFAULT, CommandLine, NULL, hInstance из начала проги.

add      ESP-50                                   ; вершину стека уводим наверх


   Кажется, здесь резервируется 50h (возможно, под параметры).
   Если рассуждать логически, 50h это 5*16+0=80, это 20 двойных слов (dword). Но что это может быть?
   В исходнике мы на строке

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD


   Это не 20 dword а всего 4. Ниже идут директивы LOCAL, там wc, msg, hwnd, посмотрим в windows.inc, сколько там слов.

WNDCLASSEX      12 dword
MSG                     5 dword и какойто POINT?
HWND                   1 dword

   Итак, складываю, что знаю: 4+12+5+1=22, это не считая непонятного POINT. Поищу определение. В том же windows.inc POINT описан как 2 dword. В моей арифметике получается 4 лишних d-слова. Предположим, что строка объявления функции WinMain в add ESP-50 не участвует, тогда я получил 20 двойных слов, которые мне и нужны, но это нужно доказать, может, ESP-50 здесь вообще не причем.
   Доказать мой вывод можно слегка переделав исходник, например, добавить бесполезную локальную переменную, к трем LOCAL добавить пустышку

LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND

LOCAL x:WNDCLASSEX ; некая переменная x размером 12 dword


   Теперь скомпильну мой альтернативный tut3a. Для автоматизации получения exe-шника я расмотрел bat-ники из MASM32\BIN.

build tut3a

   По идее, в отладчике вместо add ESP-50 должно стать add ESP-80? А если не стало, это вовсе не значит, что я был неправ, это может означать, что компилятор MASMа слишком умный, чтоб в готовый файл пустышки пихать. Да что гадать, айсить надо!
   Бред какой-то, точка входа поднялась с 401040 на 401000, почему? Тут я маху дал, изменения в исходнике не причем. Я все tut-ы до этого собирал не build.bat из \BIN, а командами

ml /c /coff /nologo tut3.asm
Link /SUBSYSTEM:WINDOWS /MERGE:.rdata=.text tut3.obj > nul

   Теперь надо заново собрать, без build-а, чтоб все также было. И заметку написать выше. А со следующего урока буду build.bat использовать.
   Ну вот, другое дело. Теперь по адресу 401074 лежит долгожданный ESP-80.
   Конечно, может, это и совпадение, но уж это я проверять не буду, так как пока я все эти действия выполнял, мне стало совершенно очевидно, что при сборке MASM, как и я, складывает все LOCAL-ы и в полезный код вставляет ESP - сумма байт.

Ночь четвертая. 16 августа


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

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND


   Объявления WinMain в полезном коде нет(см. сноску 05). С LOCAL-ами я разобрался, посмотрю дальше в отладчике.

mov     DWORD PTR [EBP-30], 00000030
mov     DWORD PTR [EBP-2C], 00000003
mov     DWORD PTR [EBP-28], 00401159
mov     DWORD PTR [EBP-24], ERNEL32!ExitProcess      ; у меня в XP такие глюки
mov     DWORD PTR [EBP-20], RNEL32!ExitProcess       ; почему то еще -буква
push    DWORD PTR [00402020]                         ; помните, я ей имя дал hInstance
pop     DWORD PTR [EBP-1С]                           ; это мне пока непонятно
mov     DWORD PTR [EBP-10], 00000006            
mov     DWORD PTR [EBP-0С], NEL32!ExitProcess        ; опять бяка минус бука
mov     DWORD PTR [EBP-08], 00402000

   В отличие от предыдущей строки здесь многое понятно сразу. Во-первых, колонка операторов полностью повторяет исходник, уже хорошо. Во-вторых, все строится по одному шаблону. Двойное слово помещается в зарезервированный отрезок 9 раз (там раньше был стек). И еще делается это снизу вверх, как принято в стеке, хотя при помощи команды mov можно как угодно, хоть от середины в обе стороны.
Сразу же бросается в глаза, что я ни разу не встретил в коде простого слова, все время двойные, это не просто так: dword = 4 байта = 32 бита. В win32 сейчас всё 32 и регистры сегодняшних процов тридцатидвухразрядные. Получается, чтобы сохранить в них 32-битный адрес памяти, опять нужно 4 байта, или, по-другому, - dword.
Через год все изменится, уже сегодня AMD выпускает доступный CPU для домашних пользователей, в котором архитектура 64-битная, а ребята Били вовремя подточили Windows XP под 64 бита.
Я думаю, что ассемблер для начинающих усложнится на одну ступень после перехода на 64, ведь сейчас удобнее сначала регистры типа AX в простеньких примерах использовать. Но все ровно пока все программы можно дизассемблировать, программистам на асме работа найдется, и ученики тоже будут.
Вернусь к коду. Непонятно то, что айс подставляет после запятой. Иногда это цифры, а иногда полный бред. В дизассемблерах (IDA Pro, Hiew, W32Dasm) только цифры, так что это, скорее всего, неудачный анализ софт айса, но я его винить не буду, анализ - это дело человеческое, а не машинное, по крайней мере, так было.
У меня есть мечта заняться криптографией, правда, я про это дело только пару голливудских фильмов смотрел (Enigma, Beautiful Mind), и математикой никогда не занимался. Вообще! Даже в школе, я и в школу-то почти не ходил, но это уже другая история.
Опять отвлекся, старый стал, так о чем я: ах да, анализ.

   Начинается все c [EBP-30]. Если посчитать (в айсе очень удобно "? EBP-30"), то у меня в системе получается 0012FF7C, там теперь будет 00 00 00 30. Там - это значит, что в байте по смещению 12FF7C окажется 30
в 12FF7D будет 00
в 12FF7E следующий 00
в 12FF7F последний 00

   Жалко, что структуру памяти придумали не арабы :), но так тоже можно в памяти все увидеть, каждые 4 байта (dword) читать слева направо, а в голове перекладывать.
   Напомню, что в айсе можно на EBP-30 правой кнопкой мышки нажать и показать [Display] содержимое адреса.
   Я слишком подробно все объясняю, чтоб самому не запутаться и запомнить покрепче.
   Очевидно, что в EBP-30 теперь окажется значение переменной cbSize из структуры wc, которое при сборке программы посчитает MASM, а попросит его об этом SIZEOF.
   В уроке написано что cbSize - размер структуры в байтах. Явно dword - это слишком для такого маленького значения, я просто не могу себе представить структуру 4 миллиарда 294 миллиона 967 тысяч 295 в десятичном размере! Но в win32 всё 32, а памяти нынче в компах навалом (отчасти из-за такого подхода; так удобней компиляторам, тем, кто их пишет, и нам, исследователям :).
   В следующих четырех строках происходит все то же самое, и подчеркивать тут вроде нечего, кроме kernelбреда от айса. Можно, конечно, и отключить все эти подстановки, но тогда придется учить все смещения API-функций, так что отключать я не буду. Проще на колонку исполняемого кода посмотреть, там все по байтам вывернуто, но разобрать можно.
   Дальше тоже несложно, через стек (его вершина сейчас чуть выше) заносим переменную hInstance в [EBP-1С], но почему через стек?  В самом начале проги в эту переменную помещается хэндл модуля, просто mov hInstance,EAX. Почему нельзя так же его отправить в память? Попробую переделать исходник и собрать еще один tut3. Не вышло, error A2070.
   Тут вопрос непринципиальный, нельзя так нельзя. Запомню, что mov не умеет копировать переменную в переменную, запомню и пойду дальше.

   Проскочу три строки, в них все так же.
   А вот здесь в исходнике опять invoke

push    00007F00                    ; кладем в стек параметр IDI_APPLICATION
push    00                          ; параметр NULL - с этими параметрами можно поиграться
call    USER32!LoadIconA            ; оно дает иконку по параметрам

mov     [EBP-18],EAX                ; в "отложенную" память кладется handle иконки

   Я так понял, что функция LoadIcon из библиотеки иконку помещает в память tut3(см. сноску 03), хотя тут я могу и заблуждаться.

mov     [EBP-04]                    ; все ниже некуда, все 30h байт заполнены

push    00007F00                    ; полное повторение ^ 5 строк, но в исходнике другая переменная
push    00  

call    USER32!LoadCursorA          ; курсор мыши. Ну нету у меня доки по API, :( завтра куплю
mov     [EBP-14],EAX                ; ага, не заметил пустоту в 4 байта, теперь точно все 30h полны

   Дальше в исходнике "invoke RegisterClassEx, addr wc" вместо следующих 3 строк

lea     EAX, [EBP-30]               ; в EAX попадает "вершина" того, что много строк подряд заполнялось, и название ему wc, коротко и просто.
push    EAX                         ; как параметр для следующий API-функции
call    USER32!RegisterClassExA     ; ФУФ! Класс окна зарегистрирован.

   Теперь на основе зарегистрированного класса создается окно, но это уже завтра.

Ночь пятая. 18 августа


   Вчера дела за горло взяли, и спать все-таки нужно, хотя последние дни во сне я тоже в отладчике сижу и все чего-то дизассемблирую: то свои мысли, то чужие, вот такие сны :).
   В исходнике опять непонятки, в частности, символ "\" (думаю, это перенос строки для читабельности).

push     00                         ; NULL,\
push     DWORD PTR [EBP+08]         ; hInst,\ (была объявлена в WinMain)
push     00                         ; NULL,\
push     00                         ; NULL,\
push     80000000                   ; CW_USEDEFAULT,\
push     80000000                   ; CW_USEDEFAULT,\
push     80000000                   ; CW_USEDEFAULT,\
push     80000000                   ; CW_USEDEFAULT,\
push     00CF0000                   ; WS_OVERLAPPEDWINDOW,\
push     0040200F                   ; ADDR AppName,\
push     00402000                   ; ADDR ClassName,\
push     00                         ; NULL
call     USER32!CreateWindowExA

   Иными словами, все как всегда в памяти торобоан, я уже привыкать начинаю.
   Окошко уже есть, но не на экране. На ДЕСК так его ТОПе оно само не появится. Тут начинается вторая структура в процедуре WinMain под названием hwnd

mov     [EBP-50], EAX               ; EBP-50 там будет hwnd


   Все, структура заполнена :). Надо еще что-то написать, например, hwnd - хендл окна, полученный в функции CreateWindow

push     DWORD PTR [EBP+14]          ; CmdShow (была объявлена в WinMain)
push     DWORD PTR [EBP-50]          ; hwnd
call     USER32!ShowWindow           ; вот и окошко. Простое такое

push     DWORD PTR [EBP-50]          ; hwnd
call     USER32!UpdateWindow         ; в этом примере не нужно

   Но в настоящих программах, чтобы после загрузки всех элементов (см. сноску 06) окно выглядело как задумано, пригодится.

   Все, окно на экране, задача выполнена, но чего-то не хватает, например, если здесь закончить код (гипотетически), то окно даже закрыть не получится, поэтому настоящая программа обязана иметь цикл сообщений. Не сделал ли пользователь или другая прога чего-то насчет окна, и даже мышь не проскользнет мимо этого цикла. Правда, больше всего сообщений, как я понял, шлет сама винда (только мешает работать моей tut3:).
   Здесь в исходнике вообще начинается какой-то бейсик ".WHILE TRUE", но в чистом коде, слава богу, никакого ".WHILE TRUE" нет и даже его интерпретации нет (просто метка). Есть вот что: Invoke GetMessage с параметрами. Как я понял, у Iczelion'a написано, что после строки GetMessage прога больше не выполняется, пока из GetMessage не придет ответ. То есть Винды подвесили tut3 и могут больше не вернуться (если сбой или если что-то поважнее есть, чем tut3), но у меня почему-то все время возвращаются :), как только мышку чуть сдвину или на кнопку нажму, а если ничего не сделаю, все равно возвращаются. Проверьте сами BPX на 40113A

0040112B   push     00
0040112D   push     00
0040112F   push     00

   Вот (ниже) и 3-я структура, которую LOCAL задал в начале WinMain, в айсе, я напомню, все LOCAL-ы превратились в "add ESP-50".
   "msg" еще ни разу не участвовала в проге, и сейчас она не заполнится. Ее место нахождения передается как параметр для функции GetMessage, а вот на выходе GetMessage ее заполнит, посмотрите в windows.inc, из чего состоит MSG STRUCT.

00401131   lea      EAX, [EBP-4C]             ; в двух строках ADDR msg
00401134   push     EAX  
00401135   call     USER32!GetMessageA
  
   Опять бейсик ".BREAK .IF (!eax)". Это кто придумал, чтоб в асме такие вещи печатать и чтоб он их понимал. Конечно, так оно сразу понятно на родном английском, но проц в моей машине с ума сойдет, если я ему эти байты пихать попытаюсь, а я не сойду и даже привыкну к тому, что для проца инструкции любимые. Без отладчика такой ассемблер мне и даром не нужен.

0040113A   or       EAX,EAX


   Вот это понятно, из GetMessage в EAX ноль придет, только если получен приказ закрыть окно tut3.
   Вообще, оператор or (или) используется для побитового сравнения, но также можно и топорно, как в этом случае. Если EAX ноль, то включается  флаг нуля

0040113C   jz       00401152                  ; если Z, то на 8 строк вниз, если z, то продолжим

0040113E   lea      EAX,[EBP-4C]              ; здесь msg будет переведена, если
00401141   push     EAX                       ; сообщение было скан-кодом клавы
00401142   call     USER32!TranslateMessageA  ; станет ASCII и попадет в очередь, а что такое очередь, я буду разбираться в следующий раз

00401147   lea      EAX,[EBP-4C]              ; если бы в tut3 было много окон,
0040114A   push     EAX                       ; было б из чего выбирать
0040114B   call     USER32!DispatchMessageA   ; а так вперед в WndProc

00401150   jmp      0040112B                  ; это ".ENDW" - END WHILE TRUE

00401152   mov      EAX,[EBP-44]              ; это mov  eax,msg.wParam (понять можно)

   На всякий случай разъясню: в предыдущей строке в регистр EAX загружается переменная wParam из структуры msg.
   Следующая команда leave мне не известна, в исходнике сразу идет ret. Я поискал и нашел интересную штуку, в документации от айса есть такая глава "Referencing the Stack in Conditional Breakpoints". В ней описан самый распространенный способ для Win32 получать доступ к параметрам в глубине "стека". Почему в кавычках? Потому что стек - это стек, а если мы увели вершину стека и работаем с памятью, где он раньше был, это не стек, это стек в кавычках.
   Причем здесь leave? А притом, что в начале мы имеем пролог (в tut3 в с этого начинается WinMain)

push    EBP
mov     EBP,ESP
add     ESP-50

   а в конце эпилог

mov     ESP,EBP
pop     EBP

   leave заменяет эпилог (как бы выровняла(см. сноску 04) стек). И после leave команда ret возьмет из стека правильный адрес возврата

00401155    leave
00401156    ret     0010

00401159    push    EBP
0040115A    mov     EBP,ESP

   Я уже знаю, что это был пролог, и знаю, для чего он нужен.

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

0040115C    cm      DWORD PTR [EBP+0C],02
00401160    jnz     0040116B

   Все, салют окончен, вся логическая часть программы заключается в проверке, не закрыл ли ее кто :(. А если не закрыл, а, например, передвинул, то это уже дело форточное.

   Дальше неинтересно, все и так понятно.


00401162    push    00
00401164    call    USER32!PostQuitMessage
00401169    jmp     00401180
0040116B    push    DWORD PTR [EBP+14]
0040116E    push    DWORD PTR [EBP+10]
00401171    push    DWORD PTR [EBP+0C]
00401174    push    DWORD PTR [EBP+08]
00401177    call    USER32!DefWindowProcA
0040117C    leave
0040117D    ret     0010
00401180    xor     EAX,EAX
00401182    leave
00401183    ret     0010

   Если б у меня был справочник по всем API-функциям, то ночи были бы интересней. А если б у меня был хороший учебник, то хрен бы я все это написал :).

   Вкратце из этого tut-а я узнал

or         понял,понял
jz         Ночь третья. 14 августа

   Вот и все вкратце :)

   Уже можно подвести некоторые итоги. Если у меня на 3 урока ушло 6 ночей, а уроков всего 57?
   Помните, как на старых компах RAR работал до-о-о-лго--до-о-о-лго а на экране он показывал, сколько минут и секунд осталось до конца, циферки все время прыгали, то больше, то меньше, так что сначала он показывал одно, а получалось совсем другое. Так вот и я думаю, что глупо уроки на дни умножать, человек, даже самый тупой, вначале, за чтобы ни взялся, все на порядок медленнее делает. Как вы поняли, я оптимист.

   Ну что, сейчас посплю, туда-сюда, все дела сделаю, а ночью сайт заведу, правда, я понятия не имею, как это делать, но, думаю, что завтра утром дневник будет уже в сети.
   Если ты это читаешь, значит, все получилось, ура!


   C полезными мыслями обращайся к Bitfry на ящик bitfry@land.ru
   Интим не предлагать, если ты, конечно, не девушка-красавица, да еще и хакер, в чем я очень сомневаюсь :)
   Но если серьёзно, напишите, что понравилось, а что нет. Даже если все прочли и вам кажется, что это бред сумасшедшего, напишите. Один мудрец сказал: "Бред одного сумасшедшего не доступен другому сумасшедшему". Я хочу опровергнуть это утверждение.

Сноски:

01. Прототип есть у функций, а у структур - описание (состав, свойство).

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

03. LoadIcon выдаёт handle иконки.

04. leave удаляет стековый фрейм (фрагмент), а выравнивает стек ret 10 (читай Чтиву II).

05. WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
    Эта строка связывает параметры в стеке, которые передаются WinMain, с определенными именами.
    В стеке теперь, с точки зрения процедуры WinMain, лежат не просто цифры, а переменные с определенными именами и заданным размером.

06. Функция UpdateWindow обновляет клиентскую область выбранного окна посредством посылки сообщения WM_PAINT.
    Если окно (или окна) не пустое, функция посылает сообщение WM_PAINT напрямую оконной процедуре, обходя очередь.
    Если окно пустое, сообщение не будет послано.

  
Обещанная вырезка из доки:

ФОРМАТ ИСПОЛНЯЕМЫХ ФАЙЛОВ PortableExecutables (PE)
::
Вот как происходит процесс импорта функций:  Каждой  экспоpтиpyемой  фyнкции
соответствует  некотоpый  номеp,  называемый  оpдиналом.  Эти  номеpа  задаются
последовательно, пеpвый из них pавен Ordinal Base. Некотоpые фyнкции  имеют,  к
томy же, имена. Чтобы найти RVA фyнкции по известномy оpдиналy надо вычесть  из
него Ordinal Base, yмножить pезyльтат на 4 и взять RVA по  смещению  Address  +
полyченное смещение. Чтобы полyчить RVA фyнкции по имени, надо  использyя  Name
Table Pointers найти индекс имени (начинается с  0),  а  затем  использyя  этот
индекс выбpать из Ordinal Table значение, yмножить его на 4 и взять из  Address
Table нyжный RVA. Таким обpазом, AddressTable  содеpжит  соответстyющие  именам
оpдиналы yже скоppектиpованные на Ordinal Base.
К слову об импорте. Ядро Windows'95 уже размещено в соответствующих  областях
памяти   программой   ReBase,   все   адресные   таблицы   импорта    заполнены
соответвующими  значениями  и  ничего  описанного  выше  не  происходит,   ядро
(KERNEL32, USER32, GDI32 и т.д.) просто размещается в ОЗУ. И этого  оказывается
достаточно. Подробности находятся в разделе посвященном импорту.
::
[6] Импорт
~~~~~~~~~~
Мы  подходим  к  самому  интересному  разделу  данного  файла  и  любого  PE
исполнимого  файла.  По  сравнению  со  старыми  16  битными  приложениями  все
значительно упростилось - мы говорим системе о том,  что  мы  хотим  вызвать  и
откуда, а система в нужное место нашей программы предоставляет  адрес  перехода
(внутри нашей FLAT памяти виртуальной машины). Все. Далее адрес уже  используют
по разному.  Borland  строит  самостоятельно  в  секции  кода  (точнее  линкер)
переходники вида
SomeThunkGate: Jmp D,[0XXXXXXXXh]
и все ссылки в программе оформляются:
Call SomeThunkGate
При  этом  задача  организации  импорта  возлагается  на  линковщик  (напомним,
Borland использует старый OMF формат). Прародители метода пошли  другим  путем.
Переходники содержатся в  библиотеке  импорта  и  являются  частью  библиотеки.
Линкер  просто  компонует  ее  в  программу,  причем  с  помощью  одной  хитрой
особенности: имена секций содержащие в  себе  знак  '$'  могут  объединяться  с
отсечением оставшейся части имени (секции  упорядочиваются  перед  слиянием  по
оставшейся части имени).  Линкеру  остается  лишь  из  чего-то  вроде  .idata$1
.idata$2  .idata$3  составить  одну  удобоваримую  секцию  .idata  Следует  еще
добавить, Microsoft в своих программах часто организует вызовы внешних  функций
несколько  иным  образом:  вместо   Near   вызова   переходников   используется
непосредственно требуемый адрес, примерно так
Call DWord Ptr [SomeServiceAddressVariable]
или так
Mov ESi,SomeServiceAddressValue
Call ESi
...
Call ESi
создавшие формат да создадут удобный компилятор ;-)

От автора: Для полного понимания также прочитайте Дневники чайника. Чтива 0, виток 0 + исправления.



Обсуждение статьи: Дневники чайника. Чтива I >>>


Комментарии к статье: Дневники чайника. Чтива I

Russell Mur 26.12.2004 14:34:02
:)
---
Saneok 19.03.2005 15:10:42
Мдаа....
о где найти эти уроки Калашникова
---
Timon 26.03.2005 22:48:30
KRUTO!
---
Автор 04.05.2005 16:26:57
Между прочим, на своём сайте Калашников пишет:
Запрещается любое публичное размещение материала с настоящего сайта без письменного согласия автора.
...
Наверное, поэтому его рассылка лежит не на всех тематических сайтах. :)

http://www.kalashnikoff.ru/

---
Автор 10.06.2005 03:00:11
Кто ещё не начал читать, или не понял сразу, подождите. Через пару дней выйдет \"Чтива 0 виток0\". Оно вместо рассылки Калашникова.
---
Некто 02.08.2005 23:08:27
Мда...
У мене переход от DOS к WIN был тоже мучителен. Щас главная проблема это доки по WIN API/MMX/SSE.

Есть обалденные книги по DOS/WIN16/NT/9x по адресу
http://www.info.datarecovery.ru
Там лежат электронные версии книг \"Библиотеки системного програмиста\" и нетолько от Диалог МИФИ. Собсно при помощи ~6 книг и месяца самообучаловки я и обучился...
---
Автор 15.08.2005 05:03:54
Чтива 0 виток0 и даже одна глава из витка1 готовы.
Читать всем новичкам обязательно!
Дневники I,II,III скорее всего будут подправлены. Здесь довольно много лишнего и слишком много ошибок.
Поэтому и читайте Чтиву0.

---
Goga 09.10.2005 10:42:20
Как сломать пароль к SFX-архиву...?
---
Sert 18.10.2005 00:49:05
А ассемблер где найти ??????? :))))))
---
d1EA3r 19.10.2005 18:40:00
Very good
---

Материалы находятся на сайте https://exelab.ru



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS