Авторские статьи Расширение функциональности OllyDbg

Discussion in 'Статьи' started by taha, 9 Apr 2007.

  1. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251

    Расширение функциональности 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.

    [​IMG]

    Я решил засунуть “tools” между “Options”и “Windows”. Поэтому пишем следующее:

    Code:
    POPUP "&Tools"
     {
      MENUITEM "&Calc", 4001
      MENUITEM "&Notepad", 4002
     }
    Естественно между POPUP "Op&tions" {…} и POPUP "&Window" {…}.

    4001d – 0FA1h – это ID пункта, будет отсылаться вместе с сообщением. Именно этот ID даёт программе понять, что от неё хотят (точнее понять какой пункт выбран).

    Теперь закрываем редактор, попутно разрешая сохранить изменеия и бэкап.
    Открываем Shadow.exe !

    [​IMG]

    Так... Меню создали. Теперь нужно сделать так, чтобы 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”.

    [​IMG]

    Дальше, естественно, “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

    [​IMG]

    Соответсвенно ESP+14h – это содержимое edx. Только когда управление попадёт в нашу функцию, в стек войдёт ещё и адрес возврата. Тогда edx будет по ESP+18h.

    [​IMG]

    В итоге у меня получился следующий файл

    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. Выбираем пункт и... Всё работает! Теперь блокнот\калькулятор можно вызвать из меню.

    [Заключение]

    Теперь можно смело расширять возможности любой проги. Менять настройки под себя.

    [​IMG]

    Удобно! +)

    PS. Нужно будет добавить кнопку соединения с инт0рнетом))))
     
    6 people like this.
  2. ProTeuS

    ProTeuS --

    Joined:
    26 Nov 2004
    Messages:
    1,239
    Likes Received:
    542
    Reputations:
    445
    хороший пример эксплуатации идеи дероко, респект )
    ЗЫ: если преследовать именно идею записи адрессов и констант, то есть ку4а плагинов (нопремер оллипад) ;)
     
  3. hidden

    hidden 7H3 0N3

    Joined:
    23 Apr 2006
    Messages:
    550
    Likes Received:
    332
    Reputations:
    386
    Отлично, многим будет познавательно, вот только LoadLibraryA и GetProcAddress, лучше не вставлять в основной цикл окна, это замедлит обработку сообщений, в данном случае от меню, так как они выполняются не так уж и быстро, их лучше вызвать при старте, и сохранить куда нибудь этот адрес функции или в крайнем случае сделать проверку, есть ли у нас уже этот адрес и если нет тогда выполнить.
     
  4. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251
    OllyPad - это круто, но не очень. Пусть афтар дорабатывает. Блокнот всётаки пока удобней. Хотя если нужно сделать чтот побыстрому, то OllyPad самое то.

    LoadLibraryA и GetProcAddress - несуть. Это так.. пробный вариант.. Когда сделал, было лень что то менять))
     
    1 person likes this.
  5. EB_FE

    EB_FE New Member

    Joined:
    10 Aug 2007
    Messages:
    12
    Likes Received:
    4
    Reputations:
    5
    taha
    ну тут можно и не попасть на обработку wm_command. Например,
    в win2000 все будет хорошо, а в winXP я попадаю на код
    7E3689F1 > 8BFF MOV EDI,EDI

    Так что тут лучше юзать бряк на DefWindowProcA. (Хотя и не самый лучший вариант).
    А вот как найти "переход в случаее если обработка полученного сообщения не предусмотренно" - это я не смог. можешь подсказать как искать это переход?