Booster Берём мы адрес возврата из N-го стекового фрейма. Как определить что он принадлежит процедуре Y() ?
Clerk Я далее писал - ищем бинго, хотя с call[eax] без точного знания границ функции это работать не будет.
Clerk мы знаем адрес входа в Y, чтобы найти все все возможные точки возврата в Y нужен дизасм и небольшой эвристик. правда, ситуации вроде ниже найти будет сложно push M ... ; разные команды после которых стек останется на М jmp A KIV по разному бывает. оптимизаторы могут лихо склеивать и закручивать. (виртальная протекция, я так понимаю, не проходит тк эксцепции не разрешены)
Clerk нахождение вызовов из всех веток функции (исключая jmp|call <reg>. это тоже частично можно, но сложно) и соотв, точек возврата - вполне не сложная задача. и решаная. даже тут на форуме гдето кусок кода постил, хотя в нем нет ничего такого чтоб его искать.
Предложенной мной алгоритм достаточно прост в реализации, единственно что желательно знать размер стека данных Y, но думаю это не проблема. Так что могу. ^)
Booster Первое что пришло на ум - калбэк обрабатывающий манифесты LdrpManifestProberRoutine. Имеем последовательность вызовов: Код (Text): LdrpWalkImportDescriptor LdrpLoadImportModule LdrpHandleOneNewFormatImportDescriptor LdrpHandleNewFormatImportDescriptors LdrpWalkImportDescriptor LdrpLoadDll LdrLoadDll LoadLibraryExW LoadLibraryExA LoadLibraryA Имея адрес LoadLibraryA(), можите достать её стековый фрейм и заменить там адрес возврата(из калбэка LdrpManifestProberRoutine) ? Причём уровень вложенности не фиксирован(тоесть до калбэка может быть иное число процедур). Тогда плз по пунктам или семпл.
Clerk Тут по-моему не может быть 100% решения. А насчёт LdrpManifestProberRoutine, не знаю в чём там могут быть проблемы, если они там могут быть. А алгоритм я уже изложил, он очень прост, не вижу смысла его повторять.
Booster Ну меня интересует практическая реализация, а не бесполезная матчасть. Это всёравно что сказать заюзать иду.
Clerk Так определитесь, что Вы хотите. Лично мне интересна сама задача, реализация гораздо меньше. Тем более, что мне она не нужна, у меня есть более насущные задачи.
Clerk я на предыдущей странице написал - если все запрещено, у вас есть адрес начала Y. пробегитесь по веткам Y, найдите все адреса куда может быть возврат. это не сложно. а потом, ищете по ebp в стеке один из них. следующий адрес возврата по ebp и будет адресом куда вернется управление из Y. подменяйте его. сам код я вам дать не могу, тк он уже ушел. но есть прототип делающий нечто близкое, который я постил на этом форуме. тема както "прошу проверить ..." называлась. код там прототипный, те какнибудьный, но рабочий. если оно вам надо - можете посмотреть/взять. только вслед за TermoSINteZом немного удивляюсь.
Clerk Если Вы не совсем поняли, то повторю идею - перебираем "значения/адреса" стека снизу вверх, смотрим какая инструкция находится по очередному адресу возврата чуть выше него и если там call Y, то это то что нам надо. Проблема в том, что там может быть call [eax] и мы не знаем куда этот вызов идёт, тогда без анализа нижележащего вызова не обойтись и тут без знания адресов размещения Y не обойтись. Что тут ещё объяснять?
Booster тоже мысль. call Y call [Y_in_mem] если call прямо перед ret, то компилеры заменяют {call ХХ; ret} на jmp XX jmp short Y jmp near Y jmp [Y_in_mem] и более сложное. (если ret через c2 или push далеко, придется помудохаться) push Y; ret push [Y_in_mem]; ret регистровая адресация может быть слишком сложна, но она встречается редко. в принципе, можно после нахождения адреса возврата испортить какуюнить переменную в стеке, чтоб вылезло исключение. что нить вроде pop ebx div ebх или pop ecx mov eax,[ecx+4] ; мс С++ в есх this держит. но исключения запрещены, потому этот вариант отпадает.
qqwe Я не прошу кода если вы так думаете, он мне ваш не нужен. Просто хочу знать кто юзал подобные решения. Вот на коленке собрал семпл к #27: Код (Text): .code GPECODE include Gpe.inc %NTERR macro .if Eax Int 3 .endif endm %APIERR macro .if !Eax Int 3 .endif endm Public gChainDispatch Public gLoadLibraryArg .data gSnapshot GP_SNAPSHOT <> gChainDispatch PVOID ? gLoadLibraryArg PSTR ? .code LoadLibrary2ndDispatch proc C pushad invoke DbgPrint, gLoadLibraryArg popad jmp gChainDispatch LoadLibrary2ndDispatch endp LdrpManifestProberRoutine proc DllBase:PVOID, FullDllPath:PCWSTR, ActivationContext:PVOID Local Caller:GP_CALLER lea eax,Caller push eax push UserMode push NULL push offset gSnapshot %GPCALL GP_FIND_CALLER_BELONG_TO_SNAPSHOT .if !Eax mov edx,Caller.Frame lea ecx,LoadLibrary2ndDispatch mov edx,STACK_FRAME.Next[edx] xchg STACK_FRAME.Ip[edx],ecx mov gChainDispatch,ecx mov edx,dword ptr [edx + sizeof(STACK_FRAME)] ; Arg. mov gLoadLibraryArg,edx .endif xor eax,eax ret LdrpManifestProberRoutine endp LdrSetDllManifestProber proto :PVOID _imp__LoadLibraryA proto :PSTR $Dll CHAR "psapi.dll",0 Ep proc Local GpSize:ULONG Local OldProtect:ULONG mov gSnapshot.GpBase,NULL mov GpSize,1000H * X86_PAGE_SIZE invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr gSnapshot.GpBase, 0, addr GpSize, MEM_COMMIT, PAGE_READWRITE mov ebx,gSnapshot.GpBase %NTERR add gSnapshot.GpBase,0FFFH * X86_PAGE_SIZE mov GpSize,X86_PAGE_SIZE invoke ZwProtectVirtualMemory, NtCurrentProcess, addr gSnapshot.GpBase, addr GpSize, PAGE_NOACCESS, addr OldProtect %NTERR mov gSnapshot.GpLimit,ebx mov gSnapshot.GpBase,ebx lea ecx,gSnapshot.GpLimit push eax push eax push eax push eax push eax push 1 push GCBE_PARSE_SEPARATE push ecx push dword ptr [_imp__LoadLibraryA] %GPCALL GP_PARSE %NTERR invoke LdrSetDllManifestProber, offset LdrpManifestProberRoutine invoke LoadLibrary, addr $Dll %APIERR ret Ep endp
qqwe Это проблема, если идёт цепочка таких прыжков и Y где-то посередине, то подмена адреса возврата здесь вообще не работает.
Clerk именно поэтому я упомянул про него, но ветку ту не искал лучше посмотрел на #27. библиотечная осевая функция? в них редко встречаются хитрые вызова и на сами переходы должны быть релоки. можно поискать по релокам call-ы или jmp-ы на Y, прикинуть куда будут возвраты и искать эти цифры в стеке. впрочем, этот путь плох. ваш лучше.
qqwe Там всё просто. Строится граф с единичным уровнем вложенности, тоесть процедурные ветвления не раскрываются, описывается код только текущей процедуры. Затем в калбэке выполнятеся бактрейс и для каждого фрейма выполняется проверка на вхождение адреса возврата в граф, при этом выполняется подсчёт SFN и в целевом фрейме заменятеся адрес возврата. Таким образом исключается зависимость фрейма от уровня вложенности(SFN - SFN') и расположения вызывающей процедуры в коде, это универсальное решение. Тема к тому, что эти овощи ничего такого юзать не могут, сводя всё к стандартному элементарному функционалу(апк там всякие асинхронно не рабочие, модификация PTE руками, патчи и прочий унылый изврат). Есть продвинутые решения, так нужно всё свести к мсдн описалову и какимто там детектам, пичаль. http://www.wasm.ru/forum/viewtopic.php?id=38892
Clerk гдето это я и имел в виду. только вы не строите список возможных точек возврата, а просто проверяете на попадение в граф. это, да, и проще и универсальнее, но момент отмеченный Boosterом остается тут возврат не попадет в граф.