понедельник, 23 декабря 2013 г.

Ответ на задачку №2, часть первая

Попробуем разобраться.

Изначально в условии задачи уточнялось - OnPaint не используем.
Правильно это или нет - за условиями задачи, цель была прояснить, понимает ли собеседник поведение VCL в данном случае или нет.
Для упорствующих, конечно, готов очередной подводный камень, к которому мы придем в процессе объяснения, но впрочем по порядку.

Что было предложено в процессе обсуждения вопроса:
1. Зависимость от версий Delphi
2. Выравнивание текста или его многострочность.
3. Смена/пересоздание DC при вызове Memo.Lines[0]
4. Нарушение очереди сообщений из-за вызова обработчика OnClick и как следствие прочие неприятности...
5. Неправильное место вызова, DC "не готов" для вывода (про готовность я не понял, честно, ибо если канва не готова - будет ошибка, все прочее для эстетов :)

Начнем с первого пункта.

пятница, 20 декабря 2013 г.

Задачка на понимание №2

Это уже достаточно старая задача, лет семь (если не отказала память) живет в моих тестах на профпригодность, выдаваемых кандидатам при собеседовании.
В отличии от прошлой задачи, здесь не требуется знаний о памяти процесса включающую работу стека и прочее, в ней тестировалось знание VCL, как она реагирует при взаимодействии с прямыми API вызовами, где сидит засада в общем виде и углубленка по GDI.

Итак, на скорую руку ваяем приложение в котором есть форма, кнопка и TMemo.
Дано 4 варианта реализации кода обработчика кнопки:

var
  ACanvas: THandle;
  AText: string;
begin
  ACanvas := Canvas.Handle;
  AText := Memo1.Lines[0];
  TextOut(ACanvas, Button1.Left, 20, PChar(AText), Length(AText));
end;

...

begin
  ACanvas := Canvas.Handle;
  AText := Memo1.Text;
  TextOut(ACanvas, Button1.Left, 20, PChar(AText), Length(AText));
end;

...

begin
  AText := Memo1.Lines[0];
  ACanvas := Canvas.Handle;
  TextOut(ACanvas, Button1.Left, 20, PChar(AText), Length(AText));
end;

...

begin
  AText := Memo1.Text;
  ACanvas := Canvas.Handle;
  TextOut(ACanvas, Button1.Left, 20, PChar(AText), Length(AText));
end;

Суть данного кода проста - берем текст, который содержится в Memo и тупо выводим его на канву формы.
Вариант того, что отрисовка должна быть в OnPaint, не рассматриваем, он тут лишний.

В результате на форме будет либо выведен текст, либо он будет не выведен, либо уплывет бэкграунд (ХР и ниже), но текст все равно отобразится.

Задача выглядит следующим образом:

четверг, 19 декабря 2013 г.

Ответ на задачу №1

Откуда вообще появляются такие вот непонятные куски кода в которых различные авторы предлагают искать ошибки? Вопрос по сути философский - народное творчество :)
Благодаря народному творчеству и неким "неизвестным" товарищам, чьи произведения подхватываются и расползаются на лету по многочисленным форумам, мы можем наблюдать такие перлы как WParam в обработчике хука объявленный типом Word, либо реализацию метода Execute класса TThread обернутую в Synhronize да, в прочем, можно увидеть даже перекрытие штатной DLLEntryPoint с соглашением вызова stdcall (какая разница что это только обертка над DllMain - пусть и у нас будет так, как у "взрослых дядек" :)
Особенно обидно становится тогда, когда это приобретает массовый характер.
Очень сложно объяснить человеку на форуме, что тот код, который он прочитал в очередной книжке "для хакеров", по сути не верен чуть менее чем полностью, ведь кто я такой - по сути некий неизвестный аноним в интернет пространстве, а у автора вопроса на руках есть целая книга, выпущенная серьезной издательской конторой, к которой доверия будет явно больше чем к моим ответам :)

Сейчас мы будем рассматривать один из образцов такого кода.

Он достаточно популярен в интернете, к примеру:
http://theroadtodelphi.wordpress.com/2009/10/26/detect-aero-glass-using-delphi/
или вот так:
http://www.sql.ru/forum/900738/kak-uznat-vkluchen-li-aero-v-window-7-vista
Где посреди прочих разумных вариантов звучит и такой: "Этот код не приводит к крешу приложения, ищи причину в другом месте."

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

Впрочем... к нашим баранам.

вторник, 17 декабря 2013 г.

Задачка на понимание №1

Основную идею задачек я подсмотрел у Александра Алексеева (более известного как GUNSMOKER), и подумал - а почему бы и мне не открыть такой подраздел, ибо  таких у меня накопилось предостаточно от собеседований кандидатов на вакансию :)

Итак, дана функция, реализованная в классическом стиле :

function IsAeroEnabledCheck: Boolean;
type
  _DwmIsCompositionEnabledFunc = function(IsEnabled: PBool): HRESULT; stdcall;
var
  DllHandle: THandle;
  Flag: Boolean;
  DwmIsCompositionEnabledFunc: _DwmIsCompositionEnabledFunc;
begin
  DllHandle := LoadLibrary('dwmapi.dll');
  if DllHandle > HINSTANCE_ERROR then
  try
    @DwmIsCompositionEnabledFunc := GetProcAddress(DllHandle, 'DwmIsCompositionEnabled');
    if (@DwmIsCompositionEnabledFunc <> nil) then
    begin
      DwmIsCompositionEnabledFunc(@Flag);
      Result := Flag;
    end;
  finally
    FreeLibrary(DllHandle);
  end;
end;

Задача, описать что в данном коде не верно.

четверг, 14 ноября 2013 г.

Карта памяти процесса

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

Спросите, зачем?
Ну как же, для 32-битного приложения 2-3 гигабайта – это ваш лимит за пределы которого без использования AWE вы выбраться не сможете, а контролировать собственные ресурсы все же желательно. Но даже и без этого просто с целью разобраться...

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

Давайте посмотрим, как именно программист работает с памятью при отладке (особенно при отладке стороннего приложения, проще говоря, при реверсе):

1. Как правило, самой частой операцией будет поиск значения в памяти приложения и, к сожалению, данный функционал почему-то не предоставлен в отладчике Delphi (собственно, как и в MS VC++).
2. Модификация системных структур (PEB/TEB/SEHChain/Unwind/директорий PE-файлов etc...) будет происходить гораздо проще, когда поля структур размаплены на занимаемые ими адреса и представлены в читабельном виде.
3. Отслеживание изменений в памяти процесса (практически никем не предоставляемый функционал, реализованный в виде плагинов к популярным отладчикам). Действительно, зачем трассировать до посинения, когда достаточно сравнить два снимка карты памяти, чтобы понять, тут ли происходит нужная нам модификация данных или нет?

Да, собственно, вариантов использования много.

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

Самая удобная реализация от OllyDebug 2, но, к сожалению, она не отображает данные по 64 битам (все еще ждем).

OllyDebug 2 - Process Environment Block Dump

VMMap от Марка Руссиновича выполняет чисто декоративные свойства, да красиво, да за подписью Microsoft, но практически применить выводимые ей данные тяжеловато.

VMMap отображает только самую базовую информацию не вникая в детали.

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

Process Hacker - Memory Dump

Ну а к карте памяти от IDA Pro за столько лет работы с ней я так и не привык (мне не удобно) :)

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

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

суббота, 1 июня 2013 г.

Сортировка списка по аналогу "Проводника Windows"

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

В данной мини-статье я рассмотрю одну из таких "хотелок".

Допустим у вас есть список элементов, отображаемый в TListView, вы пробуете его отсортировать и получаете вот такой результат.


Не красиво, почему это второй элемент с именем "101" находится не на своем месте? Ведь это число, а стало быть место ему как минимум после элемента с именем "2". Да и "New Folder (101)" явно должна быть после "New Folder (2)". Ведь в проводнике все выглядит нормально.


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


воскресенье, 26 мая 2013 г.

Правильное применение сплайсинга при перехвате функций подготовленных к HotPatch

В прошлой статье я рассмотрел пять вариантов перехвата функций включая их вариации.

Правда в ней я оставил не рассмотренными две неприятных ситуации:
1. Вызов перехваченной функции в тот момент, когда ловушка снята.
2. Одновременный вызов перехваченной функции из двух разных нитей.

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

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

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

Перехват сплайсингом через JMP NEAR OFFSET или PUSH ADDR + RET (наиболее уязвимый к данным ошибкам) рассмотрен не будет, т.к. по хорошему, без реализации дизассемблера длин, заставить данный вариант перехвата работать как нужно не получится.


пятница, 26 апреля 2013 г.

Атомарные операции

Буквально на днях ко мне обратились с вопросом.

А зачем нужен префикс LOCK, или его аналог InterlockedDecrement при вызове процедуры _LStrClr из модуля System. Данная процедура декрементирует счетчик ссылок строки и при его обнулении освобождает память, ранее занятую строкой.

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

В принципе предпосылка интересная, но...

понедельник, 22 апреля 2013 г.

Использование Lua скриптов в составе ПО

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

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

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

Ну или в качестве другого варианта возьмем игры - в них разработчики пишут некий базовый фреймворк, а внутреннее наполнение производится скриптами, в которых и содержится большая часть игрового мира.
Хотите новый уровень игрушки - пожалуйста, вот вам новый набор скриптов (утрирую, конечно) :)

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

Вот что-то подобное мы и рассмотрим в данной статье.

В качестве основы скрипта я возьму интерпретируемый язык программирования "Lua".
Данный язык очень часто встречается в том инструментарии, который я использую в повседневной работе, поэтому его выбор для статьи был для меня весьма очевиден.


вторник, 5 марта 2013 г.

Рисуем поверх TWinControl

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

Когда-то давным давно один уважаемый в Delphi сообществе человек разъяснял - почему есть проблемы с выводом графики поверх контролов.

И объяснял он примерно таким образом:
Вот представим себе стол, пусть он будет аналогом формы (TForm) и мы возьмем фломастер и начнем на нем рисовать. Поверхность стола - это его канва (TCanvas) и на ней у нас полный простор для фантазии. А теперь бросим на стол фотографии. Они представляют из себя TImage и собой они закрыли часть рисунка на столе. Они не убрали то изображение, которое было под ними, они просто находятся поверх него, а само изображение все еще присутствует, хоть его и не видно. Фотографий много, вы их можете перекладывать одну поверх другой, выбирая нравящиеся, тем самым вы неявно работаете со свойствами BringToFront конкретного TImage выводя его на передний план.
Если мы опять захотим нарисовать прямо сейчас - мы возьмем фломастер и сделаем рисунок, и нам не помешают расположенные на столе фотографии, мы просто проведем линию поверх них.
Но вот мы ставим на стол тарелку - она закрывает собой и стол, и фотографии. Это TWinControl. Возьмите фломастер и попробуйте нарисовать линию поверх стола так, чтобы она отобразилась еще и на тарелке, плавно продолжая рисунок с канвы формы  - тогда вы сможете понять как сложно это сделать программно :)


вторник, 26 февраля 2013 г.

Нюансы использования Ribbon от DevExpress

Версия 1.0.3 

Однако ребята из Редмонда не перестают удивлять нас своими наработками в рамках пользовательского интерфейса. То у них модными были тулбары и плоские кнопки, то тулбары в градиенте и разворачивающиеся меню, лента вот недавно появилась, а в последней версии офиса опять в моду входит концепция "плоских" элементов (я уж не говорю о METRO). Причем каждый раз с новым веянием Microsoft публикует большой манускрипт, описывающий почему именная текущая концепция самая правильная.

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

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

Их много.
Хороших, плохих - разных. Но я остановлюсь только на одной из них - это DevExpress, наши ребята с отделом разработки в Туле.

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

В статье я буду рассматривать "DevExpress VCL ExpressBars components" версии 12.1.4 и все описанное будет относится именно к данной версии. В более ранних версиях данного пакета некоторые элементы могут отсутствовать или работать не так как описано.

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

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

Комментарий от DevExpress Team:
Тело комментария
Ну что ж, приступим...

воскресенье, 27 января 2013 г.

Реализация перехвата вызовов API

Версия 1.0.1 

Монитор. Отличное слово, правда, объединяющее в себе целую кучу понятий. Ну например впервые данное слово было применено в 1861 году к броненосцу "USS Monitor". Чуть позже данным словом стали называть дисплеи. Еще через некоторое в славную когорту мониторов вошли более привычные нам вещи, наподобие счетчиков производительности, а вместе с ними целая куча разнообразного ПО, основной задачей которого является наблюдение - то есть мониторинг.

Задача монитора проста - по сути он наблюдатель, хотя может выступать и в качестве управляющего, кстати один из вариантов перевода "Monitor" - наставник :). Задачи его так же очевидны, отдавать набор данных, на основе которых можно произвести анализ ситуации и сделать соответствующие выводы.

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

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

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

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

В итоге, дабы не объяснять в следующий раз все заново, я решил сделать обзорную статью где попробую рассказать все максимально простым языком о том, "как это работает" :)