Расширение функциональности OllyDbg [ intro ] Итак, начнём. Наверное каждый кто исследовал сложную/большую программу, сталкивался с проблемой запоминания адресов, подсчёта некоторых констант. Для сохранения адресов и коментариев к некоторым кускам кода, многие пользуються обычным Блокнотом (как и я). Что ни говори, без него уже никуда. Тут возникает проблема его вызова. Как то лень постоянно открывать Пуск > etc. Многие возразят: ‘А разве, Win+R > notepad, не достаточно быстро?’. Да это быстро/удобно, но всё равно не совсем то. Хорошо бы создать дополнительное меню “tools” в OllyDbg, где бы и находились итемы часто используемых прог. Блокнот, Калькулятор etc. Но без хирургического вмешательства этого не достичь. Придется OllyDbg “лечь под нож”. На данную статью меня вдохновила статья из второго номера журнала ARTeam и отсутсвие подобных на великом и могучем. [ tools ] Для этого нам понадобятся: Restorator 2005 – редактор ресурсов Shadow - я выбрал эту модификацию OllyDbg (по вкусу) MASM – компилятор [Правка ресурсов] Windows-программа, помимо исполняемого кода, содержит ресурсы: иконки, диалоги, битмапы, музыку. Только ресурсы не преобразовываются как исполняемый код. И для исправления ресурсов намного удобнее использовать специальные программы. Для этого я выбрал Restorator. Открываем Shadow.exe в Restorator 2005. Перед нами папки ресурсов: диалоги, картинки, меню. Нам естественно нужна папка Menu. Заходим в неё. Выбираем MAINMENU. Я решил засунуть “tools” между “Options”и “Windows”. Поэтому пишем следующее: Code: POPUP "&Tools" { MENUITEM "&Calc", 4001 MENUITEM "&Notepad", 4002 } Естественно между POPUP "Op&tions" {…} и POPUP "&Window" {…}. 4001d – 0FA1h – это ID пункта, будет отсылаться вместе с сообщением. Именно этот ID даёт программе понять, что от неё хотят (точнее понять какой пункт выбран). Теперь закрываем редактор, попутно разрешая сохранить изменеия и бэкап. Открываем Shadow.exe ! Так... Меню создали. Теперь нужно сделать так, чтобы OllyDbg обрабатывала сообщения этих итемов. [Цикл сообщений] Когда пользователь выбирает пункт меню, Windows отсылает программе сообщение WM_COMMAND, которое обрабатывается в цикле сообщений. Каждый, кто программировал под Windows, писал следующие строки: Code: .WHILE TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW GetMessage пеpедает указатель на MSG стpуктуpу Windows. Эта стpуктуpа будет заполнена инфоpмацией о сообщении, котоpые Winsows хотят послать окну этого модуля. Если возвращается false, то это сигнал, что пора завершаться. TranslateMessage переводит код нажатой клавиши в ASCII-сомвол. И теперь самое интересное – DispatchMessage. Эта API передаёт управление функции обработки сообщений (WndProc). Тоесть эта функция поможет нам найти “WndProc”, в которой содержится обработчик сообщения WM_COMMAND. Итак отрываем файл Shadow.exe в другой Olly[Dbg|ICE|Shadow]. Мы попадём на EP. Code: 00401000 > $ /EB 10 JMP SHORT Shadow.00401012 00401002 |66 DB 66 ; CHAR 'f' 00401003 |62 DB 62 ; CHAR 'b' 00401004 |3A DB 3A ; CHAR ':' 00401005 |43 DB 43 ; CHAR 'C' 00401006 |2B DB 2B ; CHAR '+' Запускаем программу (F9) и закрываем все лишнии окна (такие как CPU, хэндлеры, логи, карта памяти), в отлаживаемой программе. Оставивь нужно только основное окно. Мы ведь не хотим рыться в сообщениях чужих окон. Теперь перейдём в DispatchMessage. Для этого жмём Ctrl-G и вводим “DispatchMessageA”. Ставим break “F2” в начале функции и запускаем программу. Останавливаемся. Снимаем бряк. Переходим на карту памяти Alt-M и ставим break на секцию кода. Теперь вам понятна логика действий? Сейчас мы внутри DispatchMessage, которая передаёт управление “WndProc”. Соответсвенно первое попадание в секцию кода – это попадание на “WndProc”. F9 и мы попадём сюда: Code: 004323D4 /$ 55 PUSH EBP 004323D5 |. 8BEC MOV EBP,ESP 004323D7 |. 81C4 04F0FFFF ADD ESP,-0FFC 004323DD |. 50 PUSH EAX 004323DE |. 81C4 30FDFFFF ADD ESP,-2D0 004323E4 |. 53 PUSH EBX 004323E5 |. 56 PUSH ESI 004323E6 |. 57 PUSH EDI 004323E7 |. 8B5D 10 MOV EBX,[ARG.3] 004323EA |. 8B45 0C MOV EAX,[ARG.2] 004323ED |. 3D 11010000 CMP EAX,111 ; Switch (cases 1..478) 004323F2 |. 7F 61 JG SHORT Shadow.00432455 004323F4 |. 0F84 1E140000 JE Shadow.00433818 И вот уже невооружонным глазом видно где проверка WM_COMMAND. 004323ED |. CMP EAX,111 ; Switch (cases 1..478) WM_COMMAND equ 111h Идём по F8 до строки по адресу 004323ED. Далее в меню выбираем “Go to”. Дальше, естественно, “Case 111 (WM_COMMAND)”. И мы здесь: Code: 00433818 |> \8BCB MOV ECX,EBX ; Case 111 (WM_COMMAND) of switch 004323ED 0043381A |. C1E9 10 SHR ECX,10 0043381D |. 66:85C9 TEST CX,CX 00433820 |. 0F85 EC090000 JNZ Shadow.00434212 00433826 |. 8BC3 MOV EAX,EBX Помещаем сюда бряк и выбираем, добавленный нами, пункт в “tools”. Например “calc”. Идём по F8 вот до этого места: Code: 00433A1A |. /E9 77070000 JMP Shadow.00434196 00433A1F |> |81EA C7090000 SUB EDX,9C7 00433A25 |. |0F84 37070000 JE Shadow.00434162 00433A2B |. |4A DEC EDX 00433A2C |. |0F84 4E070000 JE Shadow.00434180 00433A32 |. |E9 5F070000 JMP Shadow.00434196 00433A37 |> |33C0 XOR EAX,EAX ; Case 7D1 of switch 0043382F 00433A39 |. |83C9 FF OR ECX,FFFFFFFF JMP Shadow.00434196 – это переход в случаее если обработка полученного сообщения не предусмотренно. Теперь нам следует подыскать свободное место в программе (для внедрения некоторого кода). Как всегда место нашлось в оверлее. Code: 004AF670 00 DB 00 004AF671 00 DB 00 004AF672 00 DB 00 004AF673 00 DB 00 004AF674 00 DB 00 004AF675 00 DB 00 004AF676 00 DB 00 004AF677 00 DB 00 004AF678 00 DB 00 004AF679 00 DB 00 004AF67A 00 DB 00 004AF67B 00 DB 00 004AF67C 00 DB 00 004AF67D 00 DB 00 004AF67E 00 DB 00 И что же мы туда будем внедрять? Нет! Не обработку данного сообщения, хотя это частично так. Это было бы слишком жирно. Мы напишем dll, Функцию из которой, будем вызывать с этого участка. Code: 004AF670 > \60 PUSHAD 004AF671 . 68 97EC5000 PUSH Shadow_-.0050EC97 ; /ProcNameOrOrdinal = "f_menu" 004AF676 . 68 8DEC5000 PUSH Shadow_-.0050EC8D ; |/FileName = "tools.dll" 004AF67B . E8 B8FAFFFF CALL <JMP.&KERNEL32.LoadLibraryA> ; |\LoadLibraryA 004AF680 . 50 PUSH EAX ; |hModule 004AF681 . E8 0AFAFFFF CALL <JMP.&KERNEL32.GetProcAddress> ; \GetProcAddress 004AF686 . FFD0 CALL EAX 004AF688 . 61 POPAD Теперь JMP Shadow.00434196 меняем на JMP Shadow.004AF670, а в конец этого кода добавляем Code: 004AF689 .^ E9 084BF8FF JMP Shadow_-.00434196 Отлично! Так же не забудьте записать где-нибудь имена библиотеки и функции. Заметка: ------------------------------- CALL <JMP.&KERNEL32.LoadLibraryA> и CALL <JMP.&KERNEL32.GetProcAddress> - не тупо вбиты как “call LoadLibraryA”, “call GetProcAddress”. А как “CALL 004AF138”, “CALL 004AF090”. Code: 004AF138 $- FF25 B4D35000 JMP DWORD PTR DS:[<&KERNEL32.LoadLibrary>; kernel32.LoadLibraryA … 004AF090 $- FF25 44D35000 JMP DWORD PTR DS:[<&KERNEL32.GetProcAddr>; kernel32.GetProcAddress Это сделанно для того, чтобы файл мог запускаться на других системах, где адресс API может быть другим. -------------------------------- Теперь сохраним пропатченный файл. Для этого в меню выбираем “Copy to executable” > “All modifications” > “Copy all”. В открывшемся окне выбираем “Save file” и сохраняем. [Пишем dll] Начнём со скелета DLL. Code: .386 .model flat, stdcall option casemap:none include D:\masm32\include\windows.inc include D:\masm32\include\user32.inc include D:\masm32\include\kernel32.inc includelib D:\masm32\lib\user32.lib includelib D:\masm32\lib\kernel32.lib .code DLLEntry proc hInstDLL:DWORD, reason:DWORD, unused:DWORD .if reason == DLL_PROCESS_ATTACH mov eax,TRUE .endif Ret DLLEntry Endp f_menu proc RET f_menu EndP end DLLEntry Теперь о том как получить сообщение. Когда шли по F8, думаю вы заметили, что оно храниться в edx. Но когда выполняются LoadLibraryA, GetProcAddress регистр edx засерается. Не забываейте, что мы сохранили в стеке все регистры, командой PUSHAD. PUSHAD заносит в стек все регистры от eax до edi Соответсвенно ESP+14h – это содержимое edx. Только когда управление попадёт в нашу функцию, в стек войдёт ещё и адрес возврата. Тогда edx будет по ESP+18h. В итоге у меня получился следующий файл Code: .386 .model flat, stdcall option casemap:none include D:\masm32\include\windows.inc include D:\masm32\include\user32.inc include D:\masm32\include\kernel32.inc includelib D:\masm32\lib\user32.lib includelib D:\masm32\lib\kernel32.lib .data p_calc db "\calc.exe",0 p_notepad db "\notepad.exe",0 .data? pi PROCESS_INFORMATION <> startinfo STARTUPINFO <> buffer db 50 dup(?) .code DLLEntry proc hInstDLL:DWORD, reason:DWORD, unused:DWORD .if reason == DLL_PROCESS_ATTACH mov eax,TRUE .endif Ret DLLEntry Endp f_menu proc mov eax,[esp+18h] cmp eax,5D9h jne @F invoke GetSystemDirectory, addr buffer, sizeof buffer invoke lstrcat,addr buffer,addr p_calc invoke CreateProcess, addr buffer, NULL, NULL, NULL,\ FALSE, NORMAL_PRIORITY_CLASS,\ NULL, NULL, addr startinfo, addr pi RET @@: cmp eax,5DAh jne @F invoke GetWindowsDirectory, addr buffer, sizeof buffer invoke lstrcat,addr buffer,addr p_notepad invoke CreateProcess, addr buffer, NULL, NULL, NULL,\ FALSE, NORMAL_PRIORITY_CLASS,\ NULL, NULL, addr startinfo, addr pi @@: RET f_menu EndP end DLLEntry Компилим! Запускаем нашу Shadow.exe. Выбираем пункт и... Всё работает! Теперь блокнот\калькулятор можно вызвать из меню. [Заключение] Теперь можно смело расширять возможности любой проги. Менять настройки под себя. Удобно! +) PS. Нужно будет добавить кнопку соединения с инт0рнетом))))
хороший пример эксплуатации идеи дероко, респект ) ЗЫ: если преследовать именно идею записи адрессов и констант, то есть ку4а плагинов (нопремер оллипад)
Отлично, многим будет познавательно, вот только LoadLibraryA и GetProcAddress, лучше не вставлять в основной цикл окна, это замедлит обработку сообщений, в данном случае от меню, так как они выполняются не так уж и быстро, их лучше вызвать при старте, и сохранить куда нибудь этот адрес функции или в крайнем случае сделать проверку, есть ли у нас уже этот адрес и если нет тогда выполнить.
OllyPad - это круто, но не очень. Пусть афтар дорабатывает. Блокнот всётаки пока удобней. Хотя если нужно сделать чтот побыстрому, то OllyPad самое то. LoadLibraryA и GetProcAddress - несуть. Это так.. пробный вариант.. Когда сделал, было лень что то менять))
taha ну тут можно и не попасть на обработку wm_command. Например, в win2000 все будет хорошо, а в winXP я попадаю на код 7E3689F1 > 8BFF MOV EDI,EDI Так что тут лучше юзать бряк на DefWindowProcA. (Хотя и не самый лучший вариант). А вот как найти "переход в случаее если обработка полученного сообщения не предусмотренно" - это я не смог. можешь подсказать как искать это переход?