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库罢了,不过你需要按照官方的说明导出对应的函数

  1. 下载所需文件
  2. 编写对应的导出函数

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中的调用处

前面两处调用 00497a0300479b23最后都是调用到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插件编写的主要内容,如果未能解决你的问题,请参考以下文章

OD插件编写

华为OD机试题,用 Java 解找出符合要求的字符串子串问题 | 含代码编写思路

如何在Sublime Text中添加代码片段

sublime text3 增加代码片段(snipper)

VIM 代码片段插件 ultisnips 使用教程

2022&2023华为OD机试 - 单词反转 2(Python)