Как стать автором
Обновить

Комментарии 72

Я когда-то тоже писал аналогичный велосипед.

Но вместо сравнивания первых 5 байт с фиксированной константой (в методе Analyze) я взял <a href=«www.google.ru/search?q=length+disassembler>дизассемблер длин LDE32.

тогда каждую перехватываемую процедуру не надо описывать в _Analyze
p.s.: для того чтобы случайно не «задеть» косвенную адресацию, достаточно опкоды проверять с небольшим списком. т.к. в прологе большинства WinAPI функций содержатся только push и mov.
В большинстве API-функций в начала вообще находится стандартый 5-байтовый пролог, однако в недрах WinNT.h, например, все уже не столь радужно.
ntdll.dll, конечно же, а не winnt.h
мне этого хватало для перехвата довольно большого количества даже Nt* и Zw* функций. конечно, для нормального продукта этого тоже мало (надо перехватывать всё), но на просторах интернета есть также и бесплатные и _очень_ продвинутые аналоги detours.

P.P.S.: в девичестве detours был бесплатен и даже не требовал detours.dll. у меня где-то даже его сорцы остались.
Все равно, если даже используешь аналог — неплохо было бы понимать, как в принципе он работает. Именно на это и рассчитана статья, а вовсе не на написание собственного велосипеда.
В данном случае, _Analyze — это просто метод-заглушка под будущую реализацию подобного механизма.

Я именно поэтому описал в возможностях модернизации применение какого-либо дизассемблера. Именно туда его и планируется вставить.
Есть еще hde32, который можно использовать в этих целях. Вообще, таких решений пусть и не очень много, но они есть. Причем, по возможностям они почти не отличаются друг от друга.
Это всё чудесно, но какой смысл что-то перехватывать в рамках одного (своего) модуля? ;) Если же вы влезаете (inject) в другой (чужой) модуль, то появляется новая проблема, например ASLR.
Смысл есть. Например, в том случае, если вы — add-in для ворда. Либо — shell extension. Либо во всех остальных случаях, когда вашу DLL совершенно легально загружают в процесс.

Кроме того, методики внедрения DLL в чужой процесс описаны на том же RSDN.
смотря как влезем. если боле-менее «легально» то всю эту рандомизацию нам сдаст с потрохами загрузчик.

ASLR имеет смысл когда мы залезаем через buffer overflow и прочие «вирусные» варианты.
Да-да, но если мы влезаем «легально», то зачем нам что-то перехватывать? ;) Ведь тогда есть «легальные» способы для доступа к данным.
В том то и проблема, что они есть не всегда, и позволяют делать не все.
иногда требуется.
либо, как ниже написали — слегка изменить поведение программы.
либо, как мне однажды понадобилось, вытаскивать данные из другой программы, но не ковырянием в памяти, а по мере их поступления.
Нет «легальных» способов для доступа к данным если код не ваш. Например внедрение в браузер, например если вы пишете программу которая управляет другими программами те же GUI test/automation. Мои друзья когда появилась такая задача всё таки купили detours, потому что она того стоит и самим такое универсальное решение разработать будет стоить дороже.
«Сэкономить 10.000$» — достаточно пафосно :)

В таком случае, используя в работе www.codeproject.com/KB/winsdk/LibMinHook.aspx, я экономлю еще и время на написание велосипеда :)
Там в заголовке анекдот на тему как раз для того, чтобы эту строку не воспринимали всерьез.

За ссылку спасибо, буду изучать.
Я тоже нисколько не претендовал на серьёзность, просто хотел ссылкой поделиться :)

Ах да… Совсем забыл, спасибо за статью.
И в который раз скажу — не надо так делать :-) Если вы, конечно, не вирье пишите :-)
Все зависит от поставленной задачи. Иногда приходится применять и такие методы.

Например, через перехват CreateFile мы реализуем прозрачную загрузку файлов с сервера.
Не, понятно, что в некоторых случаях иначе никак и для использования нужны очень веские основания.
Естественно. Работа на низком уровне — большая ответственность для программиста. Это не на шарпе формочки пихать.
Я тоже с использованием этого механизма позволяю организовать работу в пакете NX(бывший Unigraphics) в рамках нашей plm-системы.

Перехват функций работы с файлами+внутренних функций NX, для того, чтобы вместо имен файлов видны были обозначение+ревизия и пользователь не работал напрямую с файловой системой.
Не логичнее ли сделать отдельную ФС и не лезть в программу?
Слабо с ходу прикинуть трудозатраты на разработку?
Я же сказал, что вся работа ведется в рамках нашей системы.

В данном случае, пользователь нажимает где-то в nx на кнопку требующую открытия файла с диска — перехватывается GetOpenFilename — и он видит диалоги выбора, написанные мной(думаю очевидно, что это даёт бОльшие возможности по поиску, сортировкам, выборке по критериям).

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

Много чего удобного благодаря этому уже сделано :) Мыслите шире.
и если вирьё пишете, то тоже делать так не надо.
для вирья надо (как минимум):
1. всё-таки нормальный LDE, т.к. перехватить надо реально много функций
2. перехват не в первых 5 байтах, а дальше (т.к. большинство антивирусов проверяют прологи на джамп за пределы модуля). вы же не хотите спалиться? ;)
Да не так много и надо перехватить. Зачастую recv+send хватает, чтобы пароли тырить :-)
ну да, для этого конечно хватит. я подразумевал user-land руткиты ;)
блин, читал между строк и не заметил, что у вас есть ссылка :)
точнее, наоборот, через строчку читал
Бывает, что поделать.
Извините, если задам сейчас глупый вопрос. Но я правильно понимаю, что программы, написанные таким образом, совершенно точно не будут работать под Wine и в ReactOS? Там ведь те же функции API реализуются совершенно иначе?

Я не специалист в этой области, но все равно жутко интересно :)
Под ReactOS должно сработать, только там, вероятно, придется править метод поиска образа kernel32.dll в памяти процесса.
и под вайном будет.
если искать точки перехвата через GetModuleHandle/GetProcAddress
Аналогичный метод в свое время очень детально описал MsRem на wasm.ru, плюс рассмотрел варианты о том, как обмануть файерволлы, дабы они не выкидывали варнинги.
Интересно, что же такое создаёт автор, что требует перехвата CreateFile и CloseHandle? ;)
CloseHandle не нужен, он так, для примера, а вообще нужна просто прозрачная работа с серверными файлами в локальных приложениях — загрузка и выгрузка.

Перехватчик — это только часть архитектуры, нужный только для загрузки файла по первому обращению.
Что-то мне подсказывает, что Detours не зря стоит 10к $.
P.S.: боян, и метод называется splicing
Супербизон. Горжусь и плюсую.
Общеизвестных методов перехвата API-функций существует ровно два, все остальные – их вариации.

Скажем так: первые приходящие на ум.
Половина вопросов автора решится методом VEH (описан подробно здесь с блэкдж... с исходниками и подробными комментариями). Но у метода один минус: работает, начиная с Windows XP.
Вообще говоря, перехват API в user-mode дело крайне неблагодарное и некрасивое.
Я статью еще не читал, только вкратце пробежал по заголовкам, но, честно говоря, не совсем понимаю, какое структурная обработка исключений имеет отношение к перехвату функций.
На самом деле, если вкратце пробежаться по заголовкам, то речь идет о векторной обработке исключений.
Статья описывает данную технологию, и в качестве примера по ее использованию приводится в пример с перехватом функции LoadLibraryExW (к которой сводятся все UM версии LoadLibraryX). Суть в следующем: в начало функции мы вставляем одну единственную инструкцию 'INT 03', при вызове функции получаем исключение STATUS_BREAKPOINT… Ну а дальше суть понятна.
Да, этот способ кстати тоже вкратце задевался на RSDN. Там же описаны проблемы этого способа — какой-нибудь чужой обработчик SEH может поломать нам всю логику.

А вероятность встретить его в общем случае ненулевая.
Хочу еще раз обратить Ваше внимание на то, что там затрагивается VEH.
Плюс тебе за анекдот про такси!
Спасибо за статью
Спасибо за статью.
Поидее, если делать инъекцию не в свое адресное пространство, а в чужое, то, если между вызовами
lpFunc->opcode = 0xe9;
lpFunc->relativeAddress = _CalculateDispacement(lpFunc, &_My_CreateFileW);
произойдет переключение задачи, и атакуемая задача попробует вызвать недоизмененную функцию, то произойдет джамп непонятно куда, что очень печально.
Решением может быть использование одной из команд mov из SSE, которые умеют атомарно 8 или 16 байт переносить.
На самом деле, в реальном приложении вероятность этого довольно мала, так как подключение обработчиков происходит в момент LoadLibrary. Если хочется перестраховаться — можно использовать атомарные инструкции MMX или SSE.
Ну я, собственно, о перестраховке и говорю. Ситуация с переключением задачи возможна, но очень маловероятна. Но все-таки для стабильной работы программы не стоит забывать об этой проблеме. О чем я и напомнил.
Почему вы используете
void* lpFunc = GetProcAddress(hKernel32, «CreateFileW»);
вместо
void* lpFunc = &CreateFileW;
?
Так повелось, что я для собственных функций использую амперсанд, а импортированные получаю через GetProcAddress.

Наверное, можно и так, как вы предлагаете.
Кстати, про проблему, с которой можно столкнуться на x64, написано тут
НЛО прилетело и опубликовало эту надпись здесь
На самом деле, он совсем не страшный — это всего-навсего один такт процессора. Любая высокоуровневая инструкция сожрет таких тактов 10-15, особенно вызов функции.

Судя по тому, что я в свое время гуглил — она вставлена специально для возможности организовать там перехват.
А если вспомнить куда ведут многие API, то и 15 тактов это сущий пустяк
может не в тему (много букв, не прочитал все), а может кому-то пригодится — так я перехватываю waveOutWrite (к примеру):

[pascal]

type
TFarJmp = packed record
PuhsOp: byte;
PushArg: pointer;
RetOp: byte;
end;

var
FCurrentProcess: cardinal;
WaveOutFnAddr: pointer;
WaveOutOldProc, WaveOutNewProc: TFarJmp;

// init
FCurrentProcess := GetCurrentProcess;
WaveOutFnAddr := GetProcAddress(h, 'waveOutWrite');

WaveOutNewProc.PuhsOp := $68;
WaveOutNewProc.PushArg := @WaveOutWriteMod;
WaveOutNewProc.RetOp := $C3;

ReadProcessMemory(FCurrentProcess, WaveOutFnAddr, @WaveOutOldProc, sizeof (TFarJmp), w);
WriteProcessMemory(FCurrentProcess, WaveOutFnAddr, @WaveOutNewProc, sizeof (TFarJmp), w);

// fine
WriteProcessMemory(FCurrentProcess, WaveOutFnAddr, @WaveOutOldProc, SizeOf (TFarJmp), w);

function WaveOutWriteMod (hWaveOut: HWAVEOUT; lpWaveOutHdr: PWaveHdr; uSize: UINT): MMRESULT; stdcall;
begin
// чегото тут поделал скажем с данными
WriteProcessMemory (FCurrentProcess, WaveOutFnAddr, @WaveOutOldProc, SizeOf(TFarJmp), w);
Result := waveOutWrite(hWaveOut, lpWaveOutHdr, uSize);
WriteProcessMemory (FCurrentProcess, WaveOutFnAddr, @WaveOutNewProc, SizeOf(TFarJmp), w);
end;

[/pascal]
как-то так…
Ну, это, еще одна методика

push retaddr
ret

Только она по размеру вроде как больше 5 байт (5 байт только на push уйдет, + байт на ret), нэ?
а я хз, 5 байт или сколько там… когда-то когда надо было нашел способ, проверил, что работает — и пользую, а как работает — уже «черный ящик», пусть себе работает
А что мешает сохранять, например, первые байт 8 или скока нада, и потом при восстановлении возвращать все на место?
Чем больше захватываешь кода, тем больше шанс, что
1. Функция просто кончится
2. Заденешь код с косвенной адресацией
На wasm.ru есть отличный цикл статей на тему перехвата API с примерами и библиотекой.
wasm.ru/series.php?sid=8
Правда, примеры на Delphi. Библиотеки для С++ можно найти на codeproject, например:
www.codeproject.com/KB/DLL/EasyHook64.aspx
или (писали выше) www.codeproject.com/KB/winsdk/LibMinHook.aspx
А как быть с DEP? B потом когда антивирусы видят VirtualProtect они начинают нервничать.
DEP обходится как раз VirtualProtect-ом.

А нервничать начинают антивирусы только в том случае, если мы пишем в память чужого процесса.
Эээ… Я знаю, что DEP обходится VirtualProtect потому и написал про него, но меня интересует не способ разломать DEP, а способ перехвата не трогая DEP.

Антивирусы видят, что ты можешь юзать VirtualProtect и этого уже достаточно чтобы стать подозрительным.
Не хотите VirtualProtect — пишите драйвер.
Драйвер чего?
Драйвер ядра для того чтобы свободно писать блоки данных в АР
Ужас. Я не понимаю зачем это надо в разделе С++? Какое отношение пост имеет к С++? Тоже самое можно сделать на любом другом языке, это рас. Во вторых это ужасно устарело и не знают об этом разве что дотеры и веб дизайнеры. Вот уж не думал что пост ещё и комментировать будут.
Я разочарован хабром :(
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории