OD插件编写
Posted 不会写代码的丝丽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OD插件编写相关的知识,希望对你有一定的参考价值。
前言
本教程主旨用于编写一个OD插件修复部分问题或者解析反调试问题.
异常过滤器的反调试插件
当触发异常时首先会将异常交付给SEH
然后再交付到 SetUnhandledExceptionFilter
,不管是哪个阶段当存在调试器
时,首先会交付给调试器
,但是在SEH阶段调试器可以把错误交付给被调试程序,但是位于 SetUnhandledExceptionFilter
却不可以。因为我们需要修改一些系统判断逻辑从而实现调试。
首先给出一个Demo
,这个Demo使用了SetUnhandledExceptionFilter
机制进行了反调试,由于不是本文的重点所以不过多介绍。
.586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "MainWinClass",0
AppName db "Main Window",0
g_szCC db "这里有CC断点",0
g_szover db "程序结束",0
g_sz1 db "非法程序后的一个窗口",0
g_sz2 db "非法程序后的二个窗口",0
g_sz3 db "检测到单步",0
g_sz4 db "检测到返回",0
g_sz5 db "异常返回",0
g_sz6 db "测试",0
g_sz7 db "非法程序后的三个窗口",0
g_sz8 db 1
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
MyUnhandledExceptionFilter proc pExceptionInfo:ptr EXCEPTION_POINTERS
LOCAL @pContext:ptr PCONTEXT
LOCAL @pExpRecord:ptr PEXCEPTION_RECORD
mov ebx,pExceptionInfo
assume ebx:ptr EXCEPTION_POINTERS
push [ebx].ContextRecord
pop @pContext
push [ebx].pExceptionRecord
pop @pExpRecord
assume ebx:nothing
mov ebx,@pExpRecord
assume ebx:ptr EXCEPTION_RECORD
;跳过内存访问异常的指令
mov edx,@pContext
assume edx:ptr CONTEXT
mov ecx,0
.if [ebx].ExceptionCode == EXCEPTION_ACCESS_VIOLATION
add [edx].regEip,2
;设置单步
or [edx].regFlag,100h
invoke MessageBox,NULL,offset g_sz6,NULL,MB_OK
.elseif [ebx].ExceptionCode == EXCEPTION_SINGLE_STEP
;判断是否有cc
mov eax,[edx].regEip
.if byte ptr [eax] == 0cch
; ;说明有cc
invoke MessageBox,NULL,offset g_szCC,NULL,MB_OK
;检测到int3指令,证明被注入程序退出
mov eax,EXCEPTION_EXECUTE_HANDLER
ret
; .elseif byte ptr [eax] == 0C3h
; ;如果是ret,则不设置单步
; invoke MessageBox,NULL,offset g_sz4,NULL,MB_OK
.else
.if g_sz8==1
mov g_sz8,0
mov [edx].regEip,offset _SafePlace2
;设置单步tf标志位
or [edx].regFlag,100h
.endif
.endif
.endif
assume ebx:nothing
assume edx:nothing
mov eax,EXCEPTION_CONTINUE_EXECUTION
ret
MyUnhandledExceptionFilter endp
; ---------------------------------------------------------------------------
MyProtoctFun proc
mov eax,0ffffffffh
mov eax,[eax]
invoke MessageBox,NULL,offset g_sz1,NULL,MB_OK
mov eax,0
mov eax,0
mov eax,0
_SafePlace3:
invoke MessageBox,NULL,offset g_sz7,NULL,MB_OK
ret
MyProtoctFun endp
start:
invoke SetUnhandledExceptionFilter,offset MyUnhandledExceptionFilter
invoke MyProtoctFun
_SafePlace2:
invoke MessageBox,NULL,offset g_sz2,NULL,MB_OK
mov g_sz8,1
invoke MyProtoctFun
mov eax,0
mov eax,0
mov eax,0
invoke MessageBox,NULL,offset g_szover,NULL,MB_OK
end 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:none
include Plugin.Inc
include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc
includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib
.data
g_szPluginName db "myplugin",0
g_szformatmsg db "地址为%x",0
g_szKernelBase db "KernelBase",0
g_szUnhandledExceptionFilter db "UnhandledExceptionFilter",0
g_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:LPSTR
invoke crt_strcpy,shortname,offset g_szPluginName
mov eax,PLUGIN_VERSION
ret
ODBG_Plugindata endp
;extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw,
; ulong *features);
ODBG_Plugininit proc C ollydbgversion:dword,hw:dword,features:dword
LOCAL @hKernalbase:dword
invoke GetModuleHandle,offset g_szKernelBase
mov @hKernalbase,eax
invoke GetProcAddress,@hKernalbase,offset g_szUnhandledExceptionFilter
add eax,0bdh
mov g_dwFixAddr,eax
xor eax,eax
ret
ODBG_Plugininit endp
ODBG_Paused proc C reason:dword,reg:dword
LOCAL @btCode:byte
.if reason==PP_EVENT
invoke _Readmemory,addr @btCode,g_dwFixAddr,1,MM_RESILENT
.if @btCode != 90h
invoke crt_sprintf,offset g_szprint,offset g_szformatmsg,g_dwFixAddr
invoke MessageBox,NULL,offset g_szprint,NULL,MB_OK
invoke _Writememory,offset g_aryCodeNop,g_dwFixAddr,size g_aryCodeNop, MM_RESILENT or MM_DELANAL
.endif
.endif
mov eax,1
ret
ODBG_Paused endp
DllMain proc hinstDLL:HINSTANCE, fdwReason:DWORD,lpReserved:LPVOID
mov eax,TRUE
ret
DllMain endp
end DllMain
记得在编译的时候加上OLLYDBG.LIB
库即可,这个库位于第一步中的压缩包中。
OD 窗口过程函数bug
我们知道OD有一个可以检查出程序的窗口过程函数,我们当然也可以用Spy++去查看
OD截图:
SPY++截图
一切看起来都非常正常。
我们现在使用一个unicode程序呢?
OD:
对于unicode
程序发现OD
并没有正确获取到窗口过程函数
.
原因
由于OD获取窗口过程都使用多字节编码函数而不是根据目标语言选择因此存在问题。
我们首先看看GetClassLongA
在这个OD中的调用处
前面两处调用 00497a03
和00479b23
最后都是调用到004af420
,这里的原因是got和ptl重定向的原因,我们不需深究。
我们直接看004af420
处汇编
我们只需要替换50d858此处内容的地址即可完成。
MyGetClassLong proc hwnd:HWND,index:DWORD
invoke IsWindowUnicode,hwnd
.if eax ==TRUE
invoke GetClassLongW,hwnd,index
ret
.endif
invoke GetClassLongA,hwnd,index
ret
MyGetClassLong endp
ODBG_Plugininit proc C ollydbgversion:dword,hw:dword,features:dword
LOCAL @pGetAddr:dword
LOCAL @dwOlddprotect:dword
mov @pGetAddr,0050D858h
invoke VirtualProtect,@pGetAddr,1,PAGE_EXECUTE_READWRITE,addr @dwOlddprotect
mov eax,@pGetAddr
mov dword ptr [eax],offset MyGetClassLong
invoke VirtualProtect,@pGetAddr,1,@dwOlddprotect,addr @dwOlddprotect
xor eax,eax
ret
ODBG_Plugininit endp
以上是关于OD插件编写的主要内容,如果未能解决你的问题,请参考以下文章