.386
.model flat, stdcall
include WINDOWS.INC
include kernel32.inc
include user32.inc
includelib KERNEL32.LIB
includelib USER32.LIB
TestHook proto
ProcessHook proto
.const
msgCaption db "Hooktest DLL", 0h
msgHooking db "Hooking called...", 0h
msgHookDone db "Hooking done...", 0h
msgHooked db "Hooked successfully...", 0h
.data
TestHookAddr dd 0040100Fh ; пишем call сюда
HookCallData db 0E8h ; опкод call'а
.data?
oldProtect dd ? ; тут копия старых прав
bytesWritten dd ? ; сколько байт записано
ProcId dd ? ; текущий PID проги
hProc dd ? ; хендл управления процессом
.code
LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
cmp reason, DLL_PROCESS_ATTACH
jnz NotAttach
invoke TestHook ; if reason==DLL_PROCESS_ATTACH call TestHook
NotAttach:
mov eax, TRUE
ret
LibMain endp
TestHook proc
invoke GetCurrentProcessId
mov ProcId, eax
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, ProcId
mov hProc, eax
invoke MessageBox, NULL, ADDR msgHooking, ADDR msgCaption, MB_OK
invoke VirtualProtect, TestHookAddr, 5, PAGE_EXECUTE_READWRITE, oldProtect ; выставляем права
invoke WriteProcessMemory, hProc, TestHookAddr, ADDR HookCallData, 1, bytesWritten
inc TestHookAddr ; инкрементим TestHookAddr на 1
; invoke WriteProcessMemory, hProc, TestHookAddr, ProcessHook, 4, bytesWritten ; ошибка тут, ProcessHook проверял, вроде передает правильный адрес, но он абсолютный. Как узнать относительный?
invoke VirtualProtect, TestHookAddr-1, 5, oldProtect, oldProtect ; возвращаем старые права
invoke MessageBox, NULL, ADDR msgHookDone, ADDR msgCaption, MB_OK
ret
TestHook endp
ProcessHook proc
invoke MessageBox, NULL, ADDR msgHooked, ADDR msgCaption, MB_OK ; Оповещение, что обработчик успешно вызван.
ret
ProcessHook endp
end LibMain
Вычисление relative address для jmp, call
Поставил перед собой задачу, нужно написать примитивный перехватчик бажной функции программы-жертвы, работающий в среде windows.
Как он должен работать?
К программе-жертве аттачится DLL (с помощью PETools например).
Запускаем программу, DLL начинает делать своё дело:
1. Устанавливает права на участок памяти в code-секции программы жертвы, с помощью winapi-функции "VirtualProtect()".
2. Заменяет инструкцию (размер 5 байт вместе с операндом) по заданному адресу инструкцией jmp (E9 xxxxxxxx) или call (E8 xxxxxxxx), адрес которой должен указывать на нашу функцию по обработке перехваченной функции.
3. Возвращает обратно старые права на участок памяти winapi функцией "VirtualProtect()".
Для чего нужно?
Для написания патча для скомпилированной бажной программы, которую сто лет никто не обновлял, но полезность свою та не утратила при этом. Хочу добавить проверки переменных бажной функции.
Суть проблемы.
Нужно вычислить relative address (относительный адрес), чтобы записанные в программу-жертву call или jmp указывали на правильный адрес обработчика бажной функции (обработчик сидит в подгруженной DLL).
Инструкции типа FF (JMP FAR и CALL FAR с абсолютными адресами) мне непонятны, они по размеру уж точно более 5 байт. В этом случае участок кода в программе-жертве превратится в мусор, который еще и надо вычислять, сколькими nop'ами забивать, что мне пока не под силу.
Пример кода DLL, добавил комменты:
Код:
первые байти оригинальной функции заменяем на свои, предварительно сохранив оригинал где нить в памяти (ну ето само собой понятно). Потом, вместо того чтобы вычислять E8XXXXXXXX или чё там ещё можно просто засунуть адрес в eax например, а потом его оттуда вызвать:
Код:
db 0xB8 ;mov eax,
func_addr dd ? ;addr
jmp eax
func_addr dd ? ;addr
jmp eax
func_addr - адрес функции в твоей dll. Узнать его можно с помощью GetProcAddress.
Но если тебе приспичило ломать голову то, насколько я знаю для того что бы вычислить относительный адрес чего то там, то надо знать RVA того места откуда идёт вызов:
Код:
00401000 start:
00401000 E903000000 jmp loc_00401008
00401005 90 nop
00401006 90 nop
00401007 90 nop
00401008 loc_00401008:
00401008 C3 ret
00401000 E903000000 jmp loc_00401008
00401005 90 nop
00401006 90 nop
00401007 90 nop
00401008 loc_00401008:
00401008 C3 ret
Короче, как то так: E9 - опкод, 03000000 - операнд, обозначающий что надо перейти на три байта вперёд. Теоретично можно узнать адреса всего шо надо, но первый способ по-моему легче.
Код:
TestHookAddr dd 00401000h
HookCallData db 0B8h, 0ABh, 10h, 0h, 10h, 0FFh, 0D0h, 0C3h
HookCallData db 0B8h, 0ABh, 10h, 0h, 10h, 0FFh, 0D0h, 0C3h
Код:
invoke WriteProcessMemory, hProc, TestHookAddr, ADDR HookCallData, 8, bytesWritten
Результат (проверяю на тестовой проге):
Код:
00401000 /$ B8 AB100010 MOV EAX,100010AB
00401005 |. FFD0 CALL EAX
00401007 \. C3 RETN
00401005 |. FFD0 CALL EAX
00401007 \. C3 RETN
Обработчик наконец-то нормально вызвался, чем оповестил мессаджбоксом!
Мегаспасибо, 12345678! :)