OllyDbg完全教程 自定义函数描述[Custom function descriptions]

来源:互联网 发布:北海道有哪些大学知乎 编辑:程序博客网 时间:2024/04/29 06:29
自定义函数描述[Custom function descriptions]

概论[Introduction]

OllyDbg包含(做为内部资源)1900多种标准函数以及400多种标准C函数的名称和参数。分析器[Analyzer] 用这些描述使被调试程序更加易懂。比较下面一个例子,分析器的函数CreateFont:


PUSH OT.00469F2A ; ASCII "Times New Roman"
PUSH 12
PUSH 2
PUSH 0
PUSH 0
PUSH 0
PUSH 0
PUSH 0
MOV EAX,DWORD PTR [49FA70]
PUSH EAX
PUSH 190
PUSH 0

PUSH 0
PUSH 0
PUSH 10
CALL <JMP.&GDI32.CreateFontA>

这是分析后的:


MOV EAX,DWORD PTR [49FA70]
PUSH OT.00469F2A ; ?FaceName = "Times New Roman"
PUSH 12 ; ?PitchAndFamily = VARIABLE_PITCH|FF_ROMAN
PUSH 2 ; ?Quality = PROOF_QUALITY
PUSH 0 ; ?ClipPrecision = CLIP_DEFAULT_PRECIS
PUSH 0 ; ?OutputPrecision = OUT_DEFAULT_PRECIS
PUSH 0 ; ?CharSet = ANSI_CHARSET

PUSH 0 ; ?StrikeOut = FALSE
PUSH 0 ; ?Underline = FALSE
PUSH EAX ; ?Italic => TRUE
PUSH 190 ; ?Weight = FW_NORMAL
PUSH 0 ; ?Orientation = 0
PUSH 0 ; ?Escapement = 0
PUSH 0 ; ?Width = 0
PUSH 10 ; ?Height = 10 (16.)

CALL <JMP.&GDI32.CreateFontA> ; ?CreateFontA

显然,后面的代码更容易理解。API函数CreateFont 有14个参数。分析器标记所有这些参数的名称并解码他们的值。如果寄存器跟踪开启,那么分析器同时会解码参数Italic
的值为地址49FA70处双字长的内容。解码使用参数的真实值,所以如果[49FA70]里的内容改变了,那么参数Italic的值也会随之改变。当EIP指向跳转或调用该函数的命令,或指向入口时,OllyDbg也会在栈中对已知函数的参数进行解码。

OllyDbg可以对像printf()这样参数个数可变的函数进行参数解码:

PUSH EAX ; ?<%.*s>
PUSH E8 ; ?<*> = E8 (232.)
PUSH EBX ; ?<%08X>
PUSH Mymodule.004801D2 ; ?format = "Size %08X (%.*s) bytes"
PUSH ESI ; ?s
CALL Mymodule.sprintf ; ?sprintf

您可以定义自己的函数。每次您打开某个应用程序时,OllyDbg都会重新设置函数参数表并用内嵌描述添充这个表。然后尝试打开文件“<OllyDbg目录>/common.arg”和“<OllyDbg目录>/<应用程序名>.arg”,这里<应用程序名>使用8.3格式(DOS)被调试程序文件名(不带路径和扩展名)。

下面看一个简单的.arg文件实例:


INFO Simple .ARG file that decodes CreateHatchBrush
TYPE HS_X
IF 0 "HS_HORIZONTAL"
IF 1 "HS_VERTICAL"
IF 2 "HS_FDIAGONAL"
IF 3 "HS_BDIAGONAL"
IF 4 "HS_CROSS"
IF 5 "HS_DIAGCROSS"
ELSEINT
END
TYPE COLORREF
IF 0 "<BLACK>"
IF 00FFFFFF "<WHITE>"
OTHERWISE
TEXT "RGB("
FIELD 000000FF
UINT
TEXT ","

FIELD 0000FF00
UINT
TEXT ","
FIELD 00FF0000
UINT
TEXT ")"
END
STDFUNC CreateHatchBrush
"style" HS_X
"colorref" COLORREF
END

标准Windos API函数CreateHatchBrush(int style,int colorref) 有两个参数。第一个必须是阴影风格[hatch style],第二个是常量由红色、绿色、蓝色组成,并用一个32
位整数的低三字节表示。为了解码这些参数,文件定义了两个新的参数类型:HS_X 和 COLORREF。

阴影风格是一个简单的枚举类型,如0表示HS_HORIZONTAL(水平风格)、1表示HS_VERTICAL(垂直风格)。IF关键字比较参数与第一个操作数(注意:其总是十六进制的),如果相同则显示第二个操作数里的文本。但万一匹配失败会如何?关键字ELSEINT 会然OllyDbg会将参数解释为一个整数。

COLORREF 更复杂一些。首先尝试解码两个广泛使用的颜色值:黑(全0组成)与白(全0xFF组成)。如果匹配失败,COLORREF尝试解码颜色为一个结构包含红、绿、蓝的亮度。FIELD会用第一个操作数与参数进行逻辑与操作。然后转换结果为整数,并同时按位右移第一个操作及该整数,直到第一个操作数的二进制个位数字为1,这时整数按位右移的结果以无符号10进制显示出来。这个例子做了三次这样的操作,以分离出每个颜色成份。TEXT关键字用于无条件显示文本。如果参数为00030201,那么
COLORREF将其解码为RGB(1.,2.,3.)。

大多断API函数都会从栈中移除参数并保护寄存器EBX, EBP, ESI 和 EDI。声明这样的函数为STDFUNC,以告诉分析器该函数做了这样的事情。否则请其描述为FUNCTION


万一某个参数由多个域及比特值组成,比如上面提到的fdwPitchAndFamily ,我们该怎么办?请看下面这个例子:


TYPE FF_PITCH
MASK 03
IF 00 "DEFAULT_PITCH"
IF 01 "FIXED_PITCH"
IF 02 "VARIABLE_PITCH"
ELSEHEX
TEXT "|"
MASK 0C
BIT 04 "4|"
BIT 08 "8|"
MASK FFFFFFF0
IF 00 "FF_DONTCARE"
IF 10 "FF_ROMAN"
IF 20 "FF_SWISS"
IF 30 "FF_MODERN"
IF 40 "FF_SCRIPT"
IF 50 "FF_DECORATIVE"
ELSEHEX
END

前两个比特位(第0和等1位)表示倾斜度,必须一起解码。我们使用 MASK 03 来提取这两个比特并通过IF序列来解码。增加了连接符“|”,分别提取第2和第3个比特位,并分别单独解码。最后提取剩余部分并进行解码。

OllyDbg 会移除生成串尾部的连接符“|”、空格、冒号、逗号、分号和等号。

目前版本的分析仅能够解码32位参数。如您不能解码双精度浮点或长双精度浮点的函数参数。


格式描述

自定义解码信息由函数描述和类型描述两部分组成。函数描述部分非常的简单:


FUNCTION|STDFUNC [模块名]函数名
<第一个参数的名称> <第一个参数的类型>
……
<最后一个参数的名称> <最后一个参数的类型>
END

如果函数从栈中移除参数并保护寄存器EBX, EBP, ESI 和 EDI,请使用关键字STDFUNC。大多少函数都遵循这样的规则。其他情况则声明为FUNCTION。模块(EXE 或 DLL)名是可选的。如果模块名被忽略,OllyDbg会对尝试匹配任何模块。模块名不区分大小写。

函数名称总是区分大小写的。有针对UNICODE的函数必须使用后缀 A 或 W 加以区分,比如SetWindowTextA.。

参数的顺序又C风格的参数使用惯例一致。而16位Windows和32位API函数也是按惯例使用。如果参数名由多个字组成,或者包含特殊字符,那么请将其用两个单引号引起来。与在C语言中一样,省略号(叄┦且桓鎏厥獾募锹加糜诒硎静问靠杀洹K匦朐诤枋龅淖詈蟆llyDbg不会尝试解码这样的参数。如果函数的参数为空,则按functionname(void)对待

OllyDbg 仅支持32位的参数。某些参数已经预定义好了:


INT 以十六进制和带符号整数两种格式显示值
UINT 以十六进制和无符号整数两种格式显示值
HEX 以十六进制格式显示值
BOOL TRUE 或 FALSE
CHAR ASCII 字符
WCHAR UNICODE 字符
FLOAT 32位浮点数
ERRCODE 系统错误代码(像由函数GetLastError()报告的)
ADDR, PTR 地址(特殊情况:NULL)
ASCII ASCII 串指针
UNICODE UNICODE 串指针
FORMAT 在类似函数printf()(不包括wscanfW()!)使用的 ASCII 格式串
WFORMAT 类似函数wsprintfW()(不包括scanf()!)使用的 UNICODE 格式串
RECT RECT(矩形)结构指针
MESSAGE MSG(ASCII 窗口消息)结构指针
WMESSAGE MSG(UNICODE 窗口消息)结构指针
HANDLE 句柄(特殊情况:NULL, ERROR_INVALID_HANDLE)
HWND 窗口句柄
HMODULE 模块句柄
RSRC_STRING 带索引的资源串
NULL, DUMMY 有参数,但解码时跳过了
您不能重定义预定义类型。自定义类型允许您将参数分离成几个域并分别解码。类型描述有以下几种格式:


TYPE 类型名
[TEXT "任何文本"]
[<域选择器>]
<域解码>
<域解码>
[TEXT "任何文本"]
[PURGE]
...
<域选择器>
<域解码>
<域解码>
[TEXT "任何文本"]
END

类型名的程度限制在16个字符以内。 OllyDbg会无条件将"任何文本"作为生成的解码。域选择器提取一部分参数用于解码。以下域选择器,可以用于提取域:


MASK 十六进制掩码 - 域等于参数同十六进制掩码按位与(AND)的结果。

FIELD 十六进制掩码 - 参数同十六进制掩码按位与(AND)的数值,然后OllyDbg同时按位右移掩码和计算的数值直到掩码的二进制个位为1,这时数值按位右移的结果就是域的值。例如参数0xC250, FIELD F0,得到的结果是5。

SIGFIELD十六进制掩码 -参数同十六进制掩码按位与(AND)的数值,然后OllyDbg同时按位右移掩码和计算的数值直到掩码的二进制个位为1,这时数值按位右移的结果转成带符号32位数就是域的值。例如参数0xC250 ,SIGFIELD FF00,得到的结果是0xFFFFFFC2。

简单域的解码会一次显示整个域的内容:


HEX - 以十六进制形式显示域内容;

INT - 以带符号十进制形式显示域内容(带小数点);

UINT -以无符号十进制形式显示域内容(带小数点);

CHAR - 以 ASCII 字符形式显示域内容。

域若是一个枚举类型,则可以使用IF序列,如果必要的话还可以在IF序列后跟关键字 TRYxxx 与 ELSExxx:


IF 十六进制值 "文本" - 如果域等于十六进制值,则将文本作为输出字符串;

TRYASCII - 如果域是一个指向ASCII串的指针,则显示这个串;

TRYUNICODE - 如果域是一个指向UNICODE串的指针,则显示这个串;

TRYORDINAL - 如果域是一序号(有16位均为0),则会显示为序号(“#”后跟整数);

OTHERWISE - 如果前面IF语句为真,则停止解码,否则继续解码;


ELSEINT - 如果前面所有的 IF 和 TRYxxx 语句均失败,则以带符号十进制数形式(带小数点)显示这个域;

ELSEHEX -如果前面所有的 IF 和 TRYxxx 语句均为失败,则以十六进制形式显示这个域;

ELSECHAR -如果前面所有的 IF 和 TRYxxx 语句均为失败,则以 ASCII 字符形式显示这个域;

ELSEWCHAR -如果前面所有的 IF 和 TRYxxx 语句均为失败,则以 UNICODE 字符形式显示这个域。

如果域是一个二进制位集,则可以使用BIT序列,如果必要的话可以后面跟关键字 BITZ 与 BITHEX :

BIT 十六进制掩码 "文本" - 如果值与十六进制掩码按位与(AND)的结果不是0,则将文本做为输出串;

BITZ十六进制掩码 "文本" - 如果值与十六进制掩码按位与(AND)的结果是0,则将文本做为输出串;

BITHEX十六进制掩码 - 如果值与十六进制掩码按位与(AND)的结果不是0,则将结果以十六进制形式显示。

特殊关键字 PURGE 会从输出串尾部移除以下几种符号:


空格 ' '
逗号 ','
或 '|'
冒号 ':'
等于 '='
这会让某些解码情况变的简单。关键字END是类型定义结尾标记并会自动运行PURGE命令。


预编译类型

OllyDbg在预编译资源时,已经包含150多种类型描述。以下列出了一部分。您可以在自定义文件中直接使用这些类型:


LANG_X - 操作系统语言ID(0 - 未知、 9 - 语言、 C - 法语,等等)

GENERIC_X - 访问类型(GENERIC_READ, GENERIC_WRITE...)

FILE_SHARE_X - 共享类型(FILE_SHARE_READ, FILE_SHARE_WRITE)

CREATEFILE_X - 文件创建模式(CREATE_NEW, OPEN_EXISTING...)

FILE_ATTRIBUTE_X - 文件属性(READONLY, SYSTEM, DELETE_ON_CLOSE...)

RT_AXX - 资源类型(RT_CURSOR, RT_GROUP_ICON, ASCII string...)

RT_WXX - 资源类型(RT_CURSOR, RT_GROUP_ICON, UNICODE string...)

COORD - 坐标结构 "(X=xxx,Y=yyy)"

STD_IO_X - 标准句柄(STD_INPUT_HANDLE, STD_ERROR_HANDLE...)

GMEM_X - 全局内存类型(GMEM_FIXED, GPTR...)

LMEM_X - 局部内存类型(LMEM_FIXED, LPTR...)

FSEEK_X - 文件查找类型(FILE_BEGIN, FILE_CURRENT...)

OF_X - 文件模式(fOF_READ, OF_SHARE_COMPAT, OF_VERIFY...)

O_X - 文件创建模式(O_RDONLY, O_BINARY, SH_COMPAT...)

SEMAPHORE_X - 信号量类型(SEMAPHORE_ALL_ACCESS, SYNCHRONIZE...)

SLEEP_TIMEOUT - 超时(INFINITE 或时间)

ROP - 一些标准柵格运算标志代码(ROP)(SRCCOPY, MERGEPAINT...)

COLORREF - RGB 颜色值("<WHITE>", "RGB(rr.,gg.,bb.)"...)

WS_X - 窗口风格(WS_OVERLAPPED, WS_POPUP...)

WS_EX_X - 扩展窗口风格(WS_EX_DLGMODALFRAME, WS_EX_TOPMOST...)

MF_X - 菜单标志(MF_BYPOSITION, MF_ENABLED...)

WM_X - ASCII窗口消息类型(WM_CREATE, WM_KILLFOCUS, CB_SETCURSEL...)

WM_W - UNICODE窗口消息类型(WM_CREATE, WM_KILLFOCUS, CB_SETCURSEL...)

VK_X - 虚拟键盘代码(VK_LBUTTON, VK_TAB, VK_F10...)

MB_X - message box style (MB_OK, MB_ICONHAND...)

HKEY_X - 预定义注册表句柄(HKEY_CLASSES_ROOT, HKEY_LOCAL_MACHINE...)

还有更多的预编译类型。如果常量在它文件被定义为ABC_ xxxxxxxx,那么一般就有ABC_X预编译类型。



 注意:
1.如果OllyDbg是即时调试器,并且在Windows 95下挂接执行了DebugBreak() 的应用程序,则这个应用程序在挂接后,还会运行。在基于NT的系统下,应用程序应该会在
DebugBreak()暂停。
2. 命令 SMSW (保存机器状态字[Store Machine Status Word])。 这个命令仅接受寄存器 AX 作为参数,而程序编译成EAX接受参数。
原创粉丝点击