Ollydbg插件菜单管理插件 vOllyMenuMgr 1.0 by cexer
1.插件说明:
Ollydbg 插件装多了之后,右键菜单变得越来越长,要在其中寻找某个功能越来越不方便,其实其中有很多根本在右键中不会使用的功能,所以写了这个插件,只启用那些需要的菜单。
2.使用方法:
2.1 文件安装
将以下三个文件,复制到 Ollydbg 的 plugin 目录之后重启 Ollydbg:
vOllyMenuMgr.dll 插件核心程序
vOllyMenuMgr.xml 插件配置文件
vOllyMenuMgr_readme.txt 插件的帮助文件,即本文件
2.2 菜单说明
安装成功之后,主菜单会出现 vOllyMenuMgr 菜单,其中子菜单功能如下:
Settings 打开配置界面。
Reload 手动修改配置文件之后,需要调用这个重新载入 vOllyMenuMgr.xml。
Console 打开或关闭控制台窗口,方便观察菜单调用情况。
Help 用默认文本编辑器打开本帮助文件。
About 显示本插件关于窗口。
3.配置方法
可以在菜单中选择 vOllyMenuMgr.dll->Settings 进行配置,也可手动配置,方法如下:
在 vOllyMenuMgr.xml 中配置为启用的插件菜单才会弹出,配置格式:
在 "vOllyMenuMgr/MenuConfig" 结点/修改:
<Plugin name="插件名称">
<菜单类型1>启用标志</菜单类型1>
<菜单类型2>启用标志</菜单类型2>
<菜单类型3>启用标志</菜单类型3>
</Plugin>
3.1 插件名称
插件名称即 plugins 主菜单下显示的名称(去掉前面的序号)。
3.2 启用标志
启用标记使用0或1
1 启用
0 禁用
未在配置文件中配置的插件默认为禁用。
3.3 菜单类型
菜单类型为 Ollydbg 定义的菜单类型
官方文档包含的,调试中确认会调用的菜单:
名称 类型ID 类型说明
PM_MAIN 0 主菜单栏下的菜单
PM_DUMP 10 任意Dump窗口菜单
PM_MODULES 11 模块窗口菜单
PM_MEMORY 12 内存窗口菜单
PM_THREADS 13 线程窗口菜单
PM_BREAKPOINTS 14 断点窗口菜单
PM_REFERENCES 15 参考窗口菜单
PM_RTRACE 16 运行跟踪窗口菜单
PM_WATCHES 17 监视窗口菜单
PM_WINDOWS 18 窗口列表菜单
PM_DISASM 31 反汇编窗口右键菜单
PM_CPUDUMP 32 CPU Dump窗口菜单
PM_CPUSTACK 33 CPU栈右键菜单
未在官方文档包含的,调试中发现的会调用的菜单:
名称 类型ID 类型说明
PM_HANDLES 21 句柄窗口菜单
PM_PATCHES 20 Ollydbg自身修改的代码菜单
调试中发现比较意外,不会调用菜单的窗口:
名称 类型ID 类型说明
- - 日志窗口
PM_CPUREGS 34 寄存器窗口
3.4 注意:
未在配置文件中配置的插件默认为禁用。
4 实现原理
第一步 先定位菜单函数的调用位置
第二步 将该位置的代码Hook,调用自定义函数
第三步 在自定义函数中根据配置文件禁用、启用菜单
4.1 定位菜单函数的调用位置
用VC自己随便写个插件DLL,其中实现 ODBG_Plugininit 函数,在这个函数开头加个断点,然后将插件放到 Ollydbg 的 plugin 目录,使用 Ollydbg 作为宿主程序,在VC中启动调试。函数断下之后,VC切到反汇编模式,单步执行完本函数,可以回到 Ollydbg 中调用插件ODBG_Plugininit 的地方。
经过反复测试,发现只有1个地方调用 ODBG_Plugininit:
004969BA |. 890424 |mov dword ptr [esp], eax
004969BD |. 54 |push esp
004969BE |. 8B15 7C3B4D00 |mov edx, dword ptr [4D3B7C]
004969C4 |. 52 |push edx
004969C5 |. 6A 6E |push 6E
004969C7 |. FFD7 |call edi ; ODBG_Plugininit
在 Ollydbg 中打开另一个 Ollydbg 进行调试,定位到这个函数中,一路读下去可以理出:
插件信息数组,保存在 0x004F0AB4
插件总数,保存在 0x004F55B4
插件信息的结构:
#pragma pack(push,1)
struct vOllyPlugin
{
BYTE unkownbytes0[4];
char filename[0x104]; // @ 0x4
char pluginname[0x20]; // @ 0x108
BYTE unkownbytes1[0x108]; // @ 0x128
TODBG_Pluginmainloop fmainloop; // @ 0x230
TODBG_Pluginmenu fmenu; // @ 0x234
TODBG_Pluginaction factoin; // @ 0x238
TODBG_Pluginshortcut fshortcut; // @ 0x23C
TODBG_Pluginsaveudd fsaveudd; // @ 0x240
TODBG_Pluginuddrecord fuddrecord; // @ 0x244
TODBG_Pluginreset freset; // @ 0x248
TODBG_Paused fpaused; // @ 0x24C
TODBG_Pausedex fpausedex; // @ 0x250
TODBG_Plugincmd fcmd; // @ 0x254
};
#pragma pack(pop)
其中的函数类型,根据插件SDK文档确定:
typedef int (cdecl *TODBG_Plugininit)( int ollydbgversion, HWND hw, ulong *features );
typedef int (cdecl *TODBG_Plugindata)( char *shortname );
typedef void (cdecl *TODBG_Pluginaction)( int origin, int action, void *item );
typedef void (cdecl *TODBG_Pluginmainloop)( DEBUG_EVENT *debugevent );
typedef int (cdecl *TODBG_Pluginshortcut)( int origin, int ctrl, int alt, int shift, int key, void *item );
typedef void (cdecl *TODBG_Pluginsaveudd)( t_module *pmod, int ismainmodule );
typedef int (cdecl *TODBG_Pluginuddrecord)( t_module *pmod, int ismainmodule, ulong tag, ulong size, void *data );
typedef void (cdecl *TODBG_Pluginreset)();
typedef int (cdecl *TODBG_Paused)( int reason, t_reg *reg );
typedef int (cdecl *TODBG_Pausedex)( int reason, int extdata, t_reg *reg, DEBUG_EVENT *debugevent );
typedef int (cdecl *TODBG_Plugincmd)( int reason, t_reg *reg, char *cmd );
typedef int (cdecl *TODBG_Pluginmenu)( int origin, char data[4096], void *item );
根据这些信息,可以定位到任意插件的 ODBG_Pluginmenu 的地址,随便选择一个,在这个地址上另硬件访问断点,可以定位到所有调用菜单函数的地方。经过反复测试,发现只有两个地方调用菜单函数:
00496A19 |. FF93 34020000 |call dword ptr [ebx+234]
00496553 |. FF96 34020000 |call dword ptr [esi+234]
其中 00496A19 位于刚才发现的插件加载函数中,调用参数中的 origin 总是 PM_MAIN,可以确定这儿是在将插件菜单添加到主菜单(并且后面经过确认,主菜单只在此调用一次),因此要 Hook 的位置是 00496553,这里的 esi 包含了当前插件的信息首地址(对应 vOllydbgPlugin 结构)。
4.2 Hook 菜单调用函数
首先按插件SDK的要求,写一个 Hook 函数:
int cdecl replacement_of_0x96553( int origin, char data[4096], void *item )
{
// 取得插件信息结构地址
vOllyPlugin* plugin = NULL;
__asm { MOV plugin, esi }
// 调用自定义函数
return vOllyMenuMgr_plugin_menu( plugin, origin, data, item );
}
然后在 ODBG_Plugininit 中 Hook 00496553:
bool hook_0x96553()
{
base_address = (unsigned char*)GetModuleHandleA( NULL );
plugin_arary = (vOllyPlugin*)( base_address + 0xF0AB4 );
plugin_count = (int*)( base_address + 0xF55B4 );
hook_address = m_base_address + 0x96553;
hook_succeeded = false;
unsigned char* hook = (unsigned char*)&replacement_of_0x96553;
const char* code_old = "\xFF\x96\x34\x02\x00\x00";
DWORD protect_old = 0;
VirtualProtect( hook_address, 6, PAGE_EXECUTE_READWRITE, &protect_old );
if( memcmp( hook_address, code_old, 6 ) == 0 )
{
unsigned char* call = hook_address;
*call = 0xE8; call += 1;
*( (ptrdiff_t*)call ) = hook - call - 4; call += 4;
*call = 0x90; call += 1;
hook_succeeded = true;
}
VirtualProtect( hook_address, 6, protect_old, &protect_old );
return hook_succeeded;
}
extc int _export cdecl ODBG_Plugininit( int ollydbgversion, HWND hw, ulong * features )
{
if( hook_0x96553() )
// ...
else
// ...
}
还得在 ODBG_Plugindestroy 中完成 Unhook:
bool unhook_0x96553()
{
// 这里最好是判断字节比较安全,以防该处代码,又被其它程序修改了。
if( hook_succeeded )
{
unsigned char* code = (unsigned char*)hook_address;
const char* old_code = "\xFF\x96\x34\x02\x00\x00";
DWORD protect_old = 0;
VirtualProtect( code, 6, PAGE_EXECUTE_READWRITE, &protect_old );
vmemcpy( code, old_code, 6 );
VirtualProtect( code, 6, protect_old, &protect_old );
hook_succeeded = false;
}
}
extc void _export cdecl ODBG_Plugindestroy()
{
if( unhook_0x96553() )
// ...
else
// ...
}
5.3 在自宝岛函数中根据配置文件进行菜单调用
然后在 vOllyMenuMgr_plugin_menu 中,就可以根据配置,做对菜单进行选择性的调用了。
4 BUG
此程序未经过长期测试,使用中有问题请自行研究,或者:
发送邮件到cexer@qq.com,或者看雪(www.pediy.com)论坛联系 andy00
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)