Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.03.20;
Скачать: CL | DM;

Вниз

Любителям "скырть процесс" под NT посвящается...   Найти похожие ветки 

 
Игорь Шевченко ©   (2005-02-02 10:21) [0]

Небольшой код, позволяющий определить, есть ли в системе потенциально вредоносный процесс, скрывающий себя от Task Manager"а, Process Explorer"а и прочих просмотрщиков списка процессов. В сочетании с включенным аудитом процессов поможет выявить поделки доморощенных пакостников.
Проверено на Windows 2000 и Windows XP.

unit CheckNtDll;
interface
uses
 Windows, HsNtDef;

function RealQueryListInformation (InfoClass: Integer; var rc: NTSTATUS;
 var ReturnLength: DWORD): Pointer;

function IsNtDllHooked: Boolean;

implementation
uses
 SysUtils, NtDll, PeImage, NtStatusDefs;

{$IFDEF VER140}
{$WARN SYMBOL_PLATFORM OFF}
{$ENDIF}

type
 TSysCallData = array[0..13] of Byte;
 TNtQuerySystemInformation = function (SystemInformationClass: LongInt;
   SystemInformation: Pointer; SystemInformationLength: ULONG;
   ReturnLength: PDWORD): NTSTATUS; stdcall;

var
 FRealQuerySystemInformation: TNtQuerySystemInformation;

function RealQueryListInformation (InfoClass: Integer; var rc: NTSTATUS;
 var ReturnLength: DWORD): Pointer;
var
 ListSize: Integer;
begin
 ListSize := $400; { Начальный размер буфера }
 GetMem(Result, ListSize);
 rc := FRealQuerySystemInformation(InfoClass, Result, ListSize, @ReturnLength);
 while rc = STATUS_INFO_LENGTH_MISMATCH do begin
   FreeMem(Result);
   ListSize := ListSize * 2;
   GetMem(Result, ListSize);
   rc := FRealQuerySystemInformation(InfoClass, Result, ListSize,
     @ReturnLength);
 end;
 if rc <> STATUS_SUCCESS then begin
   FreeMem(Result);
   Result := nil;
 end;
end;

procedure MakeRealQS (const SysCallData: TSysCallData);
var
 OldProtection: DWORD;
 FuncPtr: Pointer;
begin
 FuncPtr := VirtualAlloc(nil, $1000, MEM_COMMIT, PAGE_READWRITE);
 Win32Check(Assigned(FuncPtr));
 Move(SysCallData, FuncPtr^, SizeOf(SysCallData));
 Win32Check(VirtualProtect(FuncPtr, SizeOf(SysCallData), PAGE_EXECUTE,
   OldProtection));
 @FRealQuerySystemInformation := FuncPtr;
end;

function IsNtDllCodeHooked (const FileName: string; ProcOffset: ULONG): Boolean;
var
 Image: THSPeImage;
 SysCall: TSysCallData;
 SysCall2: TSysCallData;
 QSPtr: Pointer;
begin
 Image := THSPEImage.Create (FileName);
 try
   Move(Image.RVAData[ProcOffset]^, SysCall, SizeOf(SysCall));
 finally
   Image.Free;
 end;
 QSPtr := GetProcAddress(GetModuleHandle("ntdll.dll"),
   "NtQuerySystemInformation");
 Move(QSPtr^, SysCall2, SizeOf(SysCall2));
 Result := not CompareMem(@SysCall, @SysCall2, SizeOf(SysCall));
 if Result then
   MakeRealQS (SysCall);
end;

function IsNtDllHooked: Boolean;
const
 SDllPath = "%SystemRoot%\system32\ntdll.dll";
var
 DllPath: ZString;
 HFile, HMap: THandle;
 Map: Pointer;
 Headers: PImageNtHeaders;
 QSysInfo: Pointer;
begin
 Result := false;
 Win32Check(
   ExpandEnvironmentStringsA(SDllPath, DllPath, SizeOf(DllPath)) <> 0);
 HFile := CreateFileA(DllPath, GENERIC_READ, FILE_SHARE_READ, nil,
   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
 Win32Check(HFile <> INVALID_HANDLE_VALUE);
 try
   HMap := CreateFileMappingA(HFile, nil, PAGE_READONLY, 0, 0, nil);
   Win32Check(HMap <> 0);
   try
     Map := MapViewOfFile(HMap, FILE_MAP_READ, 0, 0, 0);
     Win32Check(Assigned(Map));
     try
       Headers := RtlImageNtHeader(HMODULE(Map));
       if Assigned(Headers) then
         Result :=
           Headers.OptionalHeader.ImageBase <> GetModuleHandle("ntdll.dll");
         if not Result then begin
           QSysInfo := GetProcAddress(GetModuleHandle("ntdll.dll"),
             "NtQuerySystemInformation");
           if Assigned(QSysInfo) then begin
             Result := (Cardinal(QSysInfo) <=
               Headers.OptionalHeader.ImageBase) or (Cardinal(QSysInfo) >
               Headers.OptionalHeader.ImageBase +
               Headers.OptionalHeader.SizeOfCode);
             if not Result then
               Result := IsNtDllCodeHooked(DllPath, ULONG(QSysInfo) -
                 Headers.OptionalHeader.ImageBase);
           end else
             Result := true;
         end;
     finally
       UnmapViewOfFile(Map);
     end;
   finally
     CloseHandle(HMap);
   end;
 finally
   CloseHandle(HFile);
 end;
end;

initialization
finalization
 if Assigned(FRealQuerySystemInformation) then
   VirtualFree(@FRealQuerySystemInformation, $1000, MEM_DECOMMIT);
end.


Пример использования:

 
   FQueryInformationFunction: TQueryListInformation;
.........
 if IsNtDllHooked then begin
   ShowMessage("NtDll is hooked!");
   @FQueryInformationFunction := @RealQueryListInformation;
 end else
   @FQueryInformationFunction := @QueryListInformation;

.....

var
 ProcessInfo: Pointer;
 rc: NTSTATUS;
 Dummy: ULONG;
begin
 ProcessInfo := FQueryInformationFunction (
   SystemProcessesAndThreadsInformation, rc, Dummy);
 if NT_SUCCESS(rc) then
   ShowProcessList (ProcessInfo)
 else
   RaiseNtError (rc);
.........


 
BiN ©   (2005-02-02 10:25) [1]

Игорь, приветствую!

Ты наверняка сам знаешь, что перехват можно осуществлять не только правкой IAT.

С уважением.


 
BiN ©   (2005-02-02 10:30) [2]

Или я чего-то недопонимаю в коде(?)....


 
kaZaNoVa ©   (2005-02-02 10:37) [3]

BiN ©   (02.02.05 10:25) [1]
имхо обычно ориентируемся на самые часто-испрользуемые способы перехвата и их отлавливаем .))

//ща тестирую, результаты сообщу))


 
Игорь Шевченко ©   (2005-02-02 10:40) [4]

BiN ©   (02.02.05 10:25) [1]

Привет! Данный код как раз служит для вытаскивания за ушко и на солнышко процессов, которые пользуются методом внедрения без правки IAT, а установкой в адресном пространстве "жертвы" кода перехода на свою скрывающую функцию по корректному адресу в NTDLL.DLL

Краткий алгоритм такой: Проверяется соответствие как правленной IAT, так и совпадения кода по адресу NtQuerySystemInformation в загруженной DLL и на диске. Если код не совпадает, то для вызова функции используется тот код, который в файле NTDLL.DLL на диске находится.

С уважением,


 
BiN ©   (2005-02-02 10:43) [5]

Игорь Шевченко ©   (02.02.05 10:40) [4]

Проверяется соответствие как правленной IAT, так и совпадения кода по адресу NtQuerySystemInformation в загруженной DLL и на диске

(выделение мое)

Точно, не обратил внимание на эту строку:

Image := THSPEImage.Create (FileName);


в функции IsNtDllCodeHooked


 
BiN ©   (2005-02-02 10:46) [6]

Правда, тут могут начаться "гонки" с перехватом NtCreateFile и т.п., но это уже из другой оперы...


 
Игорь Шевченко ©   (2005-02-02 10:57) [7]

BiN ©   (02.02.05 10:46) [6]

А зачем перехватывать NtCreateFile ? Я, честно говоря, смысла не вижу.

С уважением,


 
kaZaNoVa ©   (2005-02-02 11:04) [8]

BiN ©   (02.02.05 10:46) [6]
всё равно всё не перехватят))
95% имхо всех "перехватчиков" останавливаются на том, что не видны в таск-манагере и всё)))


 
kaZaNoVa ©   (2005-02-02 11:09) [9]

ура, пример работает!!!
//проверял пока только одну функцию - детектор перехвата IsNtDllHooked- потрясающе))


 
BiN ©   (2005-02-02 11:09) [10]

Игорь Шевченко ©   (02.02.05 10:57) [7]

А зачем перехватывать NtCreateFile ? Я, честно говоря, смысла не вижу.


А с помощью каких функций считывается PE-заголовок ntdll.dll?


 
Игорь Шевченко ©   (2005-02-02 11:12) [11]

BiN ©   (02.02.05 11:09) [10]

Разумеется, NtCreateFile + NtCreateSection. Но дело в том, что если перехватить, то как тогда остальные будут работать ? Ведь любая программа отображает NTDLL на свое адресное пространство.

С уважением,


 
BiN ©   (2005-02-02 11:13) [12]

вдогонку к
BiN ©   (02.02.05 10:46) [6]

Правда, тут могут начаться "гонки" с перехватом NtCreateFile и т.п., но это уже из другой оперы...


Имеется ввиду также перехват остальных функций I/O файловой системы.


 
kaZaNoVa ©   (2005-02-02 11:13) [13]

kaZaNoVa ©   (02.02.05 11:09) [9]
но подмену IAT кажется не определяет . .(или я ошибаюсь?) - другай пример, который только скрывается от такс-менеджера,но виден Процесс-Эксплореру - тот не определятся как IsNtDllHooked ...


 
Kerk ©   (2005-02-02 11:16) [14]

Игорь Шевченко ©   (02.02.05 11:12) [11]

так аккуратненько так... проверять какой файл пытаются открыть и все такое :)


 
BiN ©   (2005-02-02 11:17) [15]

Игорь Шевченко ©   (02.02.05 11:12) [11]

Но дело в том, что если перехватить, то как тогда остальные будут работать ? Ведь любая программа отображает NTDLL на свое адресное пространство


А разве правка кода будет происходить не в адресном пространстве одного процесса? Не понятно, причем здесь дугие.

С уважением,


 
kaZaNoVa ©   (2005-02-02 11:17) [16]

Kerk ©   (02.02.05 11:16) [14]
получится бесконечная "гонка вооружений" ))


 
Kerk ©   (2005-02-02 11:19) [17]

Kerk ©   (02.02.05 11:16) [14]
причем здесь дугие.


а как ты узнаешь в каком именно процессе код править? или будешь специально под какую-то тулзу затачиваться?


 
GMan   (2005-02-02 11:52) [18]

2 Игорь Шевченко:

Что такое HsNtDef?


 
Игорь Шевченко ©   (2005-02-02 12:01) [19]

Всем:

Данный код иллюстирует только идею. Если все детали выкладывать, места не хватит. Остальные используемые модули представляют кучу служебных полезностей, написанных в разное время.
Часть можно взять по адресу
http://www.schevchenko.net.ru/SRC/Common_60.zip

BiN ©   (02.02.05 11:17) [15]

Если перехватывать  NtCreateFile, то с какой целью ? Не дать открыть NtDll ? Так это вся система сломается.

С уважением,


 
Kerk ©   (2005-02-02 12:02) [20]

Игорь Шевченко ©   (02.02.05 12:01) [19]
Если перехватывать  NtCreateFile, то с какой целью ? Не дать открыть NtDll ? Так это вся система сломается.


подменить


 
Игорь Шевченко ©   (2005-02-02 12:08) [21]

Kerk ©   (02.02.05 12:02) [20]

Не совсем понял, чем подменить. И главное, с какой целью. NTDLL является первой DLL, отображаемой на адресное пространство процесса.

С уважением,


 
BiN ©   (2005-02-02 12:10) [22]

Игорь Шевченко ©   (02.02.05 12:01) [19]

Если перехватывать  NtCreateFile, то с какой целью ? Не дать открыть NtDll ? Так это вся система сломается.


Да, согласен. Я имел ввиду, что можно организовать подмену считываемых с диска данных - тех самых первых 14 байт перехваченной функции. Хоть это и финт ушами.

С уважением,


 
Игорь Шевченко ©   (2005-02-02 12:18) [23]

BiN ©   (02.02.05 12:10) [22]

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

С уважением,


 
BiN ©   (2005-02-02 12:23) [24]

Игорь Шевченко ©   (02.02.05 12:18) [23]

Если я правильно понял, ты предлагаешь хранить "слепки" с заголовков оригинальных функций для каждой версии(?).

С уважением,


 
kaZaNoVa ©   (2005-02-02 12:32) [25]

BiN ©   (02.02.05 12:23) [24]

> "слепки" с заголовков оригинальных функций для каждой
> версии(?).

и будет как антивирус, с "белым списком" )))


 
Игорь Шевченко ©   (2005-02-02 12:40) [26]

BiN ©   (02.02.05 12:23) [24]

Ни в коем разе. Это совершенно не переносимый вариант будет. Я основываюсь на том, что системные вызовы представляют собой код по вполне определенному шаблону, и он не меняется, так как изменение не имеет смысла. Меняется только номер вызова. Вот его я получаю чтением файла.

С уважением,


 
BiN ©   (2005-02-02 12:48) [27]

Игорь Шевченко ©   (02.02.05 12:40) [26]

Понятно. Также стало ясно почему именно 14 байт :).

С уважением,


 
Piter ©   (2005-02-02 15:54) [28]

Игорь Шевченко ©   (02.02.05 10:21)
В сочетании с включенным аудитом процессов


а что это такое, где находится и зачем нужно?


 
Игорь Шевченко ©   (2005-02-02 16:12) [29]

Piter ©   (02.02.05 15:54) [28]

Локальная политика безопасности + F1


 
Piter ©   (2005-02-02 16:45) [30]

И еще, я вот посмотрел на код, компилятора под рукой нету, но на взгляд кое что не понял:

- в RealQueryListInformation выделяется память, на которую указывает Result. А кто эту память будет зачищать?

В примере использования написано:

end else
  @FQueryInformationFunction := @QueryListInformation;


а что такое QueryListInformation?

Ну и:

FreeMem(Result);
  ListSize := ListSize * 2;
  GetMem(Result, ListSize);


можно использовать ReallocMem, будет нагляднее :)


 
Игорь Шевченко ©   (2005-02-02 16:53) [31]


> - в RealQueryListInformation выделяется память, на которую
> указывает Result. А кто эту память будет зачищать?


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

> а что такое QueryListInformation?


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


 
kaZaNoVa ©   (2005-02-02 16:57) [32]

Piter ©   (02.02.05 16:45) [30]

> А кто эту память будет зачищать?

Игорь Шевченко ©   (02.02.05 16:53) [31]

> В данном случае - никто. Но никто не мешает
> реализовать освобождение памяти в своем примере,
> равно, как и ReallocMem.


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


 
Piter ©   (2005-02-02 17:04) [33]

Игорь Шевченко ©   (02.02.05 16:12) [29]
Локальная политика безопасности + F1


Открыл справку, посмотрел. Вот что у меня:

Администрирование

Службы компонентов

Управление компьютером

Источники данных (ODBC)

Окно просмотра событий

Локальная политика безопасности

Системный монитор

Службы


Нигде про аудит процессов не нашел :(

Хочу оговориться, что у меня w2k.

В политиках аудита нашел "Аудит отслеживания процессов". Но что это такое и что будет,  если включить - не понял.


 
Игорь Шевченко ©   (2005-02-02 17:06) [34]


> Но что это такое и что будет,  если включить - не понял.


F1


 
SammIk ©   (2005-02-02 21:20) [35]

Респект Игорь Шевченко ©.
Все круто, но есть одно но.
Ежели будет стоять хук на NtCreateFileEx, то соответственно
можно будет отловить обращение к хукнутым библиям и вернуть
то, что это детектор хочет.
Есть другой вариант.
Конкретно для kernel32
Через SEH получить базу образа, там в импортах найти куда идти дальше.
Если в импорт ссылается на адрес вне (imgbase,imgbase+imgsiz)
то делаем вывод, что нас прохучили по импорту.
Ежели все в порядке, то проверяем на предмет джампов саму ф-ю
по тойже схеме.
------
Хотя и такой пример работает)
------
Способ для извращенцев, но действенный.
Открывать винт на чтение, искать нужный фаил, и далее как
Игорь делать


 
Игорь Шевченко ©   (2005-02-03 10:43) [36]

SammIk ©   (02.02.05 21:20) [35]

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

С уважением,


 
SammIk ©   (2005-02-03 13:43) [37]

С тавится хук на чтение и открытие фаила.
Если открыли то, что не надо открывать. Можно вернуть
код ошибки, мол нет доступа или еще что.
Если читают то, что не надо, то возвращаем не верную инфу.
И чего тут невозможного?


 
Игорь Шевченко ©   (2005-02-03 14:12) [38]

SammIk ©   (03.02.05 13:43) [37]

Так система тоже читает. И для того, чтобы она работала, необходимо, чтобы читалось то, что запрошено и данные должны быть верными.


 
Piter ©   (2005-02-03 15:33) [39]

Игорь Шевченко ©   (03.02.05 14:12) [38]
Так система тоже читает


А она читает тоже через NtCreateFile? Или через какие-то свои внутренние функции?


 
Игорь Шевченко ©   (2005-02-03 16:26) [40]

Piter ©   (03.02.05 15:33) [39]

Все замыкается на NtCreateFile - это единственная дырка. И не читает, а получает доступ к файлу.

С уважением,



Страницы: 1 2 вся ветка

Текущий архив: 2005.03.20;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.059 c
1-1110113557
smok_red
2005-03-06 15:52
2005.03.20
Список окон в MDI приложении в виде кнопок на ToolBar


1-1109844534
leonidus
2005-03-03 13:08
2005.03.20
Toolbar как в Word`е XP


6-1106013209
Cryon
2005-01-18 04:53
2005.03.20
Определить IP клиента pop3 сервера (Indy)


3-1108849037
table refrash
2005-02-20 00:37
2005.03.20
Как обновить данные в таблице в IB


1-1109921070
Оля
2005-03-04 10:24
2005.03.20
Чтение строки из TMemorystream