OD的bug
OD的异常bug
当程序产生了异常后,操作系统在派发异常时,首先会将异常给调试器,但是在调试过程中,OD并不能成功收到异常,需要手动对 kernel32.dll
中的 UnhandledExceptionFilter
打补丁才能解决这个问题或者使用 SharpOD
插件。
手动修复Bug
手动修改流程,使其可以执行下面的函数调用。
Win10环境下
将 UnhandledExceptionFilter
API中偏移为 C9
的位置修改为 xor eax,eax
Win7环境下
定位到 kernel32.dll
中的 UnhandledExceptionFilter
中的偏移为 54
的位置,将下图中的 test eax,eax
改为 xor eax,eax
即可避免其在分发的时候,跳过后面的大段代码。
Xp环境下
定位到 kernel32.dll
中的 UnhandledExceptionFilter
中的偏移为 8F
的位置,将 test eax,eax
后面的 jl
改成 jmp
,可以修复这个bug。
08_2
OD窗口回调函数显示bug
OD可以遍历被调试进程的所有窗口,并显示窗口的的相关信息。
一般分析窗口时需要找到窗口对应的回调函数,在里面进行分析。
OD加载进程 查看->窗口
打开窗口后,窗口回调函数的地址显示不正常,超过 80000000
为操作系统的地址空间。
分析BUG
分析OD会通过 GetClassLong
来获取窗口信息,用OD加载OD后, Alt+E
,选中主模块,右键菜单 查看名称
,首先在 GetClassLong
的每个参考上设置断点。
用spy++查看计算器的窗口信息
在被调试的OD中 查看->窗口
,在右键菜单中,选择刷新,此时OD调试器的断点断下
查看堆栈窗口的信息,发现获取的为计算器的窗口,返回到调用处,在调用处下断点,断下后F8单步步过,查看返回值。
发现其返回值错误。
分析原因
窗口分ASCII版本和UNICODE版本,而OD中只使用了 GetClassLongA
所以遇到UNICODE版本的程序就没有办法获取成功了,而计算器的窗口为Unicode版本的。
解决方案
自己写一个判断程序,判断要打开的窗口是UNICODE的版本还是ASCII版本
然后修改跳转表中对应位置 50D858
的值,为正确的 GetWindowLong
API的地址。
编写OD插件
插件是以动态链接库形式存在,导出必要函数后,放入plugin文件夹下即可。
OD在启动时,会遍历plugin目录下的所有dll,当DLL中导出了
ODBG_Plugindata
和ODBG_Pluginit
函数后,才会被当作插件来加载
通过查看OD插件帮助文档来调用对应的接口。
intODBG_Plugindata(char*shortname);
//[OUT]shortname最长为32位字符串的指针为插件名称,显示在插件菜单中。intODBG_Plugininit(intollydbgversion,HWND hw,ulong*features);
//必须回调函数。在一个有效的 OllyDbg 插件中必须使用它,你可以将所有需要初始化和分配的资源放在这个函数里。如果启动成功,函数必须返回 0,发生错误返回 -1,那样插件就会卸载,参数 ollydbgversion 是插件可以兼容的OllyDbg的版本号。intODBG_Paused(intreason,t_reg*reg)
//选回调函数。OllyDbg 在被调试程序暂停时或一个内部进程完成时调用本函数。
未设置字符数据类型错误
在头文件中有说明。
在编译选项中添加 /J
选项即可解决。
在Plugin.h头文件中,定义了常用的导出函数,不需要手动导出。
有可能会和已经安装的别的插件发生冲突,测试时只保留我们的插件来进行测试。
使用 ODBG_Paused
时进行修改。
// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include "Plugin.h"#pragma comment(lib,"Ollydbg.lib")//包含lib文件int ODBG_Plugindata(char* shortname){ strcpy(shortname, "flag0Plugin"); return PLUGIN_VERSION;}int ODBG_Paused(int reason, t_reg* reg){ if (reason == PP_EVENT ) { //1. 获取kernel32.UnhandleExceptionFilter的地址 HMODULE hModKer = GetModuleHandle("Kernel32"); PBYTE pUEFAddr = (PBYTE)GetProcAddress(hModKer, "UnhandledExceptionFilter"); //2. 定位jl xxxx的地址 PBYTE pJlAddr = pUEFAddr + 0x8F; //3. 将jl修改为jmp //E9 00000000 //90 char aryCode[] = { '\xe9', '\xa3','\x00','\x00' ,'\x00' ,'\x00','\x90' }; //修改被调试进程的内存 Writememory(aryCode, (ulong)pJlAddr, sizeof(aryCode), MM_SILENT); } return 1;//允许调用go}DWORD WINAPI MyGetClassLong(HWND hWnd, int nIndex){ if (IsWindowUnicode(hWnd)) { return GetClassLongW(hWnd, nIndex); } else { return GetClassLongA(hWnd, nIndex); }}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features){ //修改OD自身 DWORD dwOldprotect = 0; PBYTE pOldAddr = (PBYTE)0x50D858; VirtualProtect(pOldAddr, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &dwOldprotect); *(PDWORD)pOldAddr = (DWORD)MyGetClassLong; VirtualProtect(pOldAddr, sizeof(DWORD), dwOldprotect, &dwOldprotect); return 0;}BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ return TRUE;}
如何定位OD中API的位置
通过
LoadLibrary
,GetProcAddress
,来定位。通过查找特征码。
特征码:是一段唯一的二进制数值,通过遍历模块的内存对比的方式来定位函数。
使用特征码的场景,是应用于函数没有导出的情况。
OD 脚本和插件的区别
OD脚本可以直接在OD上跑,每次都要手动执行,插件是由OD启动时自动加载的用于增强功能的,脚本用来自动化,来解决重复劳动。
X64dbg
的脚本可以python编写。