Pull to refresh

Comments 72

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

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

тогда каждую перехватываемую процедуру не надо описывать в _Analyze
p.s.: для того чтобы случайно не «задеть» косвенную адресацию, достаточно опкоды проверять с небольшим списком. т.к. в прологе большинства WinAPI функций содержатся только push и mov.
В большинстве API-функций в начала вообще находится стандартый 5-байтовый пролог, однако в недрах 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, потому что она того стоит и самим такое универсальное решение разработать будет стоить дороже.
Там в заголовке анекдот на тему как раз для того, чтобы эту строку не воспринимали всерьез.

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

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

Например, через перехват 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, написано тут
UFO just landed and posted this here
На самом деле, он совсем не страшный — это всего-навсего один такт процессора. Любая высокоуровневая инструкция сожрет таких тактов 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 — пишите драйвер.
Драйвер ядра для того чтобы свободно писать блоки данных в АР
Ужас. Я не понимаю зачем это надо в разделе С++? Какое отношение пост имеет к С++? Тоже самое можно сделать на любом другом языке, это рас. Во вторых это ужасно устарело и не знают об этом разве что дотеры и веб дизайнеры. Вот уж не думал что пост ещё и комментировать будут.
Я разочарован хабром :(
Sign up to leave a comment.

Articles