участились случаи ногебания icq по вопросу "как перехватить функцию"
в гугл слал, в лес слал, в бабруйск слал, всеравно возвращаются с вопросами
незнаю куда еще можно послать... поэтому решил описать "своими словами" чтобы слать сюда.
существует стопицот способов перехвата, у всех есть свои плюсы\минусы
опишу простейший, называется "сплайсинг"
0)
есть приложение, компилить не надо,
просто качаем\запускаем\пробуем вложение поциэнт.rar,
исходник такой:
c++ Код:
#include <Windows.h>int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){
LoadLibrary(L"tabletka.dll"); //чтобы не засирать пример, "инжектить" лекарство будем так.
MessageBoxW(0,L"нажмите ок чтобы ботить, тока тсссс.....",L"ncsoft(c)",MB_ICONWARNING);
MessageBoxW(0,L"ботоводы, горите в аду!",L"угнова(c)",MB_ICONERROR | IDCANCEL );
return0;
};
как мы видим, данная программа имеет некоторые недостатки,
а именно, наглые, неполиткорректные высказывания.
а так делать незя.
1)
лезем сюда _http://msdn.microsoft.com/ изучаем нужную нам функцию (ну или хотябы код в заголовках),
она выглядит так: int __stdcall MessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
находится тут: Minimum DLL Version user32.dll
2)
теперь нужно научится ее вызывать хитрым способом (или изготовление функции трамплина),
делаем простой пример:
c++ Код:
DWORD addr=0;
int __stdcall jmpMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType){
__asm jmp [addr];};
main(){
DWORD target = (DWORD)GetProcAddress(LoadLibrary("user32.dll"),"messageboxW");
addr = target + 5; //прыгать мы будем не в "начало" функции, а на пять байт дальше.
jmpMessageBoxW(0,L"текст",L"текст", UINT uType);
};
//компилируем, убеждаемся что такая программа будет работать.
мы пропускаем часть оригинального кода, а ошибок всеравно нету, обьясняю почему.
в отладчике это будет выглядить так:
asm Код:
test.exe:10001770 jmpMessageBoxW:
test.exe:10001770pushebp // выполняется, этот код дописал нам компилятор
test.exe:10001771movebp, esp // выполняется, этот код дописал нам компилятор
test.exe:10001773jmpds:addr // наш джамп
test.exe:10001779popebp // сюда мы никогда не попадем
test.exe:1000177A retn10h
..
user32.dll:7E3B6534 MessageBoxW:
user32.dll:7E3B6534 movedi, edi //пропускаем, бессмысленная инструкция, типа x=x, так захотели в майкрософт.
user32.dll:7E3B6536 pushebp //пропускаем
user32.dll:7E3B6537 movebp, esp //пропускаем
user32.dll:7E3B6539 cmpds:dword_7E3C14BC, 0 //<<--мы прыгаем сюда
user32.dll:7E3B6540 jzshort loc_7E3B6566
сравниваем инструкции которые мы пропустили в оригинальной функции,
и какие нам дописал компилятор в функции трамплине.
видно очень красивое "совпадение", вот какбы поэтому оно и работает.
значит если изуродовать первые 5 байт оригинальной функции, мы всеравно сможем ее вызвать.
3)
придумаем "функцию фильтр_обработчик"
c++ Код:
int __stdcall hookMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType){int result = 0;
if(lstrcmpW(lpCaption,L"угнова(c)")){//если в заголовке окна есть нецензурное слово, вызываем оригинал со своими параметрами
result = jmpMessageBoxW(hWnd, L"слава роботам!!! убить всех человеков!!", L"какой то робот(с)", MB_ICONINFORMATION);
}else{//если в заголове чтото другое, ничего не трогаем, вызываем оригинал
result = jmpMessageBoxW(hWnd, lpText, lpCaption, uType);
};
return result;
};
продолжение ниже
__________________
Шожиделать.. ботить хочется..
Последний раз редактировалось destructor, 29.01.2010 в 23:13.
За это сообщение destructor нажился 7 спасибками от:
4)
собственно сам "сплайсинг"
идея заключается в том чтобы в начале оригинальной функции записать "jmp куда_нам_надо" и тогда поциэнт будет вызывать "то_что_нам_надо"
код инструкции jmp xxxxx выглядит так: 1байт опкод 0xe9 + 4 байта смещение(какраз 5 байт, тоже "совпадение"),
"смещение" означает сколько байт нам надо перепрыгнуть,
если нам надо прыгнуть на 500 байт вперед, пишем туда 500,
если нам 300 байт назад пишем туда -300.
вычеслить нужное нам значение нужно так: v = targetaddr - posjmpaddr; targetaddr, адрес куда надо прыгнуть (в нашем случае это адрес hookMessageBoxW) posjmpaddr, адрес инструкции jmp + ее размер (+5 байт).
тоесть,
c++ Код:
//делаем доступной память для записи.
target = (DWORD)GetProcAddress(LoadLibrary("user32.dll"),"MessageBoxW");
VirtualProtect((void*)target,PAGE_EXECUTE_READWRITE,10,&oldprotect);
//вычисляем адрес куда будем писать опкод:
BYTE * pE9 = (BYTE*)target;
//вычисляем адрес, куда будем писать смещение
DWORD * pofset = (DWORD*)(pE9+1);
//пишем в память:
*pE9 = 0xe9;
*pofset = (DWORD)hookMessageBoxW - (target+5);
5)
собираем в кучу написанное выше и оформляем в виде длл:
c++ Код:
#include <Windows.h>//трамлин
DWORD addr;
int __stdcall jmpMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType){
__asm jmp [addr];};
//обработчикint __stdcall hookMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType){int result = 0;
if(lstrcmpW(lpCaption,L"угнова(c)")==0){//если в заголовке окна есть нецензурное слово, вызываем оригинал со своими параметрами
result = jmpMessageBoxW(hWnd, L"слава роботам!!! убить всех человеков!!", L"какойто робот(с)", MB_ICONINFORMATION);
}else{//если в заголове чтото другое, ничего не трогаем, вызываем оригинал
result = jmpMessageBoxW(hWnd, lpText, lpCaption, uType);
};
return result;
};
BOOL WINAPI DllMain( HINSTANCE hModule,DWORD Reason, LPVOID lpReserved ){
DWORD target;
DWORD oldprotect;
BYTE * pE9;
DWORD * pofset;
switch(Reason){case DLL_PROCESS_ATTACH:
target = (DWORD)GetProcAddress(GetModuleHandleW(L"user32.dll"),"MessageBoxW");
if(!VirtualProtect((void*)target,10,PAGE_EXECUTE_READWRITE,&oldprotect))returnFALSE; // а вдруг?//настраиваем трамплин
addr = target + 5; //прыгать мы будем не в "начало" функции, а на пять байт дальше.//правим оригинал, в начало записываем джамп на наш обработчик
pE9 = (BYTE*)target;
pofset = (DWORD*)(pE9+1);
*pE9 = 0xe9;
*pofset = (DWORD)hookMessageBoxW - (target+5);
break;
case DLL_PROCESS_DETACH:
//тут выгрузка длл//надо восстановить оригинальный код функции,//но нам по*уй, такой херней страдают только в конец задроченные ботаны,//а все чоткие праграмисты знают что "меньше кода = меньше багов"break;
};
returnTRUE;
};
жмем компилить, должна получится tabletka.rar
теперь если рядом с нашим поциэнтом положить таблетку, мы сможем наблюдать чудесное исцеление.
какашками кидацо незя, т.к. это часный\стерильный случай
__________________
Шожиделать.. ботить хочется..
Последний раз редактировалось destructor, 29.01.2010 в 23:32.
За это сообщение destructor нажился 7 спасибками от:
destructor, то что первые 5 байт совпали в этом примере эт конечно хорошо, но это довольно редкий случай и охрененно упрощающий код притом. А вот если первые 5 байт не совпадают, то начинается веселье, надо переместить первые инструкции (естественно целое количество инструкций) которые заполняют первые 5 или больше байт куданить себе в массив и ещё дописать в массив джамп на оригинальную функцию (со смещением равным количеству списанных из начала байт), оформить этот массив как имеющий возможность быть выполненным (ну VirtualProtect'ом обработать опять же), а вот уже дальше записать на место первых 5 байт оригинала джамп, ну и в своей функции вместо оригинала уже вызывать не оригинал со смещением, а наш массив.
ЗЫ и эт ещё хорошо если мы можем функцию перехватываемую в дизассемблере посмотреть и посчитать сколько байт занимает целое число команд в начале, а если не можем то надо ещё как то программно это определять...
__________________
Я здесь практически не появляюсь!, Skype - ikskor
а есть идея как экспорер перехватывать? точней html контент. потом перекраивать как надо - и подсовывать эксплореру как нивчем небывало?
сейчас перед запуском своей программы я ставлю в файлик hosts этот сайт, и "редиректом" на 127.0.0.1 - где, собственно, поднимается маленький веб сервер. браузер в результате не замечает и работает, думая что все нормально... вот хотелось бы, пускай вирусоподобно, подменять этот адрес в самом эксплорере, без редактирования файла hosts. и при этом чтобы сохранялись всякие keep alive соединения будь то форум, веб аська, или чат.
хороший пример proxomitron. работает как прокси сервер и фильтр. туда засунул по шаблону к примеру рекламу бегуна - и на всех сайтах, где он её встретит - отрезает.
но там все фильтры открыты и нужно браузер настраивать на работу как с прокси. а мне хотелось бы эти самые фильтры спрятать, а все настройки автоматизировать. притом proxomitron, и современные её аналоги типа proximodo - уже не развиваются, закрыты сто лет назад. и основная проблема подвисает чуть чуть.
поэтому хотел бы сделать нечто вроди плагина для IE, который бы занимался этим самым редиректом по началу, и как развитие идеи - перекраиванием по шаблону как proxomitron.
адрес jmpMessageBoxW это начало "массива",
потом просто делаю копипасту нужного количества инструкций:
memcpy(jmpMessageBoxW,MessageBoxW, X);
и все, код не сильно усложняется
__________________
Шожиделать.. ботить хочется..
Последний раз редактировалось destructor, 30.01.2010 в 17:19.
За это сообщение destructor нажился 3 спасибками от:
Для реальных ситуаций, надо либо смотреть какие инструкции в начале функции и прописывать их в jmpMessageBoxW() руками,
либо использовать дизассемблер длин инструкций и определять длину и тип копируемых инструкций с его помощью.
Также надо учитывать что когда в начало функции пишется "jmp hook", то если какой-то поток в это время исполняет код в этом месте - прога упадет, по этому либо надо быть уверенным что никакие потоки эту часть кода исполнять не будут, либо замораживать все потоки, проверять это и если надо - переносить eip на новый код.
Дизассемблер длин инструкций, адаптированный для VC++ можно посмотреть тут http://code.google.com/p/nabla-kb/so...s/rce/hook.cpp
(функция instruction_length, пропустить сложно; остальной код в этом сорце работает только частично)