Как без дрова и сплайсинга отвечать File Management функциям (ReadFile, SetFilePointer, GetFileInformationByHandle)? Хочеца на лету ганерировать данные и отдавать сторонней проге.
Прикинул вобщем так. 1. Захват апи. Так как их много, все они в kernel32, по сему нормальный способ следующий. Для работы с обьектом нужен его описатель. Если описатель не валидный, функи будут возвращать ошибку, это нам и нужно. Если сервисы возвращают ошибку, то функции загружают код ошибки(NT и WIN32) в TEB. Тут работает следубщий механизм, дабы не описывать стопяцот раз одно и тоже, просто скопирую матчасть: Тогда поток вызывает апи, она вызывает сервис, тот возвращает ошибку, так как описатель обьекта не валидный, прогружаются коды ошибок, проверяется переменная g_dwLastErrorToBreakOn на значение ERROR_INVALID_HANDLE(Win32, а не ядерный код!), куда мы его прежде загрузим, соответственно он и будет установлен, так как ядро вернёт STATUS_INVALID_HANDLE, коды совпадают и выполняется брейк(Int 3). При этом диспетчер исключений, либо отладочный порт(если процесс не текущий) получает сообщение. Теперь нужно обработать останов. Мы выполняем бактрейс и заменяем адрес возврата из апи на свой. После чего поток отпускаем, при возврате управление будет передано на наш код. Он может непосредственно выполнить дальнейшую обработку, либо сгенерить сепшен для удалённой обработки. В стеке параметры функи, там мы восстанавливаем описатель и вызываем есчо раз эту функу, также подменив адрес возврата из неё. Она успешно отработает. Как она отработает это уже чисто ваш вопрос - выполните вы трейс её до вызова сервиса, эмуляцию её и пр. 2. Описатель файла возвратит функа CreateFileW(). Нужно её захватить. Вот деограмка: Код (Text): CreateFileW() -\ | + RtlDosPathNameToNtPathName_Ustr() -\ | | | + RtlAllocateHeap() | | | + RtlAcquirePebLock() -> RtlEnterCriticalSection() | + RtlFreeHeap() Опишу в начале общий принцип. Необходимо сгенерировать исключение какимто образом внутри апи. Это исключение обрабатывается, выполняется бактрейс и захват адреса возврата из апи. Это позволяет вызвать её повторно либо контролировать все параметры как входные, так и возвращаемые. При этом мы должны сохранить созданный описатель(если нужен) и возвратить инвалидный. Таким образом самые простые способы: o RtlAllocateHeap() - захват описан у меня в логе: o RtlAcquirePebLock() - это вход в критическую секцию FastPebLock. Захват: Подобные вопросы описаны в этом топике http://wasm.ru/forum/viewtopic.php?id=34151&p=1, там и примеры есть. Только не знаю зачем это вам. Классические способы это всякие изменения в секциях кода: джампы, брейки IAT и пр.
Clerk А можно просто перехватить вызовы API для работы с файлами, и в итоге получить тот же самый результат, но с более простой, надёжной реализацией которая, к тому же, в меньшей степени будет завязана на недокументированные внутренности ОС.
Cr4sh Код менять можно как угодно. Вот только обнаружить и снять это не составляет труда. Подумал если автор не желает юзать ядро и изменять код(джампы), то должно быть скрытно.
Твой способ применительно к данной задаче имеет существенную загвоздку: перехваты (кстати, это не обязательно должен быть сплайсинг) всё равно понадобятся, иначе как ты собрался в целевом приложении спровоцировать передачу в, к примеру, ReadFile невалидного дескриптора?
Clerk Короче, вот тебе код приложения: Код (Text): int main() { HANDLE hFile = CreateFile("C:\\WINDOWS\\explorer.exe", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD dwSize = GetFileSize(hFile, NULL); printf("File size is %d bytes\n", dwSize); CloseHandle(hFile); } return 0; } Опиши пошагово, как ты без каких-либо перехватов собираешься эмулировать файловые операции, которое это приложение выполняет.
Cr4sh Думал я понятно описал. Это и есть перехват, для некоторых трассировка не является способом захвата кода.. Укорочиваю смысл до предельно возможного: гдето внутри функи возникает состояние, при котором поток прерывается, это исключения, бесконечные циклы, ожидание на обьектах и пр. Тогда мы выполняем бактрейс и замену адреса возврата в нужном нам стековом фрейме. Либо трассировку до необходимого места. Чатсный случай этого: Описан в #2.
Cr4sh Чтоб не лезть в дебри, покажу на рабочем примере http://wasm.ru/forum/viewtopic.php?id=36993 Эмулируется создание файловой секции. Каким образом - полностью трассируется загрузчик, тоесть каждую инструкцию его и контекст потока я имею и могу менять как угодно ход исполнения.
Cr4sh Хорошо, на описанном примере решения: o Регаю VEH. o Делаю инвалидной сигну HEAP.Signature. o Жду пока диспетчер получит управление. o В нём выполняю бактрейс и смотрю откуда был вызов. Если не из CreateFile() отпускаю тред. o Иначе выполняю бактрейс и заменяю адрес возврата, после чего отпускаю тред. o Получю управление при возврате из CreateFile(). Возвращаю в hFile инвалидное значение. o Регаю код ошибки в g_dwLastErrorToBreakOn. - Вызывается GetFileSize(): o Срабатывает останов в DbgBreakPoint(). Смотрю причину, если не наша отпускаю тред. o Иначе выполняю бактрейс и заменяю адрес возврата, после чего отпускаю тред. o Получаю управление при возврате из GetFileSize(). o Обрабатываю функу как угодно.
Clerk Спасибо, теперь всё ясно. Способ действительно жизнеспособный, просто с длинного описания я сходу не врублился в суть манипуляций с хипом.
Clerk Будет-ли работать вышеописанная схема, если код приложения #8 выполняется в обработчике SEH? (типа конструкции __try - __except - __finally... или SetUnhandledExceptionFilter)
gorodon Да. Ведь нельзя спутать к примеру #AV с #DB. Даже если хватать функи средствами IDP_Engine(помните там по ссыли), то всёравно известен адрес инструкции которая должна сгенерить исключение(и целевой диапазон также).
Вот код для поиска нужно окружения и обработки брейка и отладочного вывода при инвалидации сигнатуры хипа: http://indy-vx.narod.ru/Bin/Heap.zip o xLdrParseRtlpCheckHeapSignature() o xHmgrDispatchException() (Лишнее можно удалить.)
Лог: Если инвалидация сигнатуры хипа универсальна, тоесть работает во всей линейке NT(и в W7), то останов на g_dwLastErrorToBreakOn работает только в XP, описанный способ лучший(не знаю как с совместимостью). Запускаем трейс описателей с помощью ProcessHandleTracing. После этого передача в ядро инвалидного описателя будет генерит #STATUS_INVALID_HANDLE. Тру хек
До кучи кодес. Ищет описатель директории первой: Код (Text): xCompareStringSensitiveInternal proc uses ebx UnicodeString:PUNICODE_STRING, AnsiString:PSTR, StringLength:ULONG mov ebx,StringLength mov edx,UnicodeString lea eax,[ebx*2] assume edx:PUNICODE_STRING cmp [edx]._Length,ax jne Exit mov ecx,AnsiString mov edx,[edx].Buffer @@: movzx eax,byte ptr [ecx + ebx - 1] cmp word ptr [edx + ebx*2 - 2],ax jne Exit dec ebx jnz @b xor eax,eax Exit: ret xCompareStringSensitiveInternal endp ; + ; Опредедяет указатель на ETHREAD текущего потока. ; BASE_REGION_SIZE equ 10000H xQueryKnownDllsDirectory proc uses ebx esi edi Directory:PVOID Local SystemInformation:PVOID, SystemInformationLength:ULONG Local $Directory[12]:CHAR Local ObjectName[sizeof(UNICODE_STRING) + (10*2 + 4)]:BYTE mov SystemInformationLength,0 invoke ZwQueryObject, NULL, ObjectAllTypesInformation, NULL, NULL, addr SystemInformationLength .if Eax != STATUS_INFO_LENGTH_MISMATCH ; def. 224 bytes. mov SystemInformationLength,PAGE_SIZE .endif mov SystemInformation,NULL invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr SystemInformation, 0, addr SystemInformationLength, MEM_COMMIT, PAGE_READWRITE test eax,eax jnz Exit invoke ZwQueryObject, NULL, ObjectAllTypesInformation, SystemInformation, SystemInformationLength, Eax test eax,eax mov edi,SystemInformation jnz Parse mov esi,OBJECT_ALL_TYPES_INFORMATION.NumberOfTypes[edi] mov dword ptr $Directory[0],"eriD" mov dword ptr $Directory[4],"rotc" mov dword ptr $Directory[2*4],"y" mov ebx,esi add edi,4 assume edi:POBJECT_TYPE_INFORMATION @@: invoke xCompareStringSensitiveInternal, Edi, addr $Directory, 9 movzx ecx,[edi].TypeName._Length je Parse and ecx,NOT(3) mov edi,[edi].TypeName.Buffer lea edi,[edi + ecx + 4] dec esi jnz @b mov eax,STATUS_UNSUCCESSFUL Parse: push eax invoke ZwFreeVirtualMemory, NtCurrentProcess, addr SystemInformation, addr SystemInformationLength, MEM_RELEASE pop eax sub ebx,esi ; ObjectTypeNumber test eax,eax mov esi,BASE_REGION_SIZE jnz Exit inc ebx NextRegion: mov SystemInformationLength,esi mov SystemInformation,NULL invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr SystemInformation, 0, addr SystemInformationLength, MEM_COMMIT, PAGE_READWRITE test eax,eax jnz Exit invoke ZwQuerySystemInformation, SystemHandleInformation, SystemInformation, SystemInformationLength, Eax test eax,eax jz ParseInfo push eax invoke ZwFreeVirtualMemory, NtCurrentProcess, addr SystemInformation, addr SystemInformationLength, MEM_RELEASE pop eax cmp eax,STATUS_INFO_LENGTH_MISMATCH jnz Exit add esi,BASE_REGION_SIZE cmp esi,32*BASE_REGION_SIZE jb NextRegion jmp Exit ParseInfo: mov esi,SystemInformation mov dword ptr $Directory[0],"onK\" mov ecx,fs:[TEB.Cid.UniqueProcess] mov edi,dword ptr [esi] mov dword ptr $Directory[4],"lDnw" mov dword ptr $Directory[2*4],"sl" add esi,4 NextEntry: assume esi:PSYSTEM_HANDLE_INFORMATION cmp [esi].ProcessId,ecx jne @f cmp [esi].ObjectTypeNumber,bl ; def. 2: Directory. jne @f push ecx lea edx,ObjectName push NULL push sizeof(UNICODE_STRING) + (10*2 + 4) movzx ecx,[esi].Handle push edx push ObjectNameInformation push ecx Call ZwQueryObject test eax,eax pop ecx jnz @f cmp UNICODE_STRING._Length[ObjectName],14H jne @f push ecx invoke xCompareStringSensitiveInternal, addr ObjectName, addr $Directory, 10 pop ecx jne @f mov edx,Directory movzx ecx,[esi].Handle mov dword ptr [edx],ecx jmp ParseError @@: add esi,sizeof(SYSTEM_HANDLE_INFORMATION) dec edi jnz NextEntry mov eax,STATUS_NOT_FOUND ParseError: push eax invoke ZwFreeVirtualMemory, NtCurrentProcess, addr SystemInformation, addr SystemInformationLength, MEM_RELEASE pop eax Exit: ret xQueryKnownDllsDirectory endp OBJECT_HANDLE_FLAG_INFORMATION struct Inherit BYTE ? ProtectFromClose BYTE ? OBJECT_HANDLE_FLAG_INFORMATION ends POBJECT_HANDLE_FLAG_INFORMATION typedef ptr OBJECT_HANDLE_FLAG_INFORMATION Собственно находит описатель директории "\KnownDlls". После этого его можно скопировать(не DUPLICATE_CLOSE_SOURCE!) и тутже создать новый, без разницы какой, но не типа Directory. Тогда в загрузчике будет возникать сепшен. Хидер: Код (Text): PROCESS_HANDLE_TRACING_ENABLE struct Flags ULONG ? PROCESS_HANDLE_TRACING_ENABLE ends PROCESS_HANDLE_TRACING_ENABLE_EX struct Flags ULONG ? TotalSlots ULONG ? PROCESS_HANDLE_TRACING_ENABLE_EX ends PROCESS_HANDLE_TRACING_MAX_STACKS equ 16 HANDLE_TRACE_DB_OPEN equ 1 HANDLE_TRACE_DB_CLOSE equ 2 HANDLE_TRACE_DB_BADREF equ 3 PROCESS_HANDLE_TRACING_ENTRY struct Handle HANDLE ? ClientId CLIENT_ID <> _Type ULONG ? ; HANDLE_TRACE_DB_* Stacks PVOID PROCESS_HANDLE_TRACING_MAX_STACKS (<>) PROCESS_HANDLE_TRACING_ENTRY ends PROCESS_HANDLE_TRACING_QUERY struct Handle HANDLE ? TotalTraces ULONG ? HandleTrace PROCESS_HANDLE_TRACING_ENTRY 1 DUP (<>) PROCESS_HANDLE_TRACING_QUERY ends ProcessHandleTracing equ 32 Надеюсь хоть ктото тут в теме
Тестовый семпл: http://indy-vx.narod.ru/Bin/LdrExts.zip Для кода: Код (Text): invoke GetModuleFileName, NULL, addr Buffer, MAX_PATH APIERR invoke CreateFile, addr Buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL mov ebx,eax .if Eax == INVALID_HANDLE_VALUE int 3 .endif invoke GetFileSize, Ebx, NULL APIERR mov dword ptr [Buffer],0 invoke ReadFile, Ebx, addr Buffer, 2, addr Count, NULL APIERR invoke CloseHandle, Ebx APIERR На XP: