Пытаюсь реализовать перехват функций Native API в usermode. Существующие методы не нравятся тем, что для внедрения своей DLL в чужой процесс надо этот самый процесс открывать и писать в него, что ловится уже многими антивирусами и файрволлами. Решил попробовать пропатчить KERNEL32.DLL на диске, чтобы он грузил еще и мою DLL в память процесса. Известно, что KERNEL32 грузится в память любого процесса, так что при таком подходе можно реализовать перехват нужных функций во всех без исключения процессах, включая системные. Патчил в KERNEL32 функцию DLLMain, в самом ее конце, где происходит возврат из процедуры: pop esi pop ebx <-- cюда запихал call на свой код в конце секции text. размер text cоответственно увеличил. leave ret 000C cтало: pop esi call mycode моя процедура имеет следующий вид: Код (Text): mycode: pushad ;;;сохраняем регистры push 000000"l" ;;;суем в стек имя моей DLL push "dl.x" ;;;xxxxx.dll push "xxxx" push esp ;;;указатель на имя call LoadLibraryA ;;;грузим DLL add esp, 0000000C ;;;восстанавливаем стек mov ebx,[esp+20] ;;;берем из стека адрес инструкции после моего внедренного call sub ebx, 00000005 ;;;ebx указывает на мой внедренный call push 00000000 ;;;cуем в стек 0 push esp ;;;указатель на этот 0 (по нему функция VirtualProtect запишет старый режим доступа) push 00000040 ;;;режим доступа (40 - читать писать исполнять) push 00000005 ;;;длина - 5 байт (длина внедренной инструкции call mycode) push ebx ;;;адрес call mycode call VirtualProtect ;;;изменяем режим доступа pop eax ;;;вытаскиваем из стека лишнее значение mov dword ptr [ebx],XXXX ;;;восстанавливаем старые инструкции на месте моего call mycode mov byte ptr [ebx+4], xx ;;;---//-- popad ;;;восстанавливаем регистры sub dword ptr [esp], 00000005 ;;;вычитаем из адреса возврата 5, чтобы переход произошел на только что восстановленные инструкции ret ;;;возвращаемся данная схема вроде бы работала без глюков пока тестил в XP. как только попробовал в 2К, некоторые приложения стали валиться при старте. думал может дело в моей кривой DLL, которая там что-то нарушает (писана на Delphi). убрал из вышеприведенного кода ее загрузку, оставив только восстановление оригинальных инструкций по адресу call mycode. не помогло, так и валится. не понимаю, в чем может быть дело. прочитал у микрософтов, что в DLLMain нельзя ставить вызовы LoadLibrary, попробовал патчить не DLLMain, а CreateFileW. так же в конец функции ставил call mycode и потом восстанавливал оригинальные инструкции и на них возвращался. DLL даже не грузил. вообще все заглючило при таком подходе, начал падать проводник, перестали запускаться некоторые приложения и тд. не пойму одного - где накосячил? видимо что-то я упускаю в такой схеме, но что никак не могу понять подскажите
А что с точки зрения антивирусов это уже противозаконно? А в DllMain кажется LoadLibrary нельзя вызывать, если я не путаю.
мне не хочется лишний раз пугать пользователя тем, что всякие левые антивирусы будут выкидывать свои окошки с предпуреждениями о том, что мой процесс произвел запись в чужой и теперь этому процессу отрублен сетевой доступ и тд и тп.. и бороться с этим мерзким софтом тоже не хочется ибо программа вполне обычная, не вирусная и не троянская
читай внимательнее там у меня написано, что LoadLibrary убирал и пробовал вообще другую функцию патчить, результат тот же...
Метод хренов тем, что при установке апдейтов/сервиспаков придет капут такому перехвату. И чем тебе HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs не нравиться? Прописал туда свою длл и она подгрузиться к всем процессам использующим user32
ничто не мешает следить из основной программы за кернелом и если его обновили, патчить по новой я даже автоматический патчер любой версии kernel32.dll написал на основе дизассемблера длин от z0mbie. wfp тоже побеждена, так что метод вполне неплох, как мне представляется. это слишком на виду + процессы не использующие user32 в пролете...
Насколько я понял, ты пишкшь трояна. Тогда зачем тебе нужна основная программа? Не проще ли все сделать в длл? Каким образом? Простым ее отключением, или реализован обход проверки одного конкретного файла? И вообще, если патчить, так лучше уж ntdll.dll, он точно есть во всех процессах. Правда писать тогда придется только на Native API. И вообще, трояна рекомендую реализовывать полностью в драйвере, без использования юзермодной части. Такие вещи как перехват апи и обход фаерволлов там гораздо проще делаются. Да и метод патча kernel32 будет палиться всеми антивирусами, что не есть хорошо.
нет, это не троян а некий системный софт. отключением wfp для любого конкретного файла на лету без ребута. юзаются недокументированные функции sfc api. вот этого как раз и не хочется мне. и этого тоже не очень хочется вариант с драйвером тоже прорабатывается, за основу взял и несколько изменил твой пример драйвера из твоей статьи о перехвате, вроде работает пока... но на C я хреновый писатель + разбираться с kernel mode поначалу несколько тяжко... но что-то мне подсказывает, что наверное все-таки придется писать драйвер..
В легальном софте (тем более коммерческом) такие приемы как патч системных файлов на диске вообще недопустимы. Если ты пишешь коммерческую программу (защиту или что-нибудь подобное), то делай драйвер, так как иначе все хуки можно будет легко обойти. К тому-же в драйвере все реализовать куда проще, чем в юзермоде. К тому-же для легального софта скрытность совсем не нужна, так что можно использовать AppInit_DLLs. Если ты пишешь вирусы-трояны, то для этого хуки в юзермоде иногда могут оказаться куда полезнее и незаметнее драйверов.
в конечном итоге наверное я так бы и пришел к драйверу, но только вот хочется дело с юзермодой до конца довести. зря что ли патчер кернела писал? к тому же в исследованиях о перехвате апи метод патчинга системных библиотек на диске упомянут, но практических реализаций я вроде еще пока не видел. вот хотелось сделать proof of concept некий. и вопрос к тебе: перехват в кернел моде как лучше и безгеморройнее делать? для коммерческого софта. скрытность перехвата никакая не нужна. писать в KeServiceDescriptorTable или патчить в памяти начало нужной функции? или может еще как?
Значит ждем статью посвященную этой теме. Если нк нужна скрытность, и необходима стабильная работа, то лучше KeServiceDescriptorTable ничего не придумать. Сплайсинг применять стоит только для перехвата неэкспортируемых функций ядра, а для экспортируемых - правка таблицы экспорта ядра (на ранних стадиях загрузки) либо правка таблиц импорта загруженных модулей. P.S. Номера функций в SDT лучше не забивать констнтами, а извлекать из начала Zw функций в ntdll.
для этого надо вышеупомянутый код до ума довести а то оно работать-то работает, но как-то нестабильно, зависит еще от версии виндов (2к и хр), глючит по страшному (не запускаются некоторые программы, у проводника в хр в произвольные моменты времени слетают темы и тд и тп). даже если из кода убрать LoadLibrary. по идее же тогда функция-пустышка получается, вызвалась, восстановила сплайсенные инструкции и на них вернулась.. но глючит. такое ощущения что просто забываю что-то сохранять... на ранних стадиях - это для того, чтобы перехватить нужную функцию по возможности первым, я правильно понимаю? а насчет правки таблиц импорта загруженных модулей - ты о каких модулях? юзермодных ехешниках и дллях? а где они там лежат? не то ли, что там в eax грузится?
Так как перехват идет в ядре, то о модулях ядра. Как раз то. Вот пример кода получамющего эти номера: Код (Text): PVOID GetInfoTable(ULONG ATableType) { ULONG mSize = 0x4000; PVOID mPtr = NULL; NTSTATUS St; do { mPtr = ExAllocatePool(PagedPool, mSize); memset(mPtr, 0, mSize); if (mPtr) { St = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL); } else return NULL; if (St == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(mPtr); mSize = mSize * 2; } } while (St == STATUS_INFO_LENGTH_MISMATCH); if (St == STATUS_SUCCESS) return mPtr; ExFreePool(mPtr); return NULL; } /* Получение указателя на IMAGE_NT_HEADERS PE файла. Возвращает NULL если PE файл невалиден. */ PVOID GetNtHeaders(PVOID hModule) { PIMAGE_DOS_HEADER dHdr = hModule; PIMAGE_NT_HEADERS nHeader; if (MmIsAddressValid(dHdr) && (dHdr->e_magic == IMAGE_DOS_SIGNATURE)) { nHeader = (PIMAGE_NT_HEADERS)((ULONG)dHdr + dHdr->e_lfanew); if (MmIsAddressValid(nHeader) && (nHeader->Signature == IMAGE_NT_SIGNATURE)) return nHeader; } return NULL; } /* Получение адреса системного модуля. */ PVOID GetModuleHandle(PCSTR ModuleName) { ULONG r, s; PVOID Result = NULL; PSYSTEM_MODULE_INFORMATION_EX Info = GetInfoTable(SystemModuleInformation); PCHAR ShortName; if (!Info) return NULL; for (r = 0; r < Info->ModulesCount; r++) { for (s = 255; (Info->Modules[r].ImageName[s] != '\\') && s; s--); if (!strcmp((PCHAR)&Info->Modules[r].ImageName[s + 1], ModuleName)) { Result = Info->Modules[r].Base; break; } } ExFreePool(Info); return Result; } /* Получение адреса экспортируемой функции. */ PVOID GetProcAddress(PVOID hModule, PCSTR lpProcName) { PIMAGE_NT_HEADERS nHeader = GetNtHeaders(hModule); PIMAGE_EXPORT_DIRECTORY pExport; ULONG r; PCSTR FnName; PVOID Addres; if (nHeader) { pExport = (PIMAGE_EXPORT_DIRECTORY)((ULONG)hModule + nHeader->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (MmIsAddressValid(pExport)) { for (r = 0; r < pExport->NumberOfNames; r++) { FnName = (PCSTR)(*(PULONG)(pExport->AddressOfNames + (ULONG)hModule + r * 4) + (ULONG)hModule); Addres = (PVOID)(*(PULONG)(pExport->AddressOfFunctions + (ULONG)hModule + r * 4) + (ULONG)hModule); if (!strcmp(lpProcName, FnName)) return Addres; } } } return NULL; } ULONG GetSdtEntryNumber(PCSTR lpProcName) { return *(PULONG)((ULONG)GetProcAddress(GetModuleHandle("ntdll.dll"), lpProcName) + 1); }
rendo да друг, что то у тебя всё сложно как то. Метод твой негоден, потому как винда может матюгнуться на целостность кода библиотек и еще много всякой хрени, такая фишка в 9х бы на ура проконала, а тут нет
psu Тебе чего, делать совсем нечего, только старые темы поднимать? Если уж так надо, то для этого существует личка.