Показаны сообщения с ярлыком Ассемблер. Показать все сообщения
Показаны сообщения с ярлыком Ассемблер. Показать все сообщения

20160105

Блог снова оживает.

Всем привет!

У меня вновь появилось свободное время, которое я буду уделять блогу. А чему ещё-то, верно?


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

[http://vk.com/gamehacklab]

А пока что у меня есть исключительно две задумки:

- Дальше топаем по рубрике "Перезагрузка. Ассемблер."
- Берём C++ и пишем свой движок для трейнеров с музыкой и менюшкой

Такие вот планы.


----------------------------------------
Вопросы? Пожелания? Предложения?
Вот как можно со мной связаться:
[email]
[vk]
[twitter]
[telegram]

----------------------------------------

20150920

Перезагрузка. Ассемблер. Часть 3. Знакомимся с отладчиком.

Оглавление:
----------
Часть 0. [Установка и настройка]
Часть 1. [Пишем первую программу]
Часть 2. [Как работает процессор и что такое регистры]

Часть 3. [Знакомимся с отладчиком]

------------------------------------
Весь исходный код можно взять [тут].
------------------------------------


Привет, блога моего читатели!

Сегодня у нас будет чуть более практическая тема статьи. Я уже немного рассказал о том, как работает процессор и что такое регистры. Если коротко подвести итог, то процессор - тупая считалка. Ему нужно сказать, где брать код программы (по какому адресу), а затем он начнет выполнять этот код построчно, команда за командой, буква за буквой. Сам код (программа) при этом загружен в оперативную память компьютера. Чтобы каждый раз не загружать программу с диска в память и не указывать глупому процессору, откуда начинать исполнять программу, нужна операционная система. А вы думали, что в игры играть? Не-а. Так вот. Раньше программы писали путем переключения выключателей (0 и 1), затем - выбивая дырочки на перфокартах (те же 0 и 1) и скармливая стопки этих перфокарт компьютеру, а потом произошло чудо чудное и придумался язык ассемблера - *мнемоническая* запись команд процессора в форме, понятной для человека. После этого стали появляться более крутые (высокоуровневые) языки вроде фортрана или алгола, си, паскаля и так далее. Чем дальше в лес - тем сильнее ощущалась нужда в возможности контролировать ход выполнения программы, потому что эти самые программы становились все больше и сложнее. С этой мыслью программисты того времени придумали инструмент под названием отладчик - специальную программу, позволяющую контролировать ход исполнения другой программы. Эдакий воспитатель в детском саду - тут играть можно, еще можно кашу, а Машу за косички подергать - нельзя, и песком кидаться в Витю тоже нельзя.

В рамках этой серии статей мы будем рассматривать мой любимый отладчик (на самом деле, они не очень сильно друг от друга отличаются - принцип работы у всех одинаковый) под названием Olly Debugger. Скачать его можно или на официальном [сайте] или где-нибудь еще в интернете. На момент написания этой статьи используемая мной версия - 2.01. Скачиваем архив, распаковываем куда-нибудь и - это важно! - запускаем с правами администратора. Вот оно, окошко:



Как и у практически любой другой программы Windows, у этой тоже есть меню. Помните программу из предыдущих статей, которая выводит на экран сообщение? Ищем ее в скомпилированном виде и открываем в отладчике через его меню (File -> Open):




Вот это да, как много всего! Давайте по часовой стрелки, начиная с верхнего-левого угла. Там у нас вот такое:

CPU Disasm
Address   Hex dump               Command                                  Comments
00401000  /$  6A 00              PUSH 0                                   ; /Type = MB_OK|MB_DEFBUTTON1|MB_APPLMODAL
00401002  |.  6A 00              PUSH 0                                   ; |Caption = NULL
00401004  |.  E8 0E000000        CALL 00401017                            ; |Text = "j", jump over immediate data
00401009  |.  48 65 6C 6C 6F 2C  ASCII "Hello, World!",0                  ; |ASCII "Hello, World!"
00401017  |>  6A 00              PUSH 0                                   ; |hOwner = NULL
00401019  |.  FF15 80204000      CALL DWORD PTR DS:[<&USER32.MessageBoxA> ; \USER32.MessageBoxA
0040101F  |.  6A 00              PUSH 0                                   ; /ExitCode = 0
00401021  \.  FF15 60204000      CALL DWORD PTR DS:[<&KERNEL32.ExitProces ; \KERNEL32.ExitProcess

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

Я разберу одну строчку, а дальше станет немного понятней:

00401000 6A 00 PUSH 0 ; /Type = MB_OK|MB_DEFBUTTON1|MB_APPLMODAL

Итак, 00401000 - это адрес в 16-ричной системе счисления, где команда находится в оперативной памяти. Ну, грубо говоря. Адрес этот очень нужно знать глупому процессору, потому что сам он никогда не поймет, откуда ему нужно начинать выполнять код.

6A 00 - это то, как команда выглядит в исполняемом файле, в уже скомпилированном виде. Человеку такое запоминать трудновато, так что для него придумали вот это:

PUSH 0

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

Так, вроде немного разобрались. Переходим к следующему окну, которое верхнее-правое. В нем представлены регистры процессора и то, что в них сейчас находится. Еще там же представлены флаги - они показывают, что что-нибудь 0 или 1, например - если в результате последнего сравнения получилась истина. Или если при делении получился ноль. Специальные такие штуки.

Ниже, под окном команд, находится окно дампа. Это - данные программы. Если сверху - код, то снизу - то, с чем этот код работает. Цифры, буквы, строки текста, картинки и все такое прочее. Все это лежит в оперативной памяти.

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




Дык вот. У нас есть вершина стека (кстати, есть даже специальный регистр процессора, который постоянно указывает на вершину стека - ESP). Мы можем положить данные на вершину и снять с вершины. Только так. Смотрим на картинку с игрушкой. Чтобы снять нижнее, самое большое кольцо с палки, нужно сначала снять все, что выше. Но при этом нижнее колько на палку попало первым. Вот тут так же. Есть две основные команды для работы со стеком - PUSH и POP. Первая кладет, вторая вытаскивает. Примерно так:

PUSH 0 (|-> 0 )
PUSH 1 (|-> 1 0)
PUSH 2 (|-> 2 1 0)
POP EAX (|-> 1 0, в EAX при этом теперь лежит 2)
PUSH EAX (|-> 2 1 0, да, в стек можно засунуть содержимое регистра)
PUSH 25 (|-> 25 2 1 0)
POP EBX (|-> 2 1 0, EBX=25)
POP ECX (|-> 1 0, ECX=2)

Обычно через стек передаются аргументы функций, то есть ее параметры. Например, в нашей программе вызывается функция MessageBox, которая показывает окошко. В исходном коде нашей программы вызов этой функции выглядел так:

invoke MessageBox,HWND_DESKTOP,"Hello, World!",0,MB_OK

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

В отладчике у нас нет никаких макросов и прочих удобств, там все сурово:

PUSH 0 ; Это MB_OK
PUSH 0 ; Это указатель на текстовую строку-заголовок, ее у нас нет.
PUSH 123321 ; Это указатель на текстовую строку-сообщение.
PUSH 0 ; Это HWND_DESKTOP, идентификатор окна-владельца сообщения
CALL MessageBoxA ; Это сам вызов функции

Как видите, в суровом варианте аргументы кладутся на стек по одному, причем в обратном порядке, а не так, как мы записываем в случае с invoke. Invoke при компиляции делает ровно одно - "разворачивается" в набор push и call. Итак, чтобы вызвать функцию, нужно положить на стек аргументы (в обратном порядке!), если они есть, а потом вызывать функцию через call.

Такие дела. Посмотрим, как это выглядит вживую? Конечно, посмотрим! Сейчас наша программа "поставлена на паузу" и отладчик ждет каких-то дальнейших указаний. Мы можем "проиграть" программу по шагам и посмотреть, что с ней происходит. Для этого у нас есть замечательная кнопка F8, позволяющая сделать один "шажок" (выполнить одну инструкцию), при этом "перепрыгивая" вызовы других процедур (call, например). Иначе отладчик "провалится" внутрь этой процедуры и начнет показывать код, который там внутри. Итак, видим команду:

PUSH 0

Что она делает? Она кладет 0 на вершину стека. Смотрим на стек - в самой верхней строчке лежит какая-то непонятная фигня. Давим F8! Получаем вот такую картину:




Процессор проехал на одну команду вниз, выполнив предыдущую, а на вершине стека оказался ноль. Круто? Еще как круто! Давим F8 еще пару раз, выскакивает наше сообщение, жмем в нем OK и на этом программа завершается, о чем нам и сообщает отладчик в строке состояния (Process terminated, exit code 0). Кстати, код возврата мы задаем при вызове функции ExitProcess, единственным ее аргументом. Так-то!

Пока что - на этом все. Можно выбрать в меню Debug->Restart, а можно найти какую-нибудь другую программу и попробовать посмотреть в отладчике на нее. В следующей статье я покажу, как можно при помощи отладчика исправить самый настоящий БАГ. :)

----------------------------------------
Вопросы? Пожелания? Предложения?
Вот как можно со мной связаться:
[email]
[vk]
[twitter]
[telegram]

----------------------------------------


20150817

Перезагрузка. Ассемблер. Часть 2. Как работает процессор и что такое регистры.

Оглавление:
----------
Часть 0. [Установка и настройка]
Часть 1. [Пишем первую программу]
Часть 2. [Как работает процессор и что такое регистры]

Часть 3. [Знакомимся с отладчиком]

------------------------------------
Весь исходный код можно взять [тут].
------------------------------------

Скучали? Я - да. :)

Глядите картинку:



Эта штучка, если вдруг кто не знает, называется Центральный Процессор. И эта штучка помогает компьютеру делать то, что он делает - складывать цифры.

Спросите, нафига я это рассказываю? А рассказываю я это затем, что в прошлой статье мы вызвали пару WinAPI функций, которыми (на самом деле!) дело не ограничивается. Еще возможно, что вы прочитали в какой-нибудь умной книжке про то, что в процессоре есть регистры, а на ассемблере можно с ними как-то взаимодействовать. Дык вот, это - правда. Регистры есть. Что это за слово вообще такое? Представьте себе, что процессор - маленький и слабый, но очень быстро и хорошо думает. Типичный ботаник, в общем. Он не может унести с собой сразу много учебников, но быстро решает задачи, которые в них написаны. Вот регистры - и есть эти самые "учебники". Это просто небольшие места в процессоре, где тот может сохранить какие-то данные, чтобы с ними что-то сделать. Сложить, умножить, из регистра в регистр переставить. Вот картинка:



Ячейки на таблице (все, кроме EFLAGS) - это регистры. EAX, EBX, ECX и так далее.

Теперь немножко о том, почему они так называются. Слышали когда-нибудь такое слово, как "разрядность"? 8 бит, 16 бит, 32 бита, 64 бита. Последние два еще часто любят звать x32 и x64. Встречаем, Intel 8086:

А именно, первый в мире 16-битный микропроцессор. 16-битный значит, что регистры его могут в себя вместить 16 бит каждый, не больше. То есть по 2 байта. от 0x0000 до 0xFFFF в 16-ричной же системе счисления. Вот картинка с его регистрами:

Как вы уже давно заметили, у него в именах регистров отсутствует буква "E". Кстати, буквы H и L означают High и Low, и относятся к половинкам регистров - верхние 8 бит (от 15 до 8) и нижние (от 7 до 0).

Давайте рассмотрим регистры (хотя бы основные) по порядку:



AX - регистр, выполняющий роль основного хранилища данных.
BX - в этом регистре когда-то давно содержался адрес начала исходного кода программы в памяти компьютера.
CX - регистр-счетчик, благодаря которому можно делать циклы.
DX - дополнительное хранилище данных, если в AX все не влезло.

Но, как я уже сказал, это регистры общего назначения, так что можно их использовать, как захочется. По сути, можно положить в один регистр число 2, в другой число 5, а затем заставить процессор сложить их. И положить результат куда-нибудь еще, например, в третий регистр. Круто? Неимоверно!

Возвратимся немного к современному миру. Мы (пока что) будем рассматривать только 32-битный ассемблер, так что наш потолок - 32-разрядные регистры. Это те, в именах которых есть буква "йе". Она происходит от сокращения с английского "Extended" - "Расширенный". Угадали, ага. Это дополнительные 16 бит пространства, так что всего в 32-разрядный регистр влезет аж 32 бита, или 4 байта данных.

Уф! Думаю, хватит пока что теории. В следующей статье мы посмотрим на все эти регистры с практической точки зрения, а заодно узнаем, как исправлять в нашей программе ошибки и выяснять, как вообще она работает. Оставайтесь у своих экранов!

---------------------------------------- 
Вопросы? Пожелания? Предложения? 
Вот как можно со мной связаться: 
[email]
[vk]
[twitter]
[telegram]

----------------------------------------

20150816

Перезагрузка. Ассемблер. Часть 1. Пишем первую программу.

Оглавление:
----------
Часть 0. [Установка и настройка]
Часть 1. [Пишем первую программу]
Часть 2. [Как работает процессор и что такое регистры]

Часть 3. [Знакомимся с отладчиком]

------------------------------------
Весь исходный код можно взять [тут].
------------------------------------

Соскучились? Сегодня приступим к практике и напишем нашу первую программу на ассемблере. :)

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



А теперь резко берем и пишем туда:


Написали? Тыкаем меню Run->Run, или же просто F9. Результат:


Круто? Конечно, круто! Давайте разбираться с этими страшными шестью строчками кода по порядку:

include '%include%/win32ax.inc'

Самая первая строчка говорит, что в этот исходный файл нужно включить еще один. Include - это, собственно, включить, а дальше идет имя файла в одинарных кавычках. Файлы эти лежат в папке include там, где установлен компилятор flat assembler (см. предыдущую статью), а нам сейчас нужен win32ax.inc. В нем объявлены всякие нужные и важные штуки, про которые нам сейчас знать не обязательно, но потом - обязательно узнаем, так что не переживайте. Пока что можно думать, что файл этот нужен и без него ну вот никак. Идем дальше:

.code

Это - название так называемой секции. На самом деле, как мы узнаем в будущем, EXE файлы Windows имеют определенный формат, а именно - PE, или Portable Executable, и состоит он из секций. В одной секции хранится код, в другой - данные, в третьей - ресурсы (иконки всякие и картинки). Догадались? У нас сейчас одна секция и в ней - код, судя по названию. Название, на самом деле, любое может быть.

start:

Start - это как на беговой дорожке на стадионе, отсюда начинать бежать нужно. По-умному это место называется [точкой входа]. Когда мы тыкаем мышью на EXE-файле с желанием его запустить, Windows по сути просто копирует его в оперативную память, находит адрес точки входа и командует процессору - иди туда и начинай выполнять код. Вот и все.

invoke MessageBox,HWND_DESKTOP,"Hello, World!",0,MB_OK

О, начинается код! Тут - самая интересная часть. Этой строчкой мы вызываем функцию, да не просто функцию, а функцию [WinAPI]. Что это за WinAPI такое? Это - набор функций (небольших программ), которые позволяют произвести определенное действие, больше они ничего не делают. Функция "открыть дверь", функция "налить борща", функция "поспать". И вот точно так же - функция "[показать окно с сообщением]". Функции могут принмать параметры, которые называются аргументами, а так же могут возвращать результат. Чаще всего - ноль или не ноль, то есть результат своей работы. Почему так просто? Потому что эта штука существует уже лет 20, а 20 лет назад не было возможностей толком повыпендриваться. Разберем эту строчку еще подробнее:

invoke

Специальная команда, говорящая "выполни команду!". За ней обычно следует имя функции. На самом деле является [макросом].

MessageBox

Встречаем - имя WinAPI-функции! По нему, немного приноровившись, можно легко определить, что функция делает.

HWND_DESKTOP,"Hello, World!",0,MB_OK

После названия функции обычно идут ее аргументы. Тут у нас их аж четыре штуки:

HWND_DESKTOP

Идентификатор окна рабочего стола. Вы думали, что Windows просто так называется Windows (окна)? А вот и нет! На самом деле, в ней ВСЁ - это окна! В том числе и рабочий стол. А чтобы не путаться, у каждого окна есть свой идентификатор, который по факту - просто набор цифр. Идентификатор этот часто называют хэндлом (от английского handle). Так что непонятные буквосочетания в названии идентификатора - HWND - это Handle Window. При компиляции программы компилятор, как самый умный из присутствующих, находит, что на самом деле значит HWND_DESKTOP и заменяет его цифрой-идентификатором. А мы можем писать имя, потому что так понятнее и так проще запоминается.

"Hello, World!"

Текст, который будет показывать нам окно. Прям вот текст и все.

0

Заголовок, вместо которого я написал 0, так что его там не будет. Можно написать что-нибудь в кавычках и посмотреть на эффект.

MB_OK

Эта штука определяет, каким будет окно сообщения. Да-да, они бывают разные. Список возможных можно найти по ссылке чуть выше, там где описание функции. Сейчас у нас будет просто кнопка "ОК". 

Уф! С первой строчкой кое-как разобрались. Осталось немного:

invoke ExitProcess,0

Знакомо? Смутно, но что-то припоминается. Вызываем функцию командой invoke, функция называется [ExitProcess] и принимает один-единственный аргумент, в нашем случае - ноль. Почему ноль? Потому что если программа, завершившись, показывает операционной системе ноль, значит все хорошо - так она думает. Значит завершилась она нормально и без ошибок. А функция сама, как я уже написал, завершает выполнение программы, в результате чего та закрывается.

Последняя строчка осталась:

.end start

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

Ну как? Трудно? Великие гуру всю жизнь твердили вам, что освоить ассемблер - дело избранных и что он невообразимо сложен? Как бы не так! В современном мире и под Windows все сводится к вызову тех или иных WinAPI-функций в нужном порядке и с нужными аргументами. Ничего больше. Серьезно. А вызывать эти функции можно хоть на ассемблере, хоть на Си, хоть на Паскале. Вот так вот!

А вот и [полный исходный код] программы из статьи. :)

---------------------------------------- 
Вопросы? Пожелания? Предложения? 
Вот как можно со мной связаться: 
[email]
[vk]
[twitter]
[telegram]

----------------------------------------

20150809

Перезагрузка. Ассемблер. Часть 0. Установка и настройка.

Оглавление:
----------
Часть 0. [Установка и настройка]
Часть 1. [Пишем первую программу]
Часть 2. [Как работает процессор и что такое регистры]

Часть 3. [Знакомимся с отладчиком]

------------------------------------
Весь исходный код можно взять [тут].
------------------------------------

Ребята, всем привет!

Это - нулевой пост из рубрики постов про Ассемблер для самых начинающих. В нем я покажу, причем не просто покажу, а с картинками, как установить и настроить все необходимое для начала изучения.

Вам не понадобится ни тайных знаний математики, ни опыта программирования, хотя и то и другое как следует приветствуется и лишним никогда не будет.

Поехали! По пунктам:

0. Открываем в браузере сайт - [http://flatassembler.net/]:



1. Идем в раздел "Download" и выбираем "flat assembler for Windows":


2. Распаковываем скачанный архив куда-нибудь. У меня получилось так:


3. Если по двойному клику на файле "FASMW" у вас открывается вот такое окно:


Значит все работает и вы готовы продолжить.

Чуть ниже я покажу дополнительную вещь - для тех (вроде меня), кто любит работать с командной строкой. Итак:

0. Открываем командную строку. Как? Win+R, "cmd" без кавычек, Enter:



Нам необходимо добавить путь к свежескачанному компилятору в системную переменную PATH. Это такая длинная простыня путей, откуда система попытается взять исполняемый файл, который пользователь захочет запустить. Самый первый пример - то, как мы запускали командную строку (Win+R, "cmd"). Вот cmd - это исполняемый файл, ага. Пишем в консоли вот так, держа в уме (или записав на бумажку) путь к папке, куда мы распаковали архив с flat assembler:


И жмем Enter:


Готово! Только нужно проверить, все ли правильно. Закрываем это консольное окно и открываем новое (Win+R, "cmd"), в котором пишем "fasm" без кавычек и давим Enter:


Если у вас картина примерно так же выглядит, значит все настроено верно, с чем и поздравляю. 

---------------------------------------- 
Вопросы? Пожелания? Предложения? 
Вот как можно со мной связаться: 
[email]
[vk]
[twitter]
[telegram]

----------------------------------------

20150808

Про конкурс от gamehacklab, пожертвования и будущие посты.

Ребята, всем привет!

Я кенг, дела у меня хорошо, спасибо. Почему я так долго ничего не писал и ничего не выкладывал? Да, меня тоже это бесит, прекратите кидаться, пожалуйста. Во-первых, наступило лето, но меня, равно как и вас, это не должно особенно волновать. Во-вторых, у меня сменилось место работы. Поэтому я последние 2-3 месяца на работе сижу просто безвылазно - испытательный срок, то да се. Но в этом есть и хорошая сторона - теперь у меня будет больше свободного времени, как минимум по выходным, а выходных как раз хватает на написание поста и\или запись видео.

Сейчас на моем youtube-канале самый актуальный и живой раздел - это "Перезагрузка". Суть в том, что я перезаписываю все старые видео с нуля, с хорошим качеством звука и видео, другими играми и немного на новый лад - то есть немного по-другому объясняю.

На данный момент перезагрузка обучает базовым вещам - поиску и отсеву значений и работе с указателями. Теперь, что достаточно логично, можно переходить к Ассемблеру и работе с отладчиком, а параллельно можно начинать писать свой движок для трейнеров.

Я решил начать со второго пункта, очень аккуратно и потихоньку объяснив язык ассемблера как таковой, как это все работает и что вообще происходит, так что видео будут выходить сначала с объяснениями - мы будем сидеть и изучать сам ассемблер в отладчике на абстрактных примерах, постепенно создавая трейнерный движок, а параллельно я буду показывать некоторые вещи в Cheat Engine - про Code Injection, Code Shifting, Code Relocation, Staple Intersection и тому подобные вещи.

Да, вы все правильно прочитали - теперь в блоге будет ТОЛЬКО ассемблер. Почему? Потому что я его люблю - раз. Потому что с ним в любом случае придется работать хотя бы во время взлома игры - два. Потому что он не такой пугающе сложный, как может поначалу показаться - три. И четыре - потому что его более чем достаточно для написания движка, зато можно будет неплохо выпендриться перед друзьями.

Вы могли заметить в верхней части блога штучку с подписью "помоги кенгу!". Для осмелившихся на ней кликнуть откроется возможность просто так взять и выдать мне любое количество денег на ваше усмотрение. Не то, чтобы они были мне жизненно необходимы и тем более я не собираюсь жить за счет блога, но это будет приятная плюшка, на которую можно будет купить игры или какие-то программы. Текущая цель - Bandicam, который стоит 39 сатанинских долларов. Плюс это в любом случае будет очень сильно меня мотивировать, потому что альтруистичный энтузиазм, на котором уже пять лет держится вся затея, не резиновый. И мне совершенно не хочется бросать это дело.

Больше скажу - мне хочется развивать его дальше, как минимум до уровня мировой сцены. Следовательно, чем больше будет читателей\зрителей и участников, тем быстрее попрет дело, потому что коллективный разум - всегда сильнее. Поэтому я горячо прошу всех читателей блога и зрителей канала на youtube рассказывать об этом в социальных сетях - пусть сарафанное радио работает.

Кстати, возможно я уже говорил, но на форуме gamehacklab несколько месяцев назад официально открылся раздел по взлому многопользовательских игр. И уже даже есть свои наработки, да-да!

А что за конкурс? Конкурс, к сожалению, уже прошедший, но я все равно хочу о нем написать. Берем игру, берем сроки в 2-3-4 дня и затем участники конкурса делают как можно больше опций для игры - бессмертие, бесконечные патроны или что-то еще, быстрое перемещение, возможность летать и так далее. А дальше разыгрывается призовой фонд в виде репутации на форуме, уважухи и разнообразных скидок и игр в Steam. Интересно? Добро пожаловать на форум! Уверен, конкурс этот - не единственный.

Еще совсем недавно открылась страничка Вконтакте, посвященная сайту gamehacklab.ru. Там наш админ выкладывает свою собственную серию обучающих видео, причем он любит C++. Ознакомиться будет все равно полезно, даже если вы все это уже знаете.

Вот она - [ссылочка] на официальную страницу ВК.

На этом - пока все. Думаю, что запишу хотя бы видео-введение по программированию. Критика и пожелания - в комментарии. :)

20130803

IT'S ALIVE! ALIVE!!

Привет всем, кто это читает! Ещё не забыли моё имя и тему этого блога? (:

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

Проблема тут только в одном - я сейчас пересмотрел последние видео и мне кажется, что это уже ни черта не обучающий блог, а какая-то фигня. Поэтому я прошу мнений:

1. Я смотрю последние видео и пробую продолжить от них (d3d, хуки, воллхаки и прочая прелесть на C++). Плюсы я знаю средне, d3d - ещё более средне, так что это будет медленно и, скорее всего, корявенько, но я учусь и исправляюсь.
2. Мы садимся и с нуля пишем движок для трейнеров на C++. Или на ассемблере. Или на C#. С фичами, которые предложите вы, или до которых не догадаюсь я. Весь код я буду заливать на GitHub, конечно же.
3. С нуля пишем какую-нибудь простенькую игру на d3d, потом - компаемся отладчиком в её потрохах и понимаем, как и что работает. Ещё более медленный вариант, но сулит много интересного.
3. Что-нибудь повеселее - то, что вы предложите в комментариях.

20121031



На картинке - наглядная демонстрация, как выглядит Virtual Method Table на более низком уровне. Первая строчка - закомментированный вызов той же самой функции, но при помощи макроса, следующие три - уже без макроса.

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

Если по-человечески, то выглядеть это будет как-то вот так:

pd3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

А вот чтобы выковырять адрес функции из pd3d, нам (или сишному компилятору) нужно сделать такой вот финт ушами, как на картинке выше.

PS: Если вы вдруг читаете заголовочные файлы D3D перед сном, то наверняка заметили, что в 99% функций первым аргументом идёт или D3DObject или D3DDevice. Смекаете, а? Можно перехватить любую функцию и получить доступ к актуальному на данный момент объекту\устройству.

20120816

Direct3D: Финишная прямая

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

Завтра будет Видео №0, до его просмотра рекомендую скачать и установить две вещи:

1. [Visual C++ Express] - на момент написания этого поста, 2010 версия.
2. [DirectX SDK] - на момент написания этого поста, версия от июня 2010 года.

На первом пункте особо останавливаться не буду. Скажу только, что без С++ нам пока что не обойтись. Почему? Потому что DirectX, при помощи которого и создаются 99% игр под Windows - это набор интерфейсов ([API]) для работы с графикой, выполненный в виде сборника [COM]-объектов. Страшные слова заставляют написать, о чём же это, в кратце - а о том, что есть вещь под названием [ООП] - Объектно-ориентированное программирование. Это такой подход, при котором любая вещь в программе - переменная, функция или ещё что-то - является объектом.

В детали я вдаваться не очень хочу, т.к. это довольно нудно и не очень нужно для нашей затеи, но вся штука в том, что лучше всего на работу с такой задачей подходит именно С++.

Не бойтесь, для особо терминальных случаев (как мой, к примеру) можно будет всё переписать и на более низкоуровневых языках (скажем, ассемблере ;), равно как и на аналогах (например, C#).

Моя основная задача - продемонстрировать и объяснить принцип, а вот на каком языке реализовывать идею и какими мелками рисовать картину - это уже дело вашего вкуса, я тут не в праве своё мнение навязывать. :)

20120529

Придумал, что показать в третьем видео про трейнер на C#, скоро запишу и выложу. На ассемблере нам это тоже пригодится, так что ждите. :)

20120525

Про рисование.

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

Очень сложный материал, особенно для подачи, мне бы самому разобраться как следует. С чего начать - пока думаю, скорее всего, покодим немного самостоятельно - сначала порисуем под Windows при помощи GDI\GDI+ (почитать про это можно [тут]), затем будем осваивать [DirectX] и [OpenGL].

Если очень вкратце, то GDI (и его расширение, GDI+) - это интерфейс (набор функций, но если хочется подробностей - [пожалуйста]) для работы с двухмерной (плоской) графикой в винде. Грубо говоря всё, что вы видите на мониторе, рисуется при помощи функций этого интерфейса - окна, кнопки, буквы и так далее. Если почитать подробности в MSDN, можно найти примеры рисования линий, точек, геометрических фигур, работу с кистями и много чего интересного.

Одна проблема - работает эта штука довольно медленно, что не устраивает производителей игр, для которых придумали DirectX - набор интерфейсов для отрисовки графики, работы со звуком и прочими вещами, связанными с играми и OpenGL - ещё одну (полностью открытую) библиотеку для рисования графики. Фишка этих двоих в том, что они рисуют быстро - на порядки быстрее, чем GDI\GDI+, так что все более-менее требовательные к железу игры используют эти технологии.

Их-то нам и предстоит ковырять (или смотреть, как их ковыряю я, попивая чаёк с бутерами), но для ковыряния нужна какая-никакая подготовка.

Для начала, почитайте вот про [эту] функцию. Теперь смотрим на код:


format PE GUI 4.0 
entry start 
include 'win32ax.inc' 
section '.data' data readable writable
ps PAINTSTRUCT ?
hDC dd ? 
section '.text' code readable executable
  start:
        invoke  GetModuleHandle,0
        invoke  DialogBoxParam,eax,37,HWND_DESKTOP,DialogProc,0
        or      eax,eax
        jz      exit
  exit:
        invoke  ExitProcess,0
proc DialogProc hwnddlg,msg,wparam,lparam
        push    ebx esi edi
        cmp     [msg],WM_INITDIALOG
        je      .wminitdialog
        cmp     [msg],WM_COMMAND
        je      .wmcommand
        cmp     [msg],WM_CLOSE
        je      .wmclose
        cmp     [msg],WM_PAINT
        je      .wmpaint
        cmp     [msg],WM_LBUTTONDOWN
        je      .move
        cmp     [msg],WM_RBUTTONUP
        je      .wmclose
        xor     eax,eax
        jmp     .finish
  .move:
        invoke  SendMessage,[hwnddlg],WM_NCLBUTTONDOWN,2,0
        jmp     .processed
  .wmpaint:
        invoke  BeginPaint,[hwnddlg],ps
        mov     [hDC],eax
        invoke  Rectangle,[hDC],10,10,30,30
        invoke  EndPaint,[hwnddlg],ps
        jmp     .processed
  .wminitdialog:
        jmp     .processed
  .wmcommand:
        jmp     .processed
  .wmclose:
        invoke  EndDialog,[hwnddlg],0
  .processed:
        mov     eax,1
  .finish:
        pop     edi esi ebx
        ret
endp 
section '.idata' import data readable writeable
  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          gdi,'GDI32.DLL'
  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess';,\
  import user,\
         DialogBoxParam,'DialogBoxParamA',\
         SendMessage,'SendMessageA',\
         EndDialog,'EndDialog',\
         BeginPaint,'BeginPaint',\
         EndPaint,'EndPaint'
  import gdi,\
         Rectangle,'Rectangle'

section '.rsrc' resource data readable
  directory RT_DIALOG,dialogs
  resource dialogs,\
           37,LANG_ENGLISH+SUBLANG_DEFAULT,demonstration
  dialog demonstration,'Create message box',70,70,190,175,WS_POPUP
  enddialog

В процедуре работы диалогового окна (DialogProc) у нас появился обработчик нового события - [WM_PAINT]. Это крутое событие вызывается всякий раз, когда окно хочет, чтобы его нарисовали. Тем самым GDI, да-да. Следовательно, если у нас есть обработчик этого события -  обязательно надо там что-нибудь нарисовать (в голову лезут неприличные слова и забор)!

Что нам для этого нужно? Для начала, сообщить винде, что мы собрались рисовать. Та самая функция, про которую я предложил почитать чуть выше (BeginPaint), этим и занимается. Для работы ей нужно сообщить хэндл нашего окна и адрес специальной структуры (PAINTSTRUCT), которая хранит всю нужную информацию - размеры окна и т.д., но в принципе она нам не интересна - мы просто её объявили в секции данных и забыли.

Гораздо более интересен нам ответ, который приходит в регистр EAX после выполнения функции. Ответ этот - хэндл на штуковину под названием контекст устройства ([device context]), по сути - структура, описывающая выбранную область рисования, выбранную кисть, её цвет и прочие параметры.

Так, штука важная - сохраняем в отдельной переменной hDC, она нам ещё понадобится. А для чего? Смотрим на следующую строчку и видим вызов функции [Rectangle], которая, как не трудно догадаться из её названия, рисует прямоугольник. Что ей нужно сообщить в аргументах? А всего ничего - контекст устройства (который у нас уже есть) и координаты верхнего правого и нижнего левого угла. Про координаты сделаю небольшую картинку, даже парочку.

Вот как координаты считаются в обычном мире человеков:

Значит, две оси (монитор у нас, как известно, плоский) - абсцисс и ординат, X и Y. Нижний левый угол - ноль. Точка, которую я мастерски нарисовал, имеет координаты (10;10). Всё понятно, правда? На геометрии в школе было то же самое, бррр!

А теперь - как дела обстоят в мире компьютеров:


Всё наоборот, начало координат - верхний левый угол! Предлагаю в верхний правый угол монитора прикрепить бумажку цветную. В общем, привыкнуть придётся. Теперь вспоминаем нашу функцию, рисующую прямоугольник:

invoke  Rectangle,[hDC],10,10,30,30
Координаты верхнего левого угла - (10;10), нижнего правого - (30;30). Вот картинка работающей программы (крестики я нарисовал):

 Значит, два цветных крестика - как раз те самые (10;10) и (30;30), красный и синий, соответственно. Представили, да? Важно тут запомнить, что начало отсчёта (ноль) - верхний левый угол, а не нижний левый, как на геометрии.

После того, как прямоугольник (а в нашем случае - квадрат, но никто не мешает задать другие координаты) нарисовался, мы красноречиво сообщаем, что хватит это терпеть рисование закончено - вызываем [EndPaint], сообщая ей всё тот же хэндл окна и контекст устройства.

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



20111228

Нашёл ещё одну неплохую [доку] по изучению Ассемблера. На русском, да. :)

Апдейт: А [вот тут] лежит куча-куча всяких примеров кода.

20111227

Работа с указателями, или дописываем наш трейнер.

Ещё один небольшой, но ответственный и важный шажок в создании трейнера - работа с указателями. В [видео] кратко пересказана суть этого явления, а так же живой пример вместе с пошаговой отладкой в OllyDbg. Смотрим, задаём вопросы. Извините, что сбиваюсь - хотелось спать. :)

Полный исходный код трейнера (жирным синим шрифтом - новое) из этого урока на Flat Assembler:



format PE GUI 4.0
entry start
include 'c:\fasm\include\win32a.inc'
section '.data' data readable writable
error_message   db 'Error! :(',0
caption         db 'Fasm Trainer',0
pid             dd 0
 ;Необходимо для вызова процедуры чтения указателя через invoke
Poke            dd _Poke 
ReadPointer     dd _ReadPointer
windowcap       db 'Minesweeper',0
addr            dd 0x004256BF
godmode_on      db 0x90,0x90,0x90,0x90,0x90,0x90
godmode_off     db 0xD9,0x9F,0xC4,0xE5,0x48,0x00 
;Новые переменные  
 ;Базовый адрес указателя 
pointer_base    dd 0xFFD9AA38  
buffer          dd ? ;Буфер для чтения байт из памяти 
phandle         dd ? ;Переменная для хранения хэндла процесса


section '.text' code readable executable
start:
        invoke  GetModuleHandle,0
        invoke  DialogBoxParam,eax,37,HWND_DESKTOP,DialogProc,0
        invoke  ExitProcess,0
proc DialogProc hwnddlg,msg,wparam,lparam
     cmp        [msg],WM_CLOSE
     je         .wmclose
     cmp        [msg],WM_COMMAND
     je         .wmcommand
     cmp        [msg],WM_LBUTTONDOWN
     je         .move
     cmp        [msg],WM_INITDIALOG
     je         .init
     cmp        [msg],WM_TIMER
     je         .timer
     xor        eax,eax
     jmp        .finish
  .wmcommand:
     cmp        [wparam],BN_CLICKED shl 16 + 1
     je         .poke
     cmp        [wparam],BN_CLICKED shl 16 + 2
     je         .wmclose
  .poke:
     invoke     Poke,[addr],godmode_on,6
     jmp        .finish
  .wmclose:
     ;invoke     Poke,[addr],godmode_off,6
     invoke     EndDialog,[hwnddlg],0
  .move:
     invoke     SendMessage,[hwnddlg],WM_NCLBUTTONDOWN,2,0
     ret
  .init:
     invoke     SetTimer,[hwnddlg],0,100,0
     ret
  .timer:
     invoke     GetAsyncKeyState,VK_F11
     cmp        eax,0
     je         .finish
     invoke     ReadPointer,[pointer_base] ;Читаем указатель  
.finish:
     ret
endp
proc _Poke,memadd,memval,bytes
     invoke     FindWindow, 0, windowcap
     cmp        eax,0
     je         .error
     invoke     GetWindowThreadProcessId,eax,pid
     cmp        [pid],0
     je         .error
     invoke     OpenProcess,PROCESS_ALL_ACCESS,0,[pid]
     cmp        eax,0
     je         .error
     invoke     WriteProcessMemory,eax,[memadd],[memval],[bytes],0
     cmp        eax,0
     je         .error
     ret
.error:
     invoke     MessageBox,HWND_DESKTOP,error_message,caption,MB_OK
     ret
endp
proc _ReadPointer,address ;Процедура чтения указателя      
invoke     FindWindow,0,windowcap      
cmp        eax,0      
je         .error      
invoke     GetWindowThreadProcessId,eax,pid      
cmp        [pid],0      
je         .error      
invoke     OpenProcess,PROCESS_ALL_ACCESS,0,[pid]      
;Сохраняем хэндл процесса после его открытия     
cmp        eax,0     mov        [phandle],eax 
je         .error          
;Читаем содержимое базового адреса указателя в буфер
invoke     ReadProcessMemory,[phandle],[address],buffer,4,0       
cmp        eax,0      
je         .error     
 ;Прибавляем к содержимому буфера первое смещение
add        [buffer],0x18       
;Читаем полученный адрес
invoke     ReadProcessMemory,[phandle],[buffer],buffer,4,0  
 cmp        eax,0      
je         .error     
 ;Прибавляем второе смещение     
add        [buffer],0x20 
;Читаем полученный адрес
invoke     ReadProcessMemory,[phandle],[buffer],buffer,4,0       
cmp        eax,0      
je         .error           
ret 
.error:      
invoke     MessageBox,HWND_DESKTOP,error_message,caption,MB_OK      
ret  
endp
section '.idata' import data readable writeable
  library kernel,'kernel32.DLL',\
          user,'user32.DLL'
  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess',\
         OpenProcess,'OpenProcess',\
         WriteProcessMemory,'WriteProcessMemory',\
;Объявляем новую функцию для работы с ней
         ReadProcessMemory,'ReadProcessMemory'

  import user,\
         DialogBoxParam,'DialogBoxParamA',\
         EndDialog,'EndDialog',\
         MessageBox,'MessageBoxA',\
         FindWindow,'FindWindowA',\
         GetWindowThreadProcessId,'GetWindowThreadProcessId',\
         SendMessage,'SendMessageA',\
         SetTimer,'SetTimer',\
         GetAsyncKeyState,'GetAsyncKeyState'
section '.rsrc' resource data readable
  directory RT_DIALOG,dialogs
  resource dialogs,37,LANG_ENGLISH+SUBLANG_DEFAULT,demonstration
  dialog demonstration,'Crimsonland +1 trainer by keng',70,70,190,175,WS_POPUP
         dialogitem 'STATIC','Press OK for toggle godmone on. Visit me at: www.gamehacklab.ru',-1,0,0,163,188,SS_LEFT + WS_VISIBLE+  SS_CENTER
         dialogitem 'BUTTON','OK',1,85,150,45,15,WS_VISIBLE
         dialogitem 'BUTTON','Cancel',2,135,150,45,15,WS_VISIBLE
  enddialog

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