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

Вниз

Перехват апи функций   Найти похожие ветки 

 
Коля   (2003-10-09 11:36) [0]

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


 
Digitman ©   (2003-10-09 11:47) [1]

можно. для ОС на NT-платформах.
но это достижимо только для существующих в момент осуществления перехвата процессов
для вновь стартующих процессов нужно предпринимать далеко не нетривиальные меры по отслеживанию их старта, чтобы проделать и с ними то же самое

так что, если ОС произвольная, то "овчинка выделки не стоит" : гораздо проще использовать DLL, внедряемую системой при установке глоб.хука автоматически во все существующие и потенциально стартующие новые процессы приложений


 
yaJohn ©   (2003-10-09 15:12) [2]

Есть метод. В тело перехватываемой функции внедряется jmp. Или call. Под ДОСом это называли сплайсинг. Для таких пируэтов нужно быть на 0 кольце. Это тоже достижимо.
Заморочка еще та, конечно. Само сабой зависимость от версии оси. Есть работающий пример (на С). Там перехватывали WinSock функции (т.е. реализовали т.о. сниффер). Успешно, я проверял ;)
Поиск по соответствующим ключевым словам (на английском) + "сниффер". Если не найдется, но очень надо - пишите на мыло, дома кажется был архив.


 
Digitman ©   (2003-10-09 15:37) [3]


> В тело перехватываемой функции внедряется jmp


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

и к чему извращение с jmp, когда достаточно поменять значения соответствующих элементов таблиц импорта/экспорта ?


> Для таких пируэтов нужно быть на 0 кольце


в общем случае совершенно необязательно
содификация страниц кода ничем не отличается от модификации прочих страниц


 
yaJohn ©   (2003-10-09 16:35) [4]

>таких "тел" столько будет, сколько имеется активных процессов и сколько их еще стартует новых)
Вы абсолютно уверенны? Т.е. для каждого запущенного процесса тупые винды грузят свой экземпляр кернел.длл?
>и к чему извращение с jmp, когда достаточно поменять значения соответствующих элементов таблиц импорта/экспорта ?
И как Вы себе это представляете на практике?
>содификация страниц кода ничем не отличается от модификации прочих страниц
Действительно, какая к черту разница? Код, данные... Все одно - байтики и ничего больше... Берем, тоесть чужой процесс за глотку и спрашиваем "А ну-ка где там твой код?!" И он нам быстренько отвечает. Мы не долго думая обращаемся к массиву Mem[]...


 
Digitman ©   (2003-10-09 16:47) [5]


> Т.е. для каждого запущенного процесса тупые винды грузят
> свой экземпляр кернел.длл?


а что ты понимаешь под "экземпляром" ?


> И как Вы себе это представляете на практике?


не только представляю, но и успешно пользую на практике

вкратце так :

для каждого модуля каждого процесса получаем ссылку на их таблицы импорта и экспорта, находим нужные элементы, разрешаем запись в нужные страницы памяти, меняем значения элементов на значения, указавающие на новую точку входа ... и всех делов ... кр.того, следует в 1-ю очередь проделать это для LoadLibraryA/W с целью автоотслеживания дин.загрузки интересующих с т.з. перехвата модулей


 
yaJohn ©   (2003-10-09 17:02) [6]

Под экземпляром я понимаю экземпляр. Двоичный код. В который можно при желании врезать jmp.


 
Digitman ©   (2003-10-09 17:11) [7]


> yaJohn


врезай) .... врежешь для тек.процесса и только


 
yaJohn ©   (2003-10-09 17:14) [8]

А если подумать? А не предвзято?


 
Mystic ©   (2003-10-09 17:25) [9]

>yaJohn © (09.10.03 17:14) [8]

Секция .CODE содержит атрибут "копирование при записи". Т. е. до тех пор, пока не осуществляется попыток записи на страницу, DLL из всех процессов ссылается на одну и ту же физическую страницу в памяти (если, конечно, DLL оказались загружеными по одному адресу). при попытке записи в секцию .CODE будет для этого процесса будет создана комия страницы.

Почти любая DLL содержит секцию экспорта, где стоит пара команд JMP Address. Это облегчает процесс загрузки DLL. Так что проще поменять адреса там.


 
Digitman ©   (2003-10-09 17:36) [10]


> yaJohn



> Mystic © (09.10.03 17:25) [9]


чуть опередил меня.
о COPY_ON_WRITE тебе, надеюсь, известно ?


 
Digitman ©   (2003-10-09 17:39) [11]


> yaJohn


т.н. "экземпляры" создаются в строгом соответствии с атрибутами секций в заголовочной инф-ции соответствующего PE-модуля


 
Digitman ©   (2003-10-09 17:50) [12]


> > yaJohn


про секции кода уже вроде бы прояснилось ?

есть еще секции данных (инициализированных и неинициализированных)

экз-ры этих загружаемых секций по умолчанию (если соотв.опции линкера не применялись) индивидуальны для каждого процесса, поэтому следует проделать модификацию сист.таблиц для каждого целевого процесса ... и это, как ни странно, гораздо проще выкрутасов с подменой фрагмента кода на входе в тело п/п


 
Digitman ©   (2003-10-09 17:54) [13]

просто в кач-ве иллюстрации :

код вызывается в контексте любого из код.потоков тек.процесса
(как туда этот код попадает - отдельный вопрос)


function SetDLLProcAddress(hProcess: THandle; lpExpModuleName, lpProcName: PChar; pProcAddr: Pointer): Pointer;
var
hExporter, hImporter, hModule: THandle;
pOptHdr: PImageOptionalHeader;
pExpDir: PImageExportDirectory;
pImpDir: PImageImportDescriptor;
pIATEntry: PImageThunkData;
pName: PChar;
pdwNamePtr, pdwEntryPoint: PDWord;
pwOrdinalPtr: PWord;
i, dwDirSize, nOrdinal, dwProtect, dwNewProcAddrRVA: DWord;
peb: PPeb;
pLdrData : PPEB_LDR_DATA;
pLoadOrderList : PPLIST_ENTRY;
pInLoadModuleEntry: PModuleEntry;
pModuleNfo: PModuleInfo;
begin
Result := nil;
hExporter := GetModuleHandle(lpExpModuleName);
if hExporter = 0 then
Exit;
if not Assigned(pProcAddr) then
Exit;
hModule := FindHInstance(pProcAddr);
if hModule = 0 then
Exit;
pOptHdr := PImageOptionalHeader(hExporter + PImageDosHeader(hExporter)._lfanew + SIZE_OF_NT_SIGNATURE + IMAGE_SIZEOF_FILE_HEADER);
dwDirSize := pOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
if (pOptHdr.NumberOfRvaAndSizes < 16) or (dwDirSize = 0) then
Exit;
pExpDir := PImageExportDirectory(hExporter + pOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
pdwEntryPoint := nil;
pwOrdinalPtr := PWord(hExporter + DWord(pExpDir.AddressOfNameOrdinals));
if HiWord(DWord(lpProcName)) = 0 then
begin
nOrdinal := LoWord(DWord(lpProcName)) - pExpDir.Base;
if (nOrdinal < pExpDir.NumberOfFunctions) then
pdwEntryPoint := PDWord(hExporter + DWord(pExpDir.AddressOfFunctions) + nOrdinal * SizeOf(PDWord));
end
else
begin
pdwNamePtr := PDWord(hExporter + DWord(pExpDir.AddressOfNames)) ;
for i := 0 to pExpDir.NumberOfNames - 1 do
begin
pName := PChar(PDWord(hExporter + PDword(pdwNamePtr)^));
if lstrcmp(pName,lpProcName) = 0 then
begin
nOrdinal := pwOrdinalPtr^;
pdwEntryPoint := PDWord(hExporter + DWord(pExpDir.AddressOfFunctions) + nOrdinal * SizeOf(PDWord));
Inc(nOrdinal, pExpDir.Base);
break;
end
else
begin
Inc(pdwNamePtr);
Inc(pwOrdinalPtr);
end
end;
end;
if not Assigned(pdwEntryPoint) or (pdwEntryPoint^ = 0) then
Exit;
Result := Pointer(hExporter + pdwEntryPoint^);
dwNewProcAddrRVA := DWord(pProcAddr) - hExporter;
try
if VirtualProtectEx(hProcess, pExpDir, dwDirSize, PAGE_EXECUTE_WRITECOPY, dwProtect) then
try
pdwEntryPoint^ := dwNewProcAddrRVA;
finally
VirtualProtectEx(hProcess, pExpDir, dwDirSize, dwProtect, dwProtect);
end;
peb := GetCurrentPEB;
// LockLoader(peb);
try
pLdrData := peb^.LdrData;
pLoadOrderList := @pLdrData.InLoadOrderModuleList;
pInLoadModuleEntry := PModuleEntry(pLoadOrderList^);
while PPListEntry(pInLoadModuleEntry) <> pLoadOrderList do
begin
pModuleNfo := @pInLoadModuleEntry.ModuleInfoData;
hImporter := THandle(pModuleNfo^.DllBase);
if hImporter <> hExporter then
begin
pOptHdr := PImageOptionalHeader(hImporter + PImageDosHeader(hImporter)._lfanew + SIZE_OF_NT_SIGNATURE + IMAGE_SIZEOF_FILE_HEADER);
if (pOptHdr.NumberOfRvaAndSizes = 16) then
begin
dwDirSize := pOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
if dwDirSize > 0 then
begin
pImpDir := PImageImportDescriptor(hImporter + pOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while Assigned(PChar(pImpDir.RVAImportModuleName)) do
begin
if lstrcmpi(PChar(hImporter + pImpDir.RVAImportModuleName), lpExpModuleName) = 0 then
begin
pIATEntry := PImageThunkData(hImporter + DWord(pImpDir.RVAImportAddressTable));
while Assigned(pIATEntry.Func) do
begin
if pIATEntry.Func = Result then
begin
if VirtualProtectEx(hProcess, pIATEntry, SizeOf(pIATEntry), PAGE_EXECUTE_WRITECOPY, dwProtect) then
begin
pIATEntry.Func := pProcAddr;
VirtualProtectEx(hProcess, pIATEntry, SizeOf(pIATEntry), dwProtect, dwProtect);
end;
break;
end;
Inc(pIATEntry);
end;
end;
Inc(pImpDir);
end;
end;
end;
end;
pInLoadModuleEntry := PModuleEntry(pInLoadModuleEntry.InLoadOrderLinks.Flink);
end;
finally
// UnlockLoader(peb);
end;
finally
end;
end;


 
yaJohn ©   (2003-10-09 17:58) [14]

Склоняю голову пред вашими познаниями. Никто и не думает оспаривать их истинность.

Человек задал вопрос. У меня есть работающий пример в точности соответствующий требуемой функциональности. Который почемуто игнорирует правила для .CODE.


 
Digitman ©   (2003-10-09 18:09) [15]


> yaJohn


ну так приведи его ! хотя бы в кач-ве иллюстрации своего мнения на эту тему !

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


 
Digitman ©   (2003-10-09 18:13) [16]


> Который почемуто игнорирует правила для .CODE.


да он, наверно, не игнорирует ...
просто атрибуты загруженных из секции страниц в большинстве случаев можно менять ... другой вопрос, ПОЧЕМУ ты не оперируешь в своих аргументах этим и не ссылаешься на эту важную деталь в твоей реализации) ... вот это вызывает большие и известные сомнения)


 
NightAngel ©   (2003-10-09 23:16) [17]

Всё что здесь говорилось справедливо только для платформы NT, да и то с некоторыми оговорками. Механизм COPY_ON_WRITE реализуется на уровне архитектуры процессора (процессор устанавливает бит "Грязная" в таблице страниц второго уровня, прежде чем выполнить операцию записи по адресу, отображаемому данным элементом таблицы, именно по нему Винда определяет, что нужно сделать копию модифицируемой страницы и вообще этот механизм применяется к почти любым страницам, не обязательно содержащих исполняемый код). Можно пропатчить kernel из SoftIce под любую Win на платформе NT и эти изменения будут справедливы для ВСЕХ процессов (как запущенных, так и нет), т.е. для ВСЕХ процессов эти страницы будут общими (Винда будет отображать модифицированные страницы во все адресные пространства запускаемых процессов). Далее, механизма COPY_ON_WRITE на платформе Win9x/ME - НЕТ, вот там можно перехватить вызова любых функций системных библиотек вышеописанным способом, впрочем, можно это сделать и на платформе NT (примерно так-же, как это делается из SoftIce). Проблемы будут только с реентерабельностью, да с размещением кода - перехвата. Под Win9x/ME эта задача решается просто, под NT платформы решение нетривиальное, но проще по моему использовать документированные способы перехвата API функций, а не мучиться дурью с сплайсингом.


 
Коля   (2003-10-10 04:23) [18]

Всем спасибо!
Но все те способы что вы привели, мне известны. Более того условием задачи является работоспособность на всех платформах.
to yaJohn:
скинте, пожалуйста, на nmz-18@rambler.ru пример если не сложно.


 
Коля   (2003-10-10 04:33) [19]

А можно ли использовать для этой цели DebugApi?


 
NightAngel ©   (2003-10-10 14:01) [20]

> Более того условием задачи является работоспособность на всех платформах.
> можно ли использовать для этой цели DebugApi?


Можно сделать перехват с помощью hardware break point. Я не помню чья это идея (вроде-бы Voodoo, если я не ошибаюсь), но работоспособность гарантируется на всех платформах. Идея такая: Регистры отладки поддерживают контрольные точки по командам и данным. В общем, под контрольной точкой понимается адрес, при использовании которого программой возникает отладочное исключение. Установка контрольной точки по команде обеспечивает регистрацию выполнения команды по любому линейному адресу. При возникновении отладочного исключения вызывается int 1. Обработать его в ring3 считается весьма проблематичным. Int 1 вызывается и при пошаговой отладке программы. Как оказалось можно обработать этот случай отладки и в ring3. Для этого используется SEH. При возникновении случая отладки система передает управление SEH-у данного потока который вызвал этот случай отладки, c его контекстом и кодом исключения EXCEPTION_SINGLE_STEP. Всё, что требуется - это обработать это исключение. Таким образом, чтобы перехватить, какую-либо API функцию, нужно настроить свой SEH, загрузить в DR0 адрес функции, в DR7 установить биты RW0, LEN0 в 0, L0 в 1 и ждать пока не вызовут функцию. Т.к. SEH есть у каждого потока то этот hardware break point (HBP) будет работать лишь в контексте данного потока. Т.е. если функция вызывается из новой нити, то мы ничего не поймаем. Выходом из данной ситуации является перехват CreateThread(). Перехватив CreateThread() меняем адрес нити на адрес нашей подпрограммы, которая настраивает SEH и устанавливает HBP, в качестве параметра потока (lpParameter) передаётся адрес нити, которая должна была запускаться. Получив этот параметр наша подпрограмма отдаст туда управление. В своей реализации я передавал указатель на структуру:
dd AddressOfNewThread
dd OdlLpParameter
а затем восстанавливал старое значение lpParameter (так будет корректней). После обработки EXCEPTION_SINGLE_STEP снимается HBP и "включается" пошаговый режим выполнения программы, установкой в EFLAGS TF=1. После выполнения одной команды снова устанавливается HBP. Это необходимо для предотвращения зацикливания. Этот метод работает от win95 до WinXP, т.е. является win32 совместимым. Да, и ещё - оригинальная идея была не полной, а именно, после того как мы перехватив обработчик, отдаём управление программе, она устанавливает свои обработчики. До нашего обработчика очередь может просто не дойти. Т.о. чтобы этот метод работал, необходимо быть "первым". Как-то я решил эту проблему, исходников сейчас под рукой нет.


 
Ketmar   (2003-10-10 16:03) [21]

>Коля (10.10.03 04:33) [19]
фиг там.

>NightAngel © (10.10.03 14:01) [20]
сложно. в 9х проще попасть в Ring0 и делать всё, что хочется. в NT -- другая сказка. собственно, Digitman лучше меня всё знает (хотя и я делал, делал %-).



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

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

Наверх




Память: 0.54 MB
Время: 0.008 c
14-11703
electric
2003-11-12 20:12
2003.12.04
$$$ Money $$$


4-11747
npAKTuk
2003-10-07 09:03
2003.12.04
Реализация DragNDrop в API


6-11680
ss300
2003-10-06 11:03
2003.12.04
Кодировка в отправляемом письме


4-11750
lamorg
2003-10-10 17:31
2003.12.04
Как установить ani-курсор


8-11623
Great Lesh
2003-08-04 03:54
2003.12.04
Avi -> BMP