Вебинары от команды Centos-admin.ru

image

Здравствуйте, дорогие друзья!

От лица компании Centos-admin.ru сообщаю об открытии нашего канала на Youtube и представляю вам цикл вебинаров.

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

Первый цикл вебинаров запланирован на 5 лекций по следующим тематикам:

  • Вводная лекция. О компании. Система управления конфигурациями. Как мы малым числом поддерживаем больше тысячи машин.
  • Системы виртуализации на личном опыте.
  • Инкрементное резервное копирование. Бэкап как основа спокойствия.
  • Методы аудита и ускорения работы серверов и проектов.
  • Создание своего репозитория. Сборка пакетов.

Вебинары бесплатные. Длительность порядка 30 минут.
Уровень подготовки для слушателей произвольный. Если вы видели Linux консоль вживую и
что-то пытались устанавливать и запускать, этого будет достаточно для понимания, о чем идет речь.

Для участия, пожалуйста, зарегистрируйтесь по ссылке centos-admin.ru/webinars (мы отправим напоминание перед вебинаром). Первый вебинар пройдет 02 июля в 11:00 по московскому времени.

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

Ждем вас на нашем первом вебинаре!

ссылка на оригинал статьи http://habrahabr.ru/post/259989/

Интервью с Александром Макаровым (core Yii) в Пятиминутке PHP

Всем привет!

Возможно, вы уже слушаете еженедельный подкаст о новостях из мира PHP, интересных постах в блогах и современных подходах к разработке: «Пятиминутка PHP». Если ещё нет, то обязательно подпишитесь в iTunes или по RSS!

Сегодня вышел первый выпуск с гостем: Александр Макаров (@SamDark) — один из ведущих разработчиков PHP фреймворка Yii расскажет о PHP-FIG и различных PSR, о планах на Yii 2.1, о языке Go (golang) и микросервисах, а также о проекте над которым он сейчас работает: stay.com — сервис для путешественников.

Послушать выпуск

ссылка на оригинал статьи http://habrahabr.ru/post/261513/

PSD parser или как разобрать файл Photoshop на Java

Как то предложили сделать парсер psd файла. Вроде бы очень просто. Надо найти из каких слоёв состоит документ, вывести перечень слоёв. Если слой текстовый, вывести текст и параметры форматирования. Т.е. названия шрифтов, размеры, отступы и т.д.

С чего начать?
Да поможет нам гугл. Море информации, куча подробностей как делать плагины для фотошопа. Но это всё не то. Нужна консольная программа, которая будет получать на вход psd файлы и выдавать на выходе текстовое описание его внутренностей. Установка фотошопа изначально не планировалась.

Самым полезным оказался вот этот документ: PhotoshopFileFormat — собственно спецификация структуры файла photoshop. Эдакий побайтный скелет .psd файла, как раз то, что доктор прописал.

Далее погуглил на предмет того, а делали ли что либо подобное коллеги на java?
Да, оказывается и такое есть. Parser-psd — краткое описание + исходники там же. Их программа разбирает файл на слои, причём сохраняет их в отдельные файлы(!) плюс добывается информация по нарезке(slices). Для моей задачи немного не то, но как начальный пример очень даже помог.
Файл загоняется в обычный поток DataInputStream in = new DataInputStream(input) и далее работаем с этим потоком побайтно.
Сам файл состоит из пяти основных секций:
1. File header
2. Color mode data
3. Image resources
4. Layer and mask information
5. Image data

header имеет фиксированный размер, а вот остальные блоки могут иметь разный размер. С этим повнимательнее. Один байт мимо и всё дело накроется. Размер указывается в первых четырёх байтах блока, поэтому блоки можно проскакивать, если всё парсить не надо in.skipBytes(size).
Тут главное внимание и чёткое следование спецификации. Внимание! От версии к версии формат меняется, в спецификации эти различия указываются. Однако текущая спецификация, которую мне удалось найти от 2010 года, а мне пришлось пользоваться ещё более старым описанием за 2007й год. Поэтому при разборе самых свежих версий файлов можно промахнуться.

ссылка на оригинал статьи http://habrahabr.ru/post/261511/

Создание масштабируемого распределенного приложения с нуля


Как это начиналось

Я всю жизнь занимался разработкой под Windows. Сначала на С++, затем на C#. В промежутках мелькали VB, Java Script и другая нечисть. Однако некоторое время назад всё изменилось и я впервые столкнулся с миром Linux, Java и Scala. У нас с Денисом, моим другом и соратником по многочисленным идеям, уже был свой проект – набор утилит для Windows, который пользовался широким спросом в узких кругах. В какой-то момент мы оба потеряли интерес к этому проекту и встал вопрос – что же делать дальше. Денис стал инициатором идеи нового проекта – сервис по обмену clipboard между разными устройствами. Этот проект существенно отличался от предыдущего помимо технологий ещё и целевой аудиторией. Этот сервис должен был стать полезен всем. Скопируйте данные в буфер обмена и вставьте из него на любом другом устройстве. Звучит проще некуда, пока не задумаешься над тем сколько сейчас разных устройств, а также как это все будет работать при большом количестве пользователей.
Первый прототип появился через несколько месяцев. Сервер был написан на ASP.NET и хостился на MS IIS. Было написано 2 клиента: на С++ под Windows и на Java под Android.

Тестирование показало, что прототип держит около 500 соединений. Что же делать, если их будет больше, мы ведь расчитываем на сотни тысяч пользователей 😉 Как написать сервер, который может работать с большим количеством соединений, который не надо будет выключать во время апгрейда железа или софта и который будет легко масштабироваться (то есть расширяться в случае увелечения количества пользователей).

Распределенная масштабируемая система

Для меня такие сложные термины как «распределенная масштабируемая система» ( хорошая статья ) обычно остаются пустым звуком до тех пор, пока я не пойму что за этим стоит с точки зрения технологий и продуктов. Мне недостаточно знать характеристики таких систем и хочется самому попробовать создать такую систему, чтобы осознать все её достоинства и недостатки.

Итак масштабируемая система должна иметь несколько узлов (nodes), чтобы в случае увеличения нагрузки мы могли добавлять новые узлы и в случае выхода одного узла из строя остальные бы продолжали работать. Узел это железная или виртуальная машинка. На этом и остальных рисунках каждый квадратик это сервис, в соответствии с лучшими традициями SOA (Service-Oriented Architecture). В дальнейшем мы поговорим о том где и как можно располагать эти сервисы.

На чем современные компании пишут свои сервера. Например Twitter использует функциональный язык – Scala и имеет свою собственную библиотеку Finagle ( The twitter stack ). Scala позволяет писать неблокирующий (non blocking), неизменяющий ( immutable) код. Первое важно, чтобы экономить ресурсы сервера, тк поток не ждёт освобождение какого-либо ресурса, например при IO (input output) операциях. Второе позволяет в любой момент времени распараллелить код и выполнять вычисления в разных потоках без дополнительных усилий по синхронизации. Новый сервер мы стали писать именно на Scala и сначала с использованием Finagle но позднее переехали на Play framework. Преимущество второго в том, что он более динамично развивается и к нему постоянно появляется множество плагинов.

Для того, чтобы клиенты быстро получали информацию о добавлении нового клипборда на сервер мы использовали long poll технологию. Клиент обращается к сервису и если у сервиса нет новых данных то он не сразу отвечает клиенту, а держит коннекцию в течении заданного таймаута, например 60 секунд. Если в течении этого таймаута сервер получает новые данные, то он немедленно возвращает их для удерживаемых коннекций. Клиент таким образом постоянно повторяет запросы и ждёт обновленных данных.

При таком механизме сервисы MyService1, MyService2 и тд должны уметь сообщать друг другу о новом буфере. Например если клиент висит и ждёт результата от MyService1 однако буфер был добавлен другим клиентом на MyService2 то MyService1 должен немедленно узнать об этом. Для того чтобы сервисы могли извещать друг друга о таких изменениях мы использовали remote actors из библиотеки Akka. Akka это библиотека которая позволяет изпользовать объекты без необходимости синхронизации доступа к ним. Это достигается тем, что каждому actor посылается сообщение, а не делается прямой вызов и в отдельный момент времени только одно сообщение обрабатывается actor. Кроме этого Akka позволяет вызывать actors из другого сервиса таким же образом как и локальные actors. Механизм remote actors скрывает межсетевое взаимодействие, что существенно облегчает разработку. Таким образом используя Akka MyService1 (и любой другой сервис) может извещать все остальные сервисы если к нему поподают новые данные.

Откуда же MyService1 может узнать IP адреса MyService2 и MyService3. Мы использовали ZooKeeper для того, чтобы хранить конфигурацию системы. Таким образом каждый сервис знает IP адрес ZooKeeper и может зарегестрироваться на нем, а также получить с него информацию о том, какие ещё ноды есть в системе.

Из рисунка видно, что хотя мы создали масштабируемую систему, где можно добавлять или удалять ноды прямо в рантайм, однако база данных одна и она остается слабым местом системы. Каждое обращение к базе это достаточно долгая операция. Для того, чтобы снизить нагрузку на базу и увеличить общую производительность системы мы решили добавить кеш. В качестве кеша решено было использовать Redis. Redis это хранилище данных в памяти в формате ключ-значение (NoSQL key-value data store). Несмотря на то, что звучит не слишком понятно, идея очень простая. Redis позволяет получать значения по ключу и хранит всё это в памяти. Таким образом обращение к Redis очень быстрое. Чтобы использовать Redis наш сервис был соответственно изменен и стал обращаться за новыми данными сначала к Redis а только затем, если данные не найдены, к базе. Соответсвенно, когда приходил новый буфер, то он попадал сразу и в базу и в кеш.

Количество квадратиков в нашей схеме непрерывно прирастает, но зато увеличивается и надежность системы. Уберите MyService2 и запросы продолжат обрабатывать MyService1 и MyService3. Уберите Redis и сервисы будут напрямую получать данные из базы. Уберите… Нет, DB и ZooKeeper убирать пока что не надо, тк это обрушит всю нашу систему 🙂

Для того, чтобы обеспечить надежность базы данных она должна поддерживать репликации. Мы выбрали NoSQL базу данных MongoDB тк наши сервисы имеют JSON интерфейс и результаты удобнее хранить в JSON формате, который и поддерживается MongoDB. Кроме этого MongoDB прекрасно поддерживает репликации, те мы можем запустить несколько узлов с MongoDB, связать их в одну реплику и в случае выхода из строя какого либо узла все клиенты смогут продолжить работать с другими узлами. Реплики в MongoDB должны состоять не менее чем из 3х узлов: главный (primary) узел, второстепенный (secondary) и арбитр (arbiter) который следит за остальными узлами и в случае если главный узел вышел из строя, то делает второстепенный узел главным.

Теперь мы можем смело выключать один из узлов с MongoDB и наша система этого даже не заметит. Я не буду подробно останавливаться на ZooKeeper, но и он не будет являться ахелесовой пятой нашей системы, тк также как и MongoDB поддерживает репликации.

Дотошный читатель обратит внимание, что я обошел вопросом как распределяются запросы между узлами MyService1, MyService2 и MyService3. В серьёзных системах для этого используется балансировщик нагрузки – load balancer. Однако, если вы уже устали от бесконечного числа вспомогательных сервисов в нашей системе, то можете использовать DNS в качестве простого балансировщика нагрузки. Когда в DNS приходит запрос к сервису api.myservice.com то он возвращает несколько IP адресов. Хитрость в том, что для каждого запроса порядок этих IP адресов меняется. Например для первого запроса api.myservice.com вернулись: 132.111.21.2, 132.111.21.3, 132.111.21.4 а для второго 132.111.21.3, 132.111.21.4, 132.111.21.2 соответственно клиенты всегда пытаются сначала обратиться к первому IP из списка и только в случае ошибки будут использовать 2-й или 3-й.

Развертывание

В результате мы построили по-настоящему масштабируемую (можно добавлять и удалять узлы-сервисы), распределенную (сервисы могут находится на разных узлах и даже в разных data centers) систему.

Давайте проверим удовлетворяет ли наше приложение основным требованиям предъявляемым к подобным системам.

  • Доступность – наша система всегда доступна. Если мы меняем железо для любой ноды, то остальные ноды продолжают работать. Также мы можем по очереди обновлять ПО
  • Производительность – используя более сложных балансировщик нагрузки мы можем напрявлять европейских пользователей на европейский узел, а пользователей из америки на американский узел. Кроме того использование Redis существенно сокращает обращение к базе данных
  • Надежность – при выходе из строя любого узла, остальные узлы продолжают работать
  • Масштабируемость – можно сколько угодно добавлять MyServiceN
  • Управляемость – хммм, здесь всё ещё есть вопросы, не так ли?
  • Стоимость – все технологии и библиотеки, использованные в данном проекте бесплатные и/или open source

Таким образом под вопросом остался только один критерий – управляемость. Как проще всего разворачивать SOA системы, состоящие из десятков сервисов? Мы решили использовать довольно новую, но хорошо себя зарекомендовавшую технологию – Docker. Docker в чем-то схож с широко изспользуемыми виртуальными машинами, однако имеет ряд преимуществ. Используя Docker вы можете создать docker container для каждого сервиса, а затем запустить их все либо на одной либо на разных машинах. Важный момент, что так как Docker использует встроенный в Linux механизм виртуализации, то он не требует дополнительных ресурсов в отличие от виртуальных машин. Итак мы создали контейнеры для всех узлов нашей системы. Это позволяет одинаково легко развернуть тестовое окружение, где все контейнеры запущены на одном узле и рабочее окружение, где каждый контейнер на своём узле.

В какой-то момент мы столкнулись с вопросом где же размещать нашу систему. Рассматривался самый популярный облачный провайдер – Amazon, однако решили сэкономить и разместить наше приложение на более дешевом Digital Ocean.

ссылка на оригинал статьи http://habrahabr.ru/post/261509/

«Прокачиваем» notepad.exe

image

Какая ассоциация связана у Вас с клавишей F5? Обновление страницы в браузере? Копирование файла из одной директории в другую? Запуск приложения из Visual Studio? А вот авторы notepad.exe подошли к этому вопросу довольно оригинально — по нажатию клавиши F5 происходит добавление текущей даты и времени в место, куда в этот момент указывает курсор. Всё было бы круто, если бы в notepad.exe была такая популярная и вполне естесственная для большинства текстовых редакторов фича, как перечитывание содержимого текущего файла, которая, казалось бы, и должна быть назначена на F5 / Ctrl-R или ещё какой-нибудь общепринятый хоткей.

Мы можем ждать, пока её реализуют Microsoft, выбрать другой текстовый редактор (ведь это не единственное ограничение по функционалу стандартного notepad.exe) или… Взять в руки дизассемблер, отладчик и редактор PE-файлов.

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

Чтобы не иметь дело с теми же неудобствами, с которыми мы столкнулись в предыдущей статье, давайте для начала отключим использование ASLR. Согласно вики, ASLR (Address space layout randomization) — это технология, при использовании которой случайным образом изменяется расположение в адресном пространстве процесса важных структур, а именно: образа исполняемого файла, подгружаемых библиотек, кучи и стека. Именно из-за неё в прошлый раз перезапуск приложения и приводил к изменению уже найденных нами ранее адресов. Если Вы используете Windows XP или более старую ОС, то можете с лёгкостью пропустить то, о чём будет рассказано в нескольких следующих абзацах, ведь ASLR на тот момент ещё не было.

Отключить использование ASLR можно как глобально (для этого необходимо добавить / отредактировать значение опции «MoveImages», хранящейся в реестре по адресу «HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management», чтобы сделать её равной нулю), так и локально, т.е. для конкретного исполняемого файла. Последний вариант выглядит более привлекательным, особенно если речь идёт не о виртуальной машине, а о реальной системе, так что давайте остановимся на нём.

Копируем notepad.exe в любую отличную от "%WINDIR%\System32" директорию, скачиваем, разархивируем и запускаем PE Tools, нажимаем Alt-1 и выбираем скопированный ранее notepad.exe:

image

Нажимаем на кнопку «Optional Header» и смотрим на поле DLL Flags, которое в нашем случае равно 0x8140:

image

Значение в этом поле является результатом выполнения операции битового «OR» для констант, перечисленных в официальной документации на MSDN. Несложно заметить, что наш бинарник обладает следующими характеристиками:

IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
0x8000
The image is terminal server aware

IMAGE_DLLCHARACTERISTICS_NX_COMPAT
0x0100
The image is compatible with data execution prevention (DEP)

IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
0x0040
The DLL can be relocated at load time

Обратили внимание на последнее значение? Что ж, это именно то, что нас интересует. Меняем 0x8140 на 0x8100, нажимаем «Ok» в обоих окнах и приступаем к отладке.

На какие этапы можно условно разделить наш патчинг notepad.exe?

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

Открываем notepad.exe в OllyDbg и приступаем к первому этапу.

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

Надеясь, что файл при сохранении каждый раз открывается заново, ставим бряки на вызовы WinAPI-функции CreateFileW:

image

Нажимаем Ctrl-S, выбираем имя файла (в моём случае это «C:\helper.txt») и останавливаемся на следующем месте:

image

Посмотрим, откуда и с какими аргументами нас позвали:

image

Если посмотреть, на что указывает адрес, переданный в качестве второго аргумента (right-click по строке с данным аргументом -> Follow address in stack), то мы увидим как раз наш путь:

image

Давайте посмотрим на код, находящийся перед вызовом исследуемой нами процедуры, чтобы понять, откуда и как именно к нам попал этот адрес:

image

Как Вы видите, адрес, по которому хранится путь до файла, содержится в EBP-8. Давайте снова нажмём Ctrl-S и посмотрим, куда мы попадём на этот раз (ведь теперь программа уже знает путь до файла, что может поменять ход работы приложения):

image

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

image

На этот раз адрес, по которому содержится путь до файла, хранится в регистре EBX. С момента начала текущего case-блока (обратите внимание на комментарий несколькими инструкциями раньше выделенного места) значение данного регистра не изменяется, что означает, что искать оригинальный адрес надо где-то раньше. Смотрим, какие инструкции ссылаются на начало данного case-блока (left-click по адресу 0x01004D5D -> Ctrl-R):

image

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

image

Итак, мы поняли, что по адресу 0x0100CAE0 хранится путь до текущего файла. Что дальше? А дальше мы должны найти процедуру, ответственную за считывание содержимого файла.

Очевидно, что она также будет вызывать CreateFileW (вместо этого мы могли бы перехватить вызов функции GetOpenFileName, но её нет в списке межмодульных вызовов — видимо, вместо неё используется Common Item Dialog API, которое рекомендуется на MSDN). Нажимаем Ctrl-O, выбираем любой файл (я выбрал тот же самый) и, не успев сделать двойной клик мышью, оказываемся на бряке по адресу 0x01006E8C:

image

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

image

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

image

, нажимаем F9, и… Он тут же срабатывает! Ничего, снова нажимаем F9, пытаемся передать фокус окну notepad.exe и видим, что бряк снова срабатывает. Да что ж такое! Давайте посмотрим на начало процедуры, которую вызывает данный CALL:

image

Обратите внимание на единственный комментарий — судя по кол-ву обрабатываемых значений и тому, что мы наблюдаем на практике, данная процедура служит для реакции на любое выполняемое пользователем действие, будь то передача окну notepad.exe фокуса или открытие файла. Видимо, после нажатия Ctrl-O программа не выполняет никакого CALL’а, а лишь переходит на соответствующий case-блок при помощи операции условного перехода. Давайте уберём данный бряк, ещё раз попытаемся открыть файл и найдём ближайшую к бряку, стоящему на месте вызова CreateFileW, инструкцию, к которой есть обращения в коде. Ею оказалась инструкция по адресу 0x01004DF5:

image

Ставим бряки на оба обращения, проделываем те же самые действия и оказываемся тут:

image

Ставим бряк на начало данного case’а, снова открываем тот же самый файл и пытаемся понять, что тут происходит:

; Зануляем значение в регистре EDI 01003ECC   > \33FF          XOR EDI,EDI                              ;  Case 2 of switch 01001824 ; Вызываем процедуру проверки изменений в текущем файле ; Если они были, отобразится диалоговое окно с предложением сохранить изменения в файл 01003ECE   .  57            PUSH EDI 01003ECF   .  E8 90D7FFFF   CALL notepad.01001664 ; Проверяем возвращаемое значение ; EAX == 1, если изменений не было / пользователь нажал клавишу Save / Don't Save, EAX == 0, если была нажата кнопка Cancel 01003ED4   .  85C0          TEST EAX,EAX ; Если нажали Cancel, то дальнейшее нас уже не интересует, переходим в другой case 01003ED6   .^ 0F84 8ED9FFFF JE notepad.0100186A ; Перемещаем нечто с адреса 0x100C00C в EAX и затем в EBP-10 01003EDC   .  A1 0CC00001   MOV EAX,DWORD PTR DS:[100C00C] 01003EE1   .  8945 F0       MOV DWORD PTR SS:[EBP-10],EAX ; Вызываем процедуру отображения диалогового окна с просьбой выбрать файл 01003EE4   .  8D45 F8       LEA EAX,DWORD PTR SS:[EBP-8] 01003EE7   .  50            PUSH EAX                                 ; /Arg2 01003EE8   .  FF75 F4       PUSH DWORD PTR SS:[EBP-C]                ; |Arg1 01003EEB   .  E8 31000000   CALL notepad.01003F21                    ; \notepad.01003F21 ; В результате вызова данной процедуры в EBP-8 будет храниться путь до открываемого файла ; EAX == 0 в случае успеха и 0x800704C7 в случае нажаия кнопки Cancel 01003EF0   .  8BF0          MOV ESI,EAX 01003EF2   .  3BF7          CMP ESI,EDI ; Один из прыжков на интересующую нас процедуру 01003EF4   .  0F8D FB0E0000 JGE notepad.01004DF5 01003EFA   .  81FE C7040780 CMP ESI,800704C7 01003F00   .  0F85 DC0E0000 JNZ notepad.01004DE2 01003F06   >  3BF7          CMP ESI,EDI 01003F08   .  0F8D E70E0000 JGE notepad.01004DF5 01003F0E   >  8B45 F0       MOV EAX,DWORD PTR SS:[EBP-10] 01003F11   .  A3 0CC00001   MOV DWORD PTR DS:[100C00C],EAX 01003F16   .  56            PUSH ESI 01003F17   .^ E9 A2FCFFFF   JMP notepad.01003BBE 

Теперь давайте посмотрим, какие регистры и адреса использует код по адресу 0x01004DF5, чтобы понять, какое «окружение» необходимо для его корректной работы:

image

Разумеется, данный код обращается к EBP-8, по которому, как Вы помните, хранится путь до открываемого файла. Помимо этого, ему также важно значение регистра EDI, который используется в качестве аргументов для параметров hTemplateFile и pSecurity. Первое мы можем достать из адреса 0x0100CAE0, а в обозначенные параметры можно просто передать ноль.

Теперь давайте найдём код, отвечающий за обработку нажатия клавиши F5. Для этого предлагаю поставить бряк на вызовы функций, отвечающих за получение текущего времени. Наиболее популярные из них — GetSystemTime и GetLocalTime. Первой нет в списке межмодульных вызовов, а вот вторая вызывается сразу из двух мест:

image

Ставим бряки, нажимаем F5 и оказываемся тут:

image

Прыгаем на место вызова текущей процедуры и попадаем практически в самое начало ещё одного case-блока, который, очевидно, и отвечает за обработку нажатия F5:

image

Отлично. Ищем место для нашего code cave’а и пишем (разумеется, адреса могут отличаться):

0100BEB3      33FF          XOR EDI,EDI 0100BEB5      C745 F8 E0CA0>MOV DWORD PTR SS:[EBP-8],notepad.0100CAE0    ;  UNICODE "C:\helper.txt" 0100BEBC      A1 0CC00001   MOV EAX,DWORD PTR DS:[100C00C] 0100BEC1      8945 F0       MOV DWORD PTR SS:[EBP-10],EAX 0100BEC4    ^ E9 2C8FFFFF   JMP notepad.01004DF5 

Вставляем по адресу 0x0100447B прыжок на наш code cave:

image

Нажимаем F9, снова жмём F5 и наблюдаем следующую картину:

image

Как видите, мы упали где-то в недрах функции CoTaskMemFree. Обратите внимание на аргумент, переданный этой функции — да-да, это адрес нашей строки с путём до файла. Значит, память под неё необходимо выделять при помощи CoTaskMemAlloc. В этом нам может помочь функция SHStrDup, которая создаёт дупликат переданной ей строки, выделив память под неё при помощи CoTaskMemAlloc.

Перезапускаем notepad.exe и ищем адрес функции SHStrDupW в IAT. Для этого смотрим на вызов любой другой WinAPI-функции в модуле:

image

Следовательно, адрес функции GetDlgItemTextW в IAT — 0x010012A4. Прыгаем на него и ищем нашу SHStrDupW:

image

Получается, её вызов можно оформить в виде инструкции CALL DWORD PTR DS:[010013B4]. Тогда пишем следующий код (проверка на наличие ошибок опущена):

0100BFA5   .  33FF             XOR EDI,EDI 0100BFA7   .  8D45 F8          LEA EAX,DWORD PTR SS:[EBP-8] 0100BFAA   .  50               PUSH EAX                                          ; /pTarget 0100BFAB   .  68 E0CA0001      PUSH notepad.0100CAE0                             ; |Source = "C:\helper.txt" 0100BFB0   .  FF15 B4130001    CALL DWORD PTR DS:[<&SHLWAPI.SHStrDupW>]          ; \SHStrDupW 0100BFB6   .  A1 0CC00001      MOV EAX,DWORD PTR DS:[100C00C] 0100BFBB   .  8945 F0          MOV DWORD PTR SS:[EBP-10],EAX 0100BFBE   .^ E9 328EFFFF      JMP notepad.01004DF5 

Открываем наш файл «C:\helper.txt», убеждаемся, что он пустой, редактируем и сохраняем его в другой копии notepad.exe, нажимаем F5 в отлаживаемой нами версии, и… Файл обновляется!

Давайте сохраним наши изменения в исполняемый файл. Делаем right-click по окну CPU -> Copy to executable -> All modifications -> Copy all и видим:

image

Получается, что мы вылезли за физические границы исполняемого файла. Давайте взглянем на границы секций в PE Tools (кнопка «Sections»)

image

и поместим наш code cave в какое-нибудь другое место. Для получения верхней «границы» области для «безболезненного» патча мы должны сложить Virtual Offset секции .text, куда мы собираемся положить наш патч, её Raw Size и Image Base, т.е. Virtual Offset (0x00001000) + Raw Size (0x0000A800) + Image Base (0x01000000) = 0x0100B800. Поместим его, например, по адресу 0x0100B6CF и попытаемся сохранить изменения ещё раз (right-click по окну CPU -> Copy to executable -> All modifications -> Copy all -> right-click на появившемся окне -> Save file).

Проверяем получившийся исполняемый файл на работоспособность и убеждаемся, что всё ведёт себя так, как и ожидается.

Послесловие

Цель данной статьи — в очередной раз продемонстрировать возможность добавления собственного функционала в существующие программы, не имея при этом на руках исходных кодов. А теперь возвращайтесь к своим vim’ам / emacs’ам / Notepad++ / etc, но помните — если Вы встретите баг или обратите внимание на отсутствие какого-либо функционала в редакторе с закрытым кодом, теперь Вы знаете, что надо делать.

Спасибо за внимание, и снова надеюсь, что статья оказалась кому-нибудь полезной.

ссылка на оригинал статьи http://habrahabr.ru/post/261507/