Прошу сильно не смеяться - этот код собран из разных примеров человеком, недавно узнавшим, что такое ассемблер. Код подставляется в чужой процесс (например IE), перехватывает все попытки соединения, при каждой такой попытке должен выводить msgbox. Код (Text): Tread proc Local Mhandle:HANDLE Local RealAddr1:DWORD Local CurrProcID:DWORD local Code1Addr:DWORD ;код перехода call GetCurrPosition add ecx, 5h jmp @F db 68h ; опткод push Hooker1 dd 0 db 0c3h ; опткод ret @@: mov Code1Addr,ecx ;ищем функцию dodata <db "connect",0> push ecx dodata <db "wsock32.dll",0> push ecx call EGetProcAddress .if eax==0 jmp Ext .endif mov RealAddr1,eax dodata <db "GetCurrentProcessId",0> push ecx dodata <db "kernel32.dll",0> push ecx call EGetProcAddress call eax mov CurrProcID,eax ;открываем процесс dodata <db "OpenProcess",0> push ecx dodata <db "kernel32.dll",0> push ecx call EGetProcAddress push CurrProcID push 0 push PROCESS_ALL_ACCESS call eax .if eax==NULL jmp Ext .endif mov Mhandle,eax Delt: call GetCurrPosition sub ecx, offset Delt add ecx, offset Hooker mov eax,Code1Addr mov [eax+1], ecx ;записываем перехватчик dodata <db "WriteProcessMemory",0> push ecx dodata <db "kernel32.dll",0> push ecx call EGetProcAddress push 0 push 6 push [Code1Addr] push RealAddr1 push Mhandle call eax .if eax==0 jmp Ext .endif ret ; перехватчик Hooker: dodata <db "MessageBoxA",0> push ecx dodata <db "user32.dll",0> push ecx call EGetProcAddress push MB_OK dodata <db "GOTCHA!!!",0> push ecx push ecx push 0 call eax ret Ext: dodata <db "ExitThread",0> push ecx dodata <db "kernel32.dll",0> push ecx call EGetProcAddress push 0 call eax ret Tread endp GetCurrPosition proc mov ecx, [esp] ret GetCurrPosition endp Здесь EGetProcAddress - процедура поиска адресов API. dodata - макрос вида: Код (Text): dodata MACRO some_data call GetCurrPosition add ecx, 5h jmp @F &some_data @@: ENDM Проблема в том, что код перехода не работает. То есть адрес целевой функции находится, в первые ее 6 байт что-то записывается, но не переход - при попытке использования wsock32.dll!connect программа-жертва просто падает. Кому не трудно, помогите найти ошибку - я уже голову об клаву разбил. Про необходимость приостановки потоков и восстановления оригинального начала функции я помню, но это потом.
Возможно, хотя тогда неизвестно смещение переменной. Но главное - почему не работает код перехода? Ведь все вроде правельно. Хотя какое там правельно...
неправда, оно находится в [esp]: Код (Text): call skip db 'USER32.DLL', 0 skip: call LoadLibrary P.S. код смотреть лень
Точно - по крайней мере в ecx остается верный адрес - в макросе та же технология и для всех переменных, объявленных через него, возвращается верный адрес. Сам код перехода db 68h ; опткод push Hooker1 dd 0 db 0c3h ; опткод ret весит 6 байт.
Вот, тока это masm. Там много моего мусора и недоделок - не обращай внимание. Основная идея взята отсюда и из статей Bill'а Prisoner'а
//оффтоп такой код, кстати, отлаживать довольно противно (хотя метод, бесспорно, самый удобный). думаешь, что вызов какой-нить подпрограммы, а там оказывается продолжение тела текущей
Во-первых, ты с дельтой че-то намудрил. Метку Delt нужно на строчку ниже опустить. Блин, у тебя и правда сложный для понимания код. И зачем после этой метки ты заполняешь локальную переменную Hooker1Addr??? Ты же ее нигде потом не используешь. Может тебе нужна переменная просто Hooker1? Только не забудь, что когда будешь заполнять hooker1, тебе надо тоже вычислить ее адрес. P.S.: извиняюсь, если сморозил глупость - может я где-то неправильно понял код. Просто в 2 часа ночи такой код плохо воспринимается P.P.S.: разреши дать совет: используй классический код для дельты: Код (Text): call delta delta: pop ebx sub ebx,delta Так тебе не придется каждый раз ее непонятным образом вычислять. При этом регистр ebp свободен (т.е. можешь использовать лок. переменные), а ebx
Во-первых, ты с дельтой че-то намудрил. Метку Delt нужно на строчку ниже опустить. Блин, у тебя и правда сложный для понимания код. И зачем после этой метки ты заполняешь локальную переменную Hooker1Addr??? Ты же ее нигде потом не используешь. Может тебе нужна переменная просто Hooker1? Только не забудь, что когда будешь заполнять hooker1, тебе надо тоже вычислить ее адрес. P.S.: извиняюсь, если сморозил глупость - может я где-то неправильно понял код. Просто в 2 часа ночи такой код плохо воспринимается P.P.S.: разреши дать совет: используй классический код для дельты: Код (Text): call delta delta: pop ebx sub ebx,delta Так тебе не придется каждый раз ее непонятным образом вычислять. При этом регистр ebp свободен (т.е. можешь использовать лок. переменные), а ebx кроме как для дельты тебе особо и не нужен
УРРА!!! Заработало =) Огромное спасибо. То, что в это время кто-то может заниматься моей проблемой, я и предположить не мог. Чемпион по глупостям здесь я - до сих пор не могу разобраться в трети того, что содрал с примеров=) Еще раз большое спасибо.
В продолжение темы - допустим нам вместо вместо msgbox в перехватчике нужно установить свое (подменить) соединение. Порядок выполнения ясен: восстанавливаем оригинальное начало connect, вызываем ее со своими параметрами. Вопрос: к моменту попадания в перехватчик где будут находиться оригинальные параметры, переданные функции? То есть ясно, что в стеке. Можно ли их вытащить по типу pop eax прямо в перехватчике, или это совсем глупо?
Да, это можно, но не удобно, т.к. тебе надо сначала убрать стековый кадр, потом достать адрес возврата, потом только попить параметры. Я делаю у себя так: объявляю по нормальному ф-ю обработчика Код (Text): MyHook proc p1,p2,...pn:DWORD some code MyHook endp При этом я просто заново пушу в стек переданные параметры и вызываю оригинальный обработчик, предварительно "восстановив первые байты"
То есть при вызове такого обработчика без лишних движений можно будет просто pop'ить параметры, которые в свою очередь сами передадутся в стек перехватчика? И для интереса, если идти трудным путем "надо сначала убрать стековый кадр, потом достать адрес возврата" - адрес возврата, как я понимаю будет в [esp], но механизм работы со стеком в данном случае мне не понятен, как понять "убрать стековый кадр"?
Нет. Если объявить перехватчик так, как написал я, то можно оперировать просто параметрами p1... Т.е. не надо будет писать что-то типа mov eax,[esp+7*4+8+...]. Тебе для вызова оригинального обработчика надо будет сделать так: push pn ... push p2 push p1 call original_func ;(только первые байты восстанови) Никаких попов не надо! Из своего обработчика выходишь просто с пом. ret. Если ты объявляешь процедуру так, как я сказал и/или используешь локальные переменные, то компилятор (во всяком случае масм) вначале прецедуры пишет push ebp/mov ebp,esp/sub esp... - это типа и есть стековый кадр. Я ж тебе и советую объявлять функию обработчика отдельно - так гораздо удобнее - и к параметрам, и к локальным переменным обращаться. Да и вообще со стеком не запутаешься. Но это только на мой взгляд.