пятница, 14 февраля 2014 г.

Есть ли жизнь после триала?

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

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

А это, как правило, сделать довольно просто.
Есть такая старая поговорка: если что-то запустилось и работает, можно запустить и заставить работать неограниченное количество раз :)

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

понедельник, 20 января 2014 г.

Здравствуйте, я ошибка 217 и я вам ничего не скажу

Вероятно многие встречались с таким вот "партизаном" при старте или завершении приложения:


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

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

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


понедельник, 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.

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