Искусственное усложнение структуры программы

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

• вызов функции через указатель на нее (косвенный вызов):

pFunction = Function;

а = pFunction (х, у, z);

Обычный вызов функции

а = Function (х, у, z);

транслируется в машинные команды:

push z

push у

push x

call Function

mov eax, a

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

push z

push у

push х

call [pFunction]

mov eax, a

Дизассемблеру, анализирующему данный фрагмент кода, обычно не удается определить, что указатель pFunction указывает именно на функцию Function. Если функция Function нигде не вызывается «естественным» образом, дизассемблер, возможно, будет интерпретировать ее команды как данные. Даже если дизассемблер сумеет понять, что тело функции Function содержит машинные команды, дизассемблеру, скорее всего, не удастся определить, что в команде call [pFunction] вызывается именно функция Function. Если же перед разработчиком защиты ставится задача организовать противодействие «особо умным» дизассемблерам, можно порекомендовать выполнить над указателем pFunction дополнительные операции, усложняющие код настолько, что его анализ становится совершенно не по силам программе, не обладающей искусственным интеллектом. Например:

(DWORD) pFunction = (DWORD) Function — 68;

(DWORD) pFunction += 68;

a = pFunction (x, y, z) ;

или

pFunctions[125] = Function;

a = pFunctions[0] (125, x, y, z);

• вызов функции посредством машинной команды ret (возврат из

функции):

push offset m

push Function

ret

m:

• вызов функции через обработчик исключительной ситуации:

__try

{ a = 1 / (a - a) ;

}

__except(TRUE)

{ a = Function (x, y, z) ;

}

При этом могут использоваться обработчики исключительных

ситуаций, специфичных для отладки программ:

_try

{ _asm int 3;

}

__except(TRUE)

{ a = Function (x, y, z) ;

}

• вызов функции в отдельном потоке:

s.х = х;

s.y = у;

s.z = z ;

hThread = CreateThread (NULL, 0, Function, &s, 0,

&dw) ;

WaitForSingleObject (hThread, TIMEOUT);

GetExitCodeThread (hThread, &a);

CloseHandle (hThread);

• вызов функции через пул потоков worker thread:

s.х = x;

s.y = у;

s.z = z;

s.ret = &a;

RtlQueueWorkltem (Function, &s, 0) ;

WaitForSingleObject (hEvent, TIMEOUT);

Функция Function перед возвратом управления должна сделать

системный вызов

SetEvent (hEvent);

• вызов функции через пул потоков wait thread:

s.х = х;

s.y = у;

s.z = z;

s.ret = &a;

RtlRegisterWait (ShWait, hEventl, Function, &s,

TIMEOUT1, 0);

SetEvent (hEventl);

WaitForSingleObject (hEvent2, TIMEOUT2);

Функция Function перед возвратом управления должна сделать системный вызов

SetEvent (hEvent2) ;

• вызов функции через передачу некоторому окну нестандартного

сообщения:

s.х = х;

s.y = у;

s.z = z;

а = SendMessage (hwnd, WM_USER + НIDDEN_CALLS_GATE,

Function, &s);

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

параметрах отправляемого вручную сообщения некую метку, которая не может встретиться в параметрах обычных сообщений Windows;

• вызов функции по таймеру (предполагается, что текущий поток

программы не создает никаких окон, g_s — глобальная структура):

g_s.х = х ;

g_s.у = у;

g_s.z = z ;

g_s.ret = &а;

n = SetTimer (NULL, 0, USER_TIMER_MINIMUM,

Function);

GetMessage (&msg, NULL, 0, 0) ;

DispatchMessage (&msg);

KillTimer (n) ;

• вызов функции через перечисление дочерних окон окна, содержащего

единственное дочернее окно:

s.х = х ;

s.y = у;

s.z = z ;

s.ret = &а;

EnumChildWindows (hwndWithSingleChild, Function,

&s) ;

• вызов функции через перечисление главных окон программы,

имеющей единственное главное окно:

s.х = х ;

s.y = у;

s.z = z;

s.ret = &а;

EnumThreadWindows (GetCurrentThreadld (), Function,

&s) ;

• вызов функции через перечисление файлов подкачки (pagefiles) системы, имеющей единственный файл подкачки (работает начиная с Windows 2000):

s.х = х ;

s.y = у;

s.z = z;

s.ret = &а;

EnumPageFiles (Function, &s);

• вызов функции через асинхронный ввод-вывод:

s.х = х ;

s.y = у;

s.z = z ;

s.ret = &а;

memset (&о, 0, sizeof(OVERLAPPED));

о. hEvent = &s;

ReadFileEx (hFile, p, 0, &o, Function);

Здесь hFile — любой файловый объект, открытый в асинхронном режиме; р — произвольный адрес памяти, доступный для записи. Вместо ReadFileEx можно использовать функции WriteFileEx, NtReadFile, NtWriteFile, NtDeviceloControlFile;

• использование нестандартных способов сравнения данных. Например, вместо стандартных машинных команд сравнения (cmp, test) можно использовать арифметические и логические команды (add, sub, and, or и т.д.). Как модификацию рассматриваемого метода можно рассматривать метод, заключающийся в том, что защищаемая программа транслируется не в стандартный машинный код процессора, а в некий промежуточный код, который в дальнейшем интерпретируется специальным

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

Наши рекомендации