Сейчас на форуме: Magister Yoda, johnniewalker, Kybyx, vsv1, r0lka, -Sanchez-, testrev1337 (+3 невидимых)

 eXeL@B —› Крэки, обсуждения —› Кривые хуки системных библиотек
Посл.ответ Сообщение

Ранг: 500.5 (!), 8thx
Активность: 0.230
Статус: Участник

Создано: 25 февраля 2011 22:30 · Поправил: Smon
· Личное сообщение · #1

Всем доброго времени суток!
Неоднократно встречал не особо умные хуки на системных функциях из kernel32, user32, advapi и т.п.
Это когда хук ставится вписыванием long jmp (E9h) в начало функции, что зачастую бывает критично для совместимости, подобные были например в более ранних версиях KIS и KAV (до 8ки) или в небезызвестном CryptoPro CSP. Адекватные хуки пишутся например в таблицу экспорта библиотеки, через дрова, ну и прочими вариациями.
Вопрос - есть ли надёжный и простой способ определения, захучена ли вообще заданная системная библиотека таким костыльным способом или нет? Есть конечно вариант проверять начала всех функций из экспорта (вплоть до дизасма нескольких инструкций) и проверка их на переходы за пределы библиотеки, но это долго и не совсем ясно с реализацией и надёжностью метода - ведь системные библы имеют и форварды друг в друга. Да и медленно это очень. Неужели rku юзает именно такой способ ?
PS: вариант мапить "вручную" системные библиотеки в процессе, обрабатывать релоки, и сравнивать crc двух аналогичных секций кода конечно есть, но опять таки он довольно муторен в реализации, хотя похоже и понадежней чем дизасм.

-----
"Пусть видят, что мы не шутим. Стволы для понта, ножи для дела" Lock, Stock & Two Smoking Barrels


| Сообщение посчитали полезным: mikus

Ранг: 255.8 (наставник), 19thx
Активность: 0.150.01
Статус: Участник
vx

Создано: 25 февраля 2011 22:59
· Личное сообщение · #2

Описать код графом и сравнить его с оригинальным. Иначе никак.

| Сообщение посчитали полезным: mikus

Ранг: 237.0 (наставник), 20thx
Активность: 0.130
Статус: Участник
sysenter

Создано: 25 февраля 2011 23:08
· Личное сообщение · #3

Что касается паблик RkU, то он очевидно сравнивает 1-е 5 байт с оригиналом, т.к. если поставить джамп дальше он его не увидит.

-----
продавец резиновых утёнков


| Сообщение посчитали полезным: mikus


Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 26 февраля 2011 08:13
· Личное сообщение · #4

ИМХО только мапить, настраивать релоки и сравнивать первые N байт начала функций.

-----
Yann Tiersen best and do not fuck


| Сообщение посчитали полезным: mikus

Ранг: 617.3 (!), 677thx
Активность: 0.540
Статус: Участник

Создано: 26 февраля 2011 10:49
· Личное сообщение · #5

Можно еще проверять Checksum, мало кто ее правит после патча.




Ранг: 2014.5 (!!!!), 1278thx
Активность: 1.340.25
Статус: Модератор
retired

Создано: 26 февраля 2011 11:30
· Личное сообщение · #6

Чексам загруженного образа в любом случае не сойдётся, хотя бы из-за релоков.



Ранг: 419.0 (мудрец), 647thx
Активность: 0.460.51
Статус: Участник
"Тибериумный реверсинг"

Создано: 26 февраля 2011 11:39 · Поправил: ELF_7719116
· Личное сообщение · #7

long jmp (E9h) в начало функции, что зачастую бывает критично для совместимости
Уточнение: В подавляющем случае он ложится на MOV EDI, EDI + открытые кадра стека. Дальше редко кто лезет, чтобы не дисасемблить. На этом можно сыграть.
проверять checksum - может неплохая идея(связать ее секцией кода).




Ранг: 568.2 (!), 464thx
Активность: 0.550.57
Статус: Участник
оптимист

Создано: 26 февраля 2011 11:56
· Личное сообщение · #8

Smon пишет:
Адекватные хуки пишутся например в таблицу экспорта библиотеки

И высчитываются легко тк обычно указывают за пределы секции кода бибблиотеки,поэтому умный хук ставится по опосля 3-4 комманд ;)

-----
Чтобы правильно задать вопрос, нужно знать большую часть ответа. Р.Шекли.




Ранг: 500.5 (!), 8thx
Активность: 0.230
Статус: Участник

Создано: 26 февраля 2011 12:15
· Личное сообщение · #9

ClockMan пишет:
И высчитываются легко тк обычно указывают за пределы секции кода бибблиотеки,поэтому умный хук ставится по опосля 3-4 комманд ;)

Я говорил о подмене адреса в экспорте, а опосля 3-4 команд это те же костыли и грабли что и непосредственно в самое начало.

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

-----
"Пусть видят, что мы не шутим. Стволы для понта, ножи для дела" Lock, Stock & Two Smoking Barrels




Ранг: 369.8 (мудрец), 400thx
Активность: 0.390
Статус: Участник

Создано: 26 февраля 2011 12:43
· Личное сообщение · #10

Smon пишет:
Неоднократно встречал не особо умные хуки на системных функциях из kernel32, user32, advapi и т.п.Это когда хук ставится вписыванием long jmp (E9h) в начало функции

Не особо умные - это те разработчики, что ставят такие хуки не обрабатывая rel32 команды и еще те, которые эти хуки пытаются убрать не проверив не похукано ли сверху. Отсюда и проблемы совместимости.
На 32х битных виндах начиная с XP SP1 предусмотрен специальный механизм хотпатчей для постановки безопасных хуков сплайсом, для этого заменяем mov edi, edi на jmp $-5, и в нопах находящихся перед функцией прописываем long jmp куда надо.

Smon пишет:
Адекватные хуки пишутся например в таблицу экспорта библиотеки, через дрова, ну и прочими вариациями.

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

Smon пишет:
Вопрос - есть ли надёжный и простой способ определения, захучена ли вообще заданная системная библиотека таким костыльным способом или нет? Есть конечно вариант проверять начала всех функций из экспорта (вплоть до дизасма нескольких инструкций) и проверка их на переходы за пределы библиотеки, но это долго и не совсем ясно с реализацией и надёжностью метода

Реализация тривиальна: идем по одной инструкции вниз до ret или rel32 команды, переходим по jmp rel8, не забываем ограничить число проверяемых команд чтоб не попасть в бесконечный цикл. Кода то тьфу.

Smon пишет:
Похоже вариант с мапом, обработкой релоков и последующим сравнением всё же наиболее правильный и надёжный.

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

-----
PGP key <0x1B6A24550F33E44A>




Ранг: 255.8 (наставник), 19thx
Активность: 0.150.01
Статус: Участник
vx

Создано: 26 февраля 2011 12:49
· Личное сообщение · #11

Smon
> как раз из за релоков не подходит такой вариант
Всё подходит, просто нужно дельту пересчитать. Смотрим:
Code:
  1. ULONG
  2. LdrRelocateImage (
  3.     IN PVOID NewBase,
  4.     IN PUCHAR LoaderName,
  5.     IN ULONG Success,
  6.     IN ULONG Conflict,
  7.     IN ULONG Invalid
  8.     )
  9.  
  10. /*++
  11.  
  12. Routine Description:
  13.  
  14.     This routine relocates an image file that was not loaded into memory
  15.     at the preferred address.
  16.  
  17. Arguments:
  18.  
  19.     NewBase - Supplies a pointer to the image base.
  20.  
  21.     LoaderName - Indicates which loader routine is being called from.
  22.  
  23.     Success - Value to return if relocation successful.
  24.  
  25.     Conflict - Value to return if can't relocate.
  26.  
  27.     Invalid - Value to return if relocations are invalid.
  28.  
  29. Return Value:
  30.  
  31.     Success if image is relocated.
  32.     Conflict if image can't be relocated.
  33.     Invalid if image contains invalid fixups.
  34.  
  35. --*/
  36.  
  37. {
  38.     LONG_PTR Diff;
  39.     ULONG TotalCountBytes;
  40.     ULONG_PTR VA;
  41.     ULONG_PTR OldBase;
  42.     ULONG SizeOfBlock;
  43.     PUCHAR FixupVA;
  44.     USHORT Offset;
  45.     PUSHORT NextOffset;
  46.     PIMAGE_NT_HEADERS NtHeaders;
  47.     PIMAGE_BASE_RELOCATION NextBlock;
  48.  
  49.     RTL_PAGED_CODE();
  50.  
  51.     NtHeaders = RtlImageNtHeader( NewBase );
  52.     if ( NtHeaders ) {
  53.         OldBase = NtHeaders->OptionalHeader.ImageBase;
  54.         }
  55.     else {
  56.         return Invalid;
  57.         }
  58.  
  59.     //
  60.     // Locate the relocation section.
  61.     //
  62.  
  63.     NextBlock = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData(
  64.             NewBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &TotalCountBytes);
  65.  
  66.     if (!NextBlock || !TotalCountBytes) {
  67.  
  68.         //
  69.         // The image does not contain a relocation table, and therefore
  70.         // cannot be relocated.
  71.         //
  72. #if DBG
  73.         DbgPrint("%s: Image can't be relocated, no fixup information.\n", LoaderName);
  74. #endif // DBG
  75.         return Conflict;
  76.     }
  77.  
  78.     //
  79.     // If the image has a relocation table, then apply the specified fixup
  80.     // information to the image.
  81.     //
  82.  
  83.     while (TotalCountBytes) {
  84.         SizeOfBlock = NextBlock->SizeOfBlock;
  85.         TotalCountBytes -= SizeOfBlock;
  86.         SizeOfBlock -= sizeof(IMAGE_BASE_RELOCATION);
  87.         SizeOfBlock /= sizeof(USHORT);
  88.         NextOffset = (PUSHORT)((PCHAR)NextBlock + sizeof(IMAGE_BASE_RELOCATION));
  89.  
  90.         VA = (ULONG_PTR)NewBase + NextBlock->VirtualAddress;
  91.         Diff = (PCHAR)NewBase - (PCHAR)OldBase;
  92.  
  93.         if ( !(NextBlock = LdrProcessRelocationBlock(VA,SizeOfBlock,NextOffset,Diff)) ) {
  94. #if DBG
  95.             DbgPrint("%s: Unknown base relocation type\n", LoaderName);
  96. #endif
  97.             return Invalid;
  98.         }
  99.     }
  100.  
  101.     return Success;
  102. }

Тоесть смещение для поправки вычисляется на основе базы из хидера, ну это понятно. Значит на время вызова этой функции туда нужно закрузить необходимое значение. Функа не экспортируется, но легко находится по двум инструкция [push 0xC000007B] - [push 0xC0000018] передающим статусы. Тоесть фиксим имя уже загруженного модуля в загрузчике, загружаем его есчо раз через LdrLoadDll(), при этом перехватываем LdrRelocateImage() например установив на неё брейк или протрассировав загрузчик.




Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 26 февраля 2011 13:11
· Личное сообщение · #12

Clerk Зачем такие извращения, если можно свой код нативный написать. Отмапить секции через CreateFileMapping с флагом SEC_IMAGE и пересчитать релоки на нужную базу, там кода то всего ничего.

-----
Yann Tiersen best and do not fuck




Ранг: 255.8 (наставник), 19thx
Активность: 0.150.01
Статус: Участник
vx

Создано: 26 февраля 2011 13:12
· Личное сообщение · #13

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



Ранг: 255.8 (наставник), 19thx
Активность: 0.150.01
Статус: Участник
vx

Создано: 26 февраля 2011 13:17
· Личное сообщение · #14

PE_Kill
Зачем его писать, если он уже есть непосредственно готовый бинарный



Ранг: 369.8 (мудрец), 400thx
Активность: 0.390
Статус: Участник

Создано: 26 февраля 2011 13:31
· Личное сообщение · #15

Clerk пишет:
Зачем его писать, если он уже есть непосредственно готовый бинарный

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

-----
PGP key <0x1B6A24550F33E44A>




Ранг: 255.8 (наставник), 19thx
Активность: 0.150.01
Статус: Участник
vx

Создано: 26 февраля 2011 13:48
· Личное сообщение · #16

ntldr
> Поиск неэкспортируемых функций по сигнатурам это мега-ахтунг
Мегатупость это писать загрузчики, делая всё вручную. И искать не нужно ничего, если используется трассировка загрузчика, то для каждого процедурного ветвления проверять в стеке строку (00 00 00 00 18 00 00 C0 7B 00 00 C0). Тоесть псевдокод более чем прост:
Code:
  1. if OPCODE(CONTEXT.rEip) = OPCODE(Call near ptr)
  2.    if [CONTEXT.rEsp] = 0xC000007BC000001800000000
  3.       OPTIONAL_HEADER.ImageBase = Delta
  4.    fi
  5. fi

> Я вот даже недокументированные в MSDN функции стараюсь использовать лишь по большой нужде, очень способствует отсутствию проблем.
Вручную работать с пе-форматом это есчо большее зло, так как он может меняться.



Ранг: 369.8 (мудрец), 400thx
Активность: 0.390
Статус: Участник

Создано: 26 февраля 2011 14:02
· Личное сообщение · #17

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

-----
PGP key <0x1B6A24550F33E44A>




Ранг: 500.5 (!), 8thx
Активность: 0.230
Статус: Участник

Создано: 26 февраля 2011 15:38
· Личное сообщение · #18

Всё ясно, спасибо всем за участие, закрываю тему

-----
"Пусть видят, что мы не шутим. Стволы для понта, ножи для дела" Lock, Stock & Two Smoking Barrels



 eXeL@B —› Крэки, обсуждения —› Кривые хуки системных библиотек
Эта тема закрыта. Ответы больше не принимаются.
   Для печати Для печати