OllyDbg插件深入分析一 http://www.0xaa55.com/forum.php? ... tid=1133&extra=
第二章 OllyDbg1输出函数按功能分类
学习Windows编程需要熟悉WindowsAPI,同样地,熟悉OllyDbg插件编程也要熟悉OllyDbg API,下面是我的学习路线。 插件函数
intRegisterpluginclass(char *classname,char *iconname,HINSTANCE dllinst,WNDPROCclassproc);
voidUnregisterpluginclass(char *classname);
intPluginwriteinttoini(HINSTANCE dllinst,char *key,int value);
intPluginwritestringtoini(HINSTANCE dllinst,char *key,char *s);
intPluginreadintfromini(HINSTANCE dllinst,char *key,int def);
intPluginreadstringfromini(HINSTANCE dllinst,char *key,char *s,char *def);
intPluginsaverecord(ulong tag,ulong size,void *data);
intPlugingetvalue(int type);
t_statusGetstatus(void);
Registerpluginclass原型:intRegisterpluginclass(char *classname,char *iconname,HINSTANCE dllinst, WNDPROCclassproc) 功能:生成唯一的类名并注册为插件窗口。如果iconname为NULL则使用标准插件图标(字母P)
返回:成功时返回0并填充classname,失败时返回1
参数:
classname 指向大小大于32字符的缓冲区用于接收类名
iconname 插件DLL中图标资源名
dllinst 插件实例句柄
classproc 新类的窗口过程地址
注意:注册后窗口类有8个整数(32字节)的额外空间,插件可以自由使用第2到7个整数空间(偏移8到28用于GetWindowLong和SetWindowLong)。ODBG_Plugininit是执行该函数的最佳位置
Unregisterpluginclass原型:voidUnregisterpluginclass(char *classname) 功能:注销之前通过Registerpluginclass注册的窗口类
参数:
classname 函数Registerpluginclass返回的类名
注意:在ODBG_Plugindestroy中对所有注册的窗口类调用该函数
Pluginwriteinttoini原型:intPluginwriteinttoini(HINSTANCE dllinst,char *key,int value) 功能:将整数值绑定的键值对存储在ollydbg.ini的插件自定义区段中
返回:成功时返回1,失败时返回0
参数:
dllinst 插件实例句柄
key 整数相关的键名
value 要存储在ollydbg.ini的整数
Pluginwritestringtoini原型:intPluginwritestringtoini(HINSTANCE dllinst,char *key,char *s) 功能:将ASCII字符串绑定的键值对存储在ollydbg.ini的插件自定义区段中
返回:成功时返回1,失败时返回0
参数:
dllinst 插件实例句柄
key 字符串相关的键名
s 要存储在ollydbg.ini的字符串
Pluginreadintfromini原型:intPluginreadintfromini(HINSTANCE dllinst,char *key,int def) 功能:将整数值绑定的键值对从ollydbg.ini的插件自定义区段中读取出来
返回:成功时返回目标整数,失败时返回默认值
参数:
dllinst 插件实例句柄
key 整数相关的键名
def 默认值
Pluginreadstringfromini原型:intPluginreadstringfromini(HINSTANCE dllinst,char *key,char *s,char *def) 功能:将字符串绑定的键值对从ollydbg.ini的插件自定义区段中读取出来
返回:成功时返回目标字符串,失败时返回默认值
参数:
dllinst 插件实例句柄
key 字符串相关的键名
s 用于接收目标字符串
def 默认字符串,以零终止符结尾
Pluginsaverecord原型:intPluginsaverecord(ulong tag,ulong size,void *data) 功能:将一个记录写入.udd文件
返回:成功时返回1,失败时返回0
参数:
tag 插件唯一的标签
size 写入.udd文件的数据大小,最大USERLEN
data 写入.udd文件的数据缓冲区
注意:只能从ODBG_Pluginsaveudd中调用,否则会崩溃
Plugingetvalue原型:intPlugingetvalue(int type) 功能:返回OllyDbg多个设置和变量信息
参数:
type 要返回的设置或变量信息
type | 实际类型 | 含义 | VAL_HINST | HINST | 当前OllDbg实例句柄 | VAL_HWMAIN | HWND | OllyDbg主窗口句柄 | VAL_HWCLIENT | HWND | MDI用户窗口句柄 | VAL_NCOLORS | | 常见颜色数 | VAL_COLORS | COLORREF* | 常见颜色RGB值数组 | VAL_BRUSHES | HBRUSH* | 常见颜色画刷句柄数组 | VAL_PENS | HPEN* | 常见颜色画笔句柄数组 | VAL_NFONTS | | 常见字体数 | VAL_FONTS | HFONT* | 常见字体句柄数组 | VAL_FONTNAMES | Char** | 内部字体名称 | VAL_FONTWIDTHS | int* | 常见字体平均宽度 | VAL_FONTHEIGHTS | int* | 常见字体平均高度 | VAL_NFIXFONTS | | 固定字宽字体数 | VAL_DEFFONT | | 默认字体序号 | VAL_NSCHEMES | | 配色方案数 | VAL_SCHEMES | t_scheme* | 配色方案数组 | VAL_DEFSCHEME | | 默认配色方案数组 | VAL_DEFHSCROLL | | 默认水平滚动 | VAL_RESTOREWINDOWPOS | | 从.ini文件恢复窗口位置 | VAL_HPROCESS | HANDLE | 被调试进程句柄 | VAL_PROCESSID | | 被调试进程标志ID | VAL_HMAINTHREAD | HANDLE | 被调试进程主线程句柄 | VAL_MAINTHREADID | | 被调试进程主线程标志ID | VAL_MAINBASE | | 被调试进程主模块基址 | VAL_PROCESSNAME | char* | 被调试进程名 | VAL_EXEFILENAME | char* | 被调试程序文件名 | VAL_CURRENTDIR | char* | 被调试进程当前目录 | VAL_SYSTEDIR | char* | 系统目录 | VAL_DECODEANYIP | | 不依赖EIP解码寄存器 | VAL_PASCALSTRINGS | | 解码Pascal格式字符串 | VAL_ONLYASCII | | 只解码可打印ASCII字符 | VAL_DIACRITICALS | | 允许字符串有变音符号 | VAL_GLOBALSEARCH | | 从块的开始处搜索 | VAL_ALIGNEDSEARCH | | 逐项搜索 | VAL_SEARCHMARGIN | | 浮点搜索允许误差 | VAL_KEEPSELSIZE | | 保存16进制编辑中选择项数 | VAL_MMXDISPLAY | | 对话框MMX显示模式(0:16进制 1:有符号 2:无符号) | VAL_WINDOWFONT | | 对话框中使用窗口字体 | VAL_TABSTOPS | | 制表符大小 | VAL_MODULES | t_table* | 模块表(包括.EXE和.DLL) | VAL_MEMORY | t_table* | 分配内存块表 | VAL_THREADS | t_table* | 活动线程表 | VAL_BREAKPOINTS | t_table* | 激活断点表 | VAL_REFERENCES | t_table* | 查找到的参考信息表 | VAL_SOURCELIST | t_table* | 源文件表 | VAL_WATCHES | t_table* | 监视情况表 | VAL_CPUFEATURES | | CPUID返回的CPU特征位 | VAL_TRACEFILE | FILE* | 运行跟踪记录文件句柄 | VAL_ALIGNDIALOGS | | 对齐对话框 | VAL_CPUDASM | t_dump* | 获取CPU反汇编窗口描述符 | VAL_CPUDDUMP | t_dump* | 获取CPU内存窗口描述符 | VAL_CPUDSTACK | t_dump* | 获取CPU栈窗口描述符 | VAL_APIHELP | char* | 选择的API帮助文件名 | VAL_HARDBP | | 硬件断点是否激活 | VAL_PATCHES | t_table* | 补丁表 | VAL_HINTS | t_sorted* | 带分析提示的排序数据 |
说明:带有VAL_N***的type变量表示获取数量,需要先获取该数量才能知道对应的VAL_***数据数组有多大。例如FONT系列,首先要调用Plugingetvalue(VAL_NFONTS)获取字体个数,之后再调用VAL_FONTNAMES等获取数据数组,数组元素个数就是前一步获取的个数。t_table和t_dump也类似,t_table内含的重要数据是t_sorteddata,而t_sorted结构体自身包含了元素个数,因此读者可根据例1写出输出更完整信息的程序了。返回t_dump类型的函数大部分是窗口数据,从这个意义上插件可以做的和OllyDbg一模一样! Getstatus原型:t_statusGetstatus(void) 功能:返回被调试进程的当前状态(STAT_XXX)
返回:
STAT_NONE
| 未调试进程
| STAT_STOPPED
| 进程挂起
| STAT_EVENT
| 处理调试事件,进程暂停
| STAT_RUNNING
| 进程运行
| STAT_FINISHED
| 进程结束
| STAT_CLOSING
| 调用TerminateProcess()等待结果
|
例一
#include <windows.h> #include <stdio.h> #include "Plugin.h" #pragma comment(lib,"OllyDbg.lib") char classname[32]; HINSTANCE hinst=NULL; class log//用于实现插件日志记录——单例模式 { private: log() { try { if(!AllocConsole()) { MessageBox(NULL,"无法创建命令行窗口","错误",MB_OK); throw "错误"; } } catch(...) { exit(0); } }; ~log() { FreeConsole(); } public: static void v(char* tolog) { static log obj; HANDLEout=GetStdHandle(STD_OUTPUT_HANDLE); DWORDWriteNum; WriteConsole(out,tolog,strlen(tolog),&WriteNum,NULL); } }; BOOL WINAPI DllMain(HINSTANCE hi,DWORD reason,LPVOID reserved) {//DLL入口点 if (reason==DLL_PROCESS_ATTACH) { hinst=hi;//保存实例句柄供后面使用 } return 1; } LRESULT CALLBACK WndProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) { return DefWindowProc(hw,msg,wp,lp);//暂时不用这里,因此只写个空架子 } extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw,ulong *features) { chartemp[256]; sprintf(temp,"版本号:%d\n",ollydbgversion); log::v(temp); if(0 == Registerpluginclass(temp,NULL,hinst,WndProc)) { log::v("插件注册成功:"); log::v(temp); log::v("\n"); } else { log::v("插件注册失败\n"); } return 0; } extc int _export cdecl ODBG_Plugindata(char shortname[32]) {//用于插件菜单中显示插件名 strcpy(shortname,"sample1"); return PLUGIN_VERSION; } extc int _export cdecl ODBG_Pluginshortcut(int origin,int ctrl,int alt,int shift,int key,void *item) {//按下I键(代表Information)显示OLLYDBG和进程信息 if (key!='I') return 0; switch(Getstatus()) { case STAT_NONE: log::v("——————————————未调试进程——————————————\n"); break; case STAT_STOPPED: log::v("——————————————进程挂起——————————————\n"); break; case STAT_EVENT: log::v("——————————————进程暂停,处理调试事件——————————————\n"); break; case STAT_RUNNING: log::v("——————————————进程运行——————————————\n"); break; case STAT_FINISHED: log::v("——————————————进程结束——————————————\n"); break; case STAT_CLOSING: log::v("——————————————进程等待关闭——————————————\n"); break; } chartemp[256]; sprintf(temp,"OllyDbg实例句柄:0x%0x\n",Plugingetvalue(VAL_HINST)); log::v(temp); sprintf(temp,"OllyDbg主窗口句柄:0x%0x\n",Plugingetvalue(VAL_HWMAIN)); log::v(temp); sprintf(temp,"被调试进程句柄:0x%0x\n",Plugingetvalue(VAL_HPROCESS)); log::v(temp); sprintf(temp,"被调试进程ID:%d\n",Plugingetvalue(VAL_PROCESSID)); log::v(temp); sprintf(temp,"被调试进程主线程句柄:0x%0x\n",Plugingetvalue(VAL_HMAINTHREAD)); log::v(temp); sprintf(temp,"被调试进程主线程ID:%d\n",Plugingetvalue(VAL_MAINTHREADID)); log::v(temp); sprintf(temp,"被调试进程主模块基址:0x%0x\n",Plugingetvalue(VAL_MAINBASE)); log::v(temp); sprintf(temp,"被调试进程名:%s\n",Plugingetvalue(VAL_PROCESSNAME)); log::v(temp); sprintf(temp,"被调试进程文件名:%s\n",Plugingetvalue(VAL_EXEFILENAME)); log::v(temp); sprintf(temp,"被调试进程当前目录:0x%0x\n",Plugingetvalue(VAL_CURRENTDIR)); log::v(temp); sprintf(temp,"系统目录:%s\n",Plugingetvalue(VAL_SYSTEMDIR)); log::v(temp); sprintf(temp,"被调试进程主模块基址:0x%0x\n",Plugingetvalue(VAL_MAINBASE)); log::v(temp); };
说明:如第一章所述,用到的OllyDbg-API有Registerpluginclass,Plugingetvalue,Getstatus都要在Plugin.h重新定义。该代码为Windows DLL项目主文件,插件启动后会出现控制台窗口,在加载了进程后,在OllyDbg窗口激活时按下“I”键,会输出类似下面的数据:
版本号:110
插件注册成功:OT_PLUGIN_0000
——————————————进程挂起——————————————
OllyDbg实例句柄:0x400000
OllyDbg主窗口句柄:0x3f1c14
被调试进程句柄:0x3c0
被调试进程ID:6932
被调试进程主线程句柄:0x758
被调试进程主线程ID:10444
被调试进程主模块基址:0x1000000
被调试进程名:RC
被调试进程文件名:D:\Program Files (x86)\Microsoft Visual Studio\Common\MSDev98\
Bin\RC.EXE
被调试进程当前目录:0x4d5c84
系统目录:C:\windows\system32\
被调试进程主模块基址:0x1000000
file:///C:/Users/lichao/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg
本例中提供的log类极为有用,因此需要单独提取出来作为一个文件,后面例子只包含log.h即可,不再赘述 |