OD插件编写
前言
本教程主旨用于編寫一個OD插件修復部分問題或者解析反調試問題.
官方文檔
異常過濾器的反調試插件
當觸發異常時首先會將異常交付給SEH然后再交付到 SetUnhandledExceptionFilter,不管是哪個階段當存在調試器時,首先會交付給調試器,但是在SEH階段調試器可以把錯誤交付給被調試程序,但是位于 SetUnhandledExceptionFilter卻不可以。因為我們需要修改一些系統判斷邏輯從而實現調試。
首先給出一個Demo,這個Demo使用了SetUnhandledExceptionFilter機制進行了反調試,由于不是本文的重點所以不過多介紹。
.586 .model flat,stdcall option casemap:noneinclude windows.incinclude user32.incinclude kernel32.incincludelib user32.libincludelib kernel32.libWinMain proto :DWORD,:DWORD,:DWORD,:DWORD.dataClassName db "MainWinClass",0AppName db "Main Window",0g_szCC db "這里有CC斷點",0g_szover db "程序結束",0g_sz1 db "非法程序后的一個窗口",0g_sz2 db "非法程序后的二個窗口",0g_sz3 db "檢測到單步",0g_sz4 db "檢測到返回",0g_sz5 db "異常返回",0g_sz6 db "測試",0g_sz7 db "非法程序后的三個窗口",0g_sz8 db 1 .data?hInstance HINSTANCE ?CommandLine LPSTR ?.codeMyUnhandledExceptionFilter proc pExceptionInfo:ptr EXCEPTION_POINTERSLOCAL @pContext:ptr PCONTEXTLOCAL @pExpRecord:ptr PEXCEPTION_RECORDmov ebx,pExceptionInfoassume ebx:ptr EXCEPTION_POINTERSpush [ebx].ContextRecordpop @pContextpush [ebx].pExceptionRecordpop @pExpRecordassume ebx:nothingmov ebx,@pExpRecordassume ebx:ptr EXCEPTION_RECORD;跳過內存訪問異常的指令mov edx,@pContextassume edx:ptr CONTEXTmov ecx,0.if [ebx].ExceptionCode == EXCEPTION_ACCESS_VIOLATIONadd [edx].regEip,2;設置單步or [edx].regFlag,100hinvoke MessageBox,NULL,offset g_sz6,NULL,MB_OK.elseif [ebx].ExceptionCode == EXCEPTION_SINGLE_STEP;判斷是否有ccmov eax,[edx].regEip.if byte ptr [eax] == 0cch; ;說明有ccinvoke MessageBox,NULL,offset g_szCC,NULL,MB_OK;檢測到int3指令,證明被注入程序退出mov eax,EXCEPTION_EXECUTE_HANDLERret; .elseif byte ptr [eax] == 0C3h; ;如果是ret,則不設置單步; invoke MessageBox,NULL,offset g_sz4,NULL,MB_OK.else.if g_sz8==1mov g_sz8,0mov [edx].regEip,offset _SafePlace2;設置單步tf標志位or [edx].regFlag,100h.endif.endif.endifassume ebx:nothingassume edx:nothingmov eax,EXCEPTION_CONTINUE_EXECUTIONretMyUnhandledExceptionFilter endp; ---------------------------------------------------------------------------MyProtoctFun procmov eax,0ffffffffhmov eax,[eax]invoke MessageBox,NULL,offset g_sz1,NULL,MB_OKmov eax,0mov eax,0mov eax,0_SafePlace3: invoke MessageBox,NULL,offset g_sz7,NULL,MB_OKretMyProtoctFun endpstart:invoke SetUnhandledExceptionFilter,offset MyUnhandledExceptionFilterinvoke MyProtoctFun_SafePlace2: invoke MessageBox,NULL,offset g_sz2,NULL,MB_OKmov g_sz8,1invoke MyProtoctFunmov eax,0mov eax,0mov eax,0invoke MessageBox,NULL,offset g_szover,NULL,MB_OKend start我們需要提前理解一個知識點,(異常過濾器階段)當觸發異常時系統會調用UnhandledExceptionFilter函數在這個函數內部中又會調用ZwQueryInformationProcess函數判斷是否被調試。
ZwQueryInformationProcess 函數文檔鏈接
我們的思路如下:
修改jnz 7732E179這個指令為NOP即可完成任務,在默認情況如果被調試這個指令會被執行跳轉。
在捋清楚思路之后我們就開始編寫對應的OD插件把.這個插件您可以用c語言編寫也可以用匯編編寫可以根據您的個人習慣。
若為的OD插件只不過是一個dll庫罷了,不過你需要按照官方的說明導出對應的函數
OD提供了多個導出函數這里我們舉例本例子用到的幾個
_ODBG_Plugininit導出函數用于被od加載時回調,你可以在這個函數里面做初始化操作,如果初始化成功返回0
ODBG_Plugindata用于告訴OD這個插件支持的版本
ODBG_Paused OD被執行暫停的時候回調,會傳入被暫停的原因
我們這里給出一個匯編版本
;odDllPlugin.Def EXPORTS_ODBG_Plugindata=ODBG_Plugindata_ODBG_Plugininit=ODBG_Plugininit_ODBG_Paused=ODBG_Paused;Plugin.Inc PLUGIN_VERSION equ 110 PP_MAIN equ 0003h PP_EVENT equ 0000h PP_PAUSE equ 0001h PP_TERMINATED equ 0002h MM_RESTORE equ 01h MM_SILENT equ 02h MM_DELANAL equ 04h MM_RESILENT equ (MM_RESTORE or MM_SILENT)_Readmemory proto C :ptr, :dword,:dword, :dword _Writememory proto C :ptr, :dword,:dword, :dword最重要的實現文件
.586 .model flat,stdcall option casemap:noneinclude Plugin.Incinclude windows.incinclude user32.incinclude kernel32.incinclude msvcrt.incincludelib user32.libincludelib kernel32.libincludelib msvcrt.lib.datag_szPluginName db "myplugin",0g_szformatmsg db "地址為%x",0g_szKernelBase db "KernelBase",0 g_szUnhandledExceptionFilter db "UnhandledExceptionFilter",0g_aryCodeNop db 6 dup(90h)g_szprint db 100 dup(0h)g_dwFixAddr dd 0 .code;int _export cdecl ODBG_Plugindata(char shortname[32]);ODBG_Plugindata proc C shortname:LPSTRinvoke crt_strcpy,shortname,offset g_szPluginNamemov eax,PLUGIN_VERSIONretODBG_Plugindata endp;extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw, ; ulong *features);ODBG_Plugininit proc C ollydbgversion:dword,hw:dword,features:dwordLOCAL @hKernalbase:dwordinvoke GetModuleHandle,offset g_szKernelBasemov @hKernalbase,eaxinvoke GetProcAddress,@hKernalbase,offset g_szUnhandledExceptionFilteradd eax,0bdhmov g_dwFixAddr,eaxxor eax,eaxretODBG_Plugininit endpODBG_Paused proc C reason:dword,reg:dwordLOCAL @btCode:byte.if reason==PP_EVENTinvoke _Readmemory,addr @btCode,g_dwFixAddr,1,MM_RESILENT.if @btCode != 90hinvoke crt_sprintf,offset g_szprint,offset g_szformatmsg,g_dwFixAddrinvoke MessageBox,NULL,offset g_szprint,NULL,MB_OKinvoke _Writememory,offset g_aryCodeNop,g_dwFixAddr,size g_aryCodeNop, MM_RESILENT or MM_DELANAL.endif.endifmov eax,1ret ODBG_Paused endpDllMain proc hinstDLL:HINSTANCE, fdwReason:DWORD,lpReserved:LPVOIDmov eax,TRUEret DllMain endpend DllMain記得在編譯的時候加上OLLYDBG.LIB庫即可,這個庫位于第一步中的壓縮包中。
OD 窗口過程函數bug
我們知道OD有一個可以檢查出程序的窗口過程函數,我們當然也可以用Spy++去查看
OD截圖:
SPY++截圖
一切看起來都非常正常。
我們現在使用一個unicode程序呢?
OD:
對于unicode程序發現OD并沒有正確獲取到窗口過程函數.
原因
由于OD獲取窗口過程都使用多字節編碼函數而不是根據目標語言選擇因此存在問題。
我們首先看看GetClassLongA在這個OD中的調用處
前面兩處調用 00497a03和00479b23最后都是調用到004af420,這里的原因是got和ptl重定向的原因,我們不需深究。
我們直接看004af420處匯編
我們只需要替換50d858此處內容的地址即可完成。
總結
- 上一篇: AE 2021最新最全插件滤镜大全一键安
- 下一篇: python 拼音 四线格_拼音四线三格