VMP分析插件v1.3
From:http://bbs.pediy.com/showthread.php?t=154621以前的说明可以看原来的帖子
http://bbs.pediy.com/showthread.php?t=143377
2012/08/13更新 v1.3
修复了插入指令时最后一条指令被部分覆盖会造成备份错位的问题。
修复了有时在汇编和插入指令对话框中输入文字会自动设置光标到第一行的问题。
修复了指令窗口撤销选择处修改时选择范围包含没有修改过的指令块可能会发生错误的问题。
修复了当前目录和OD目录不同时读取ollydbg.ini失败的问题。
修复了注释和标签中包含%时可能出错的问题。
修复了一个1.2版由于修改错误导致的对存在连续指令块的程序可能分析失败的问题。
修复了一个由于重定位导致的在DLL中插入指令或在第一条指令汇编可能出错的问题。
修复了启用进入虚拟机中断时在第一条指令设置断点,中断后再取消断点会导致被调试程序出错的问题。
修复了选中启用虚拟机调试但没有选中进入虚拟机时中断,手动执行到执行虚拟指令也不会中断的问题。
增加了对一些DEMO版和1.6X版的支持,只在1.64 DEMO和2.06 DEMO做过测试,不一定完全可用。
去掉了地址在所有模块之外不能分析的限制,现在会添加到未知模块,但是有些内容比如分支表分析可能没有原来准确,而且保存到UDD的断点和分析点等重新加载时可能因为重定位不同而出错。
现在表达式中可以使用ESP了,表示真实ESP。和真实ESP有关的功能必须在分析后才能使用,否则真实ESP为0。
现在所有窗口的显示设置,比如列宽、配色、字体、高亮和其他的选项等都可以自动保存到ollydbg.ini了,保存格式和原来不同,以前的设置都会失效。
现在复制窗口内容时不会复制列宽小于3的列(把列宽拉到最小,变成灰色),和OD相同。
修改了计算返回地址的方法,一些以前计算错误的情况现在也可以计算了。
现在选择了退出虚拟机时加密寄存器的返回地址显示为解密寄存器后返回到的地址,而不是以前显示的解密寄存器代码的地址。
如果选了检查虚拟机完整性可能不能正确计算返回地址或函数调用目标,因为检查结果参与计算,会被认为是变量。
现在地址如果有对应的符号名,会显示在地址栏的地址后面,和OD选项中地址显示格式 HEX,符号 相同。
如果开始分析的地址有标签,虚拟程序会自动添加标签VMP_标签,否则添加标签VMP_地址。
1字节和2字节虚拟寄存器现在可以分低位和高位(vRxBL、vRxBH、vRxWL、vRxWH)。
原来没有发现,其实1字节和2字节寄存器操作指令可以取寄存器中的任何位置,但几乎都是分成两半使用,所以只添加了高位和低位。
现在断点、补丁、分析点可以保存到UDD文件了,重新加载相关模块时可以恢复。
不属于任何模块的记录(模块名显示为"未知模块")保存到主程序的UDD文件中,但是这些记录无法重定位,重启后有可能因地址改变而不能使用。
如果分析内容所在的模块不是主模块,关闭前一定要在模块中保留一个断点(不是虚拟指令断点)。因为插件只能在ODBG_Pluginsaveudd回调函数中保存UDD文件,如果OD认为模块中没有需要保存的内容就不会调用这个函数,这样保存的数据就会丢失。
还有一个要注意的是保存功能可能和一些插件有冲突,OD规定在ODBG_Pluginuddrecord回调函数中不是自己的插件记录应该返回0,让其他插件处理,可是有些插件不遵守这个规定,其他插件就得不到自己的记录了。我就因此调试了很长时间找不到原因,最后发现有一个插件(CodeRipper)把所有记录都处理了。
插件主菜单添加
重启程序保留分析:选择是否在不关闭OD的情况下重新调试已经分析过的程序保留分析结果,只保留最后一次的分析,调试不同的程序会清除分析结果。这个选项对保存到UDD文件的内容无效,不管是否选中,都会在调试结束时保存。
反汇编后自动分析:是否在反汇编虚拟程序后自动开始分析,如果不自动分析的话也可以在反汇编窗口菜单或按Ctrl+A手动开始分析。
打开补丁窗口:打开补丁窗口,查看所有对程序的修改(汇编、插入指令等)。
打开分析点窗口:打开分析点窗口,查看所有开始分析的地址和分析状态。
虚拟指令信息窗口
这一版虚拟指令信息文件修改比较大,原来的文件已经不能使用了,如果有自己添加的内容的话需要重新添加。
一些指令的描述做了修改,以适应数据分析。
去掉了操作数说明。
现在代码高亮设置可以使用了,使用OD高亮选项中对应的颜色。
添加指令有效选项,如果选中在分析时作为有效指令处理。
添加了写操作数的操作表达式,分析时会使用。
表达式说明:
运算符,按优先级从高到低排序
:(连接)
~(非)
*(无符号乘)、**(有符号乘)、/(无符号除)、//有符号除、%(无符号余数)、%%(有符号余数)
+(加)、-(减)
<<(逻辑左移)、>>(逻辑右移)、>>>(算术右移)、<<>(循环左移)、>><(循环右移)
&(与)
~&(与非)
^(异或)
|(或)
=(赋值)
内存操作用 长度 段前缀:[地址] 的形式,所有表达式可以用BYTE、WORD、DWORD指定长度。
指令窗口
地址:现在可以双击地址栏显示相对或绝对地址
HEX数据:现在可以单击标题切换显示解密前和解密后的数据。选中转移或转移目标指令时会显示转移路径。
注释:现在单击标题可以切换显示引用数据、产生数据、堆栈信息。
引用数据/产生数据:必须先经过分析。可以显示指令引用或产生数据的地址、类型、数值,和指令信息中的读操作数/写操作数对应,可以在信息窗口的数据栏中查看更详细的内容。
堆栈:必须先经过分析。显示指令执行前的vESP、真实ESP、虚拟堆栈长度。
添加了代码高亮,可以在指令信息中设置。无效指令显示灰色或填充指令颜色,计算常量的指令也属于无效指令。
添加了查找指令功能,基本和OD相同,查找的指令序列不能跨指令块,最大20条指令。
ANY n 匹配0到n(十进制)条任意指令,没有操作数时n=1,相邻的会合并,最大20条。
REG和CONST匹配任意寄存器和常量。
添加了图表功能,可以画控制流图和数据流图,生成gdl或vcg文件,可以用VCG TOOL或IDA的WinGraph打开,插件里就不带了。图表不能显示中文,中文注释可能看不见。
控制流图可以看出程序的流程,每个分支和指令块的关系。VMP的指令块一般都很长,如果显示全部指令的话画出来的图非常难看,使用控制流图(部分指令)可以只显示前后10条指令。控制流图中Switch的Case是0到Case数的序号,不是实际检查的值。
数据流图可以显示数据处理流程,指令之间的关系,我是研究化简方法时使用,顺便加进来了。数据流图不能跨指令块,如果指令很多画出来的图可能非常乱,数据流图(指令相关)可以只显示和选择指令相关的内容,会简单一些,选择的指令显示加粗的红色边框。不同的指令或不同的操作数发出的路径使用不同颜色,否则走远了之后可能分不清了。无效或常量节点和路径显示灰色,从灰色节点发出有效路径说明这是一个最终常量。
有几种特殊节点:
StartData:指令块开始时的已知数据。
EndData:指令块结束时的已知数据。
UnknownSource:数据来源不在已知数据中,这并不代表数据未知,只是不在当前分析的已知数据中,比如读内存。
UnknownTarget:数据目标未知,一般是无法计算目标地址。
图表中会显示当前注释栏的内容,注释会使节点变得很宽,比较难看,如果不想显示注释可以把注释栏宽度拉到最小(变灰)。
指令窗口菜单
撤销选择处修改:现在可以恢复添加指令时拆分的指令块了,但是最好在补丁窗口中恢复相关的补丁,否则可能有些问题。
删除插入指令:删除插入指令时添加的指令块,清除指令块中所有断点,恢复内存内容,不能删除正在执行的指令块。
断点-条件记录:设置条件记录断点,使用方法基本和OD相同。
断点-指令条件断点(运行后):已去掉,可以用指令条件断点代替,弹出窗口中可以设置。
断点-指令条件记录断点:设置指令条件记录断点,使用方法基本和OD的条件断点相同。
查找-指令:查找指令序列,详细信息请看上面查找指令的说明。
查找-常量:查找指令包含的常量,如果已经分析,也包括引用数据和产生数据中的常量。
查找-下个:继续上一次的查找。
查找-所有指令:查找指令序列,把所有找到的内容显示到参考窗口。
查找-所有常量:查找常量,把所有找到的内容显示到参考窗口。
查看-相对地址/绝对地址:把地址栏显示的内容在相对地址和绝对地址之间切换。
查看-加密HEX数据/解密HEX数据:把数据栏显示的内容在解密前和解密后之间切换。
查看-注释/堆栈/引用数据/产生数据:把注释栏显示的内容在注释、堆栈、引用数据、产生数据之间切换。
分析-分析虚拟程序(选择):分析当前选择的虚拟程序,有很多功能必须分析后才能使用。
分析-分析虚拟程序(全部):分析当前模块中的全部虚拟程序。
分析-删除分析(选择):删除当前选择的虚拟程序中的分析结果。
分析-删除分析(全部):删除当前模块中的全部虚拟程序的分析结果。
图表-控制流图(部分指令):生成虚拟程序的控制流图,每个指令块只显示前后10条指令。
图表-控制流图(全部指令):生成虚拟程序的控制流图,显示所有指令。
图表-数据流图(指令相关):生成指令块的数据流图,只显示和选择的指令相关的内容。
图表-数据流图(指令块):生成指令块的数据流图,显示所有指令。
指令窗口快捷键
SHIFT+F4:和菜单 断点-条件记录 相同。
CTRL+F:和菜单 查找-指令 相同。
CTRL+L:和菜单 查找-下个 相同。
CTRL+A:和菜单 分析-分析虚拟程序 相同。
信息窗口
添加了数据栏,可以点击标题在信息、指令说明、数据之间切换。
数据:显示指令引用和产生的数据、引用数据的来源指令、产生数据的目标指令。
信息窗口菜单
查看信息/查看指令说明/查看数据:显示内容在信息、指令说明、数据之间切换。
修改寄存器/修改数据:修改选择的寄存器或数据。
跟随:跟随返回或调用地址到OD的反汇编窗口。
数据窗口中跟随地址/数据窗口中跟随数值:跟随内存地址或数值到数据窗口。
反汇编窗口中跟随:跟随数值到指令窗口。
转到数据来源/转到数据目标:转到引用数据的来源指令或产生数据的目标指令。
复制选择部分到剪贴板:复制选中的行到剪贴板。
复制所有内容到剪贴板:复制整个表格到剪贴板。
信息窗口快捷键
SPACE:和菜单 修改寄存器/修改数据 相同。
CTRL+C:和菜单 复制选择部分到剪贴板 相同。
寄存器窗口
现在可以显示真实ESP了。
寄存器窗口菜单
高亮选择寄存器/不高亮选择寄存器:是否在指令窗口中高亮显示选择的寄存器,只支持通用寄存器。寄存器高亮使用代码高亮中非法指令的颜色,如果没有选择代码高亮或没有设置非法指令颜色,寄存器高亮无效
堆栈窗口
现在可以显示真实ESP了。
地址可以在绝对、相对于指定地址、相对于vREG、相对于vESP、相对于真实ESP之间切换。
中断时自动显示的地址可以在锁定、vREG、vESP、真实ESP之间切换。
堆栈窗口菜单
地址-绝对/相对于所选/相对于vREG/相对于vESP/相对于ESP:切换地址显示格式。
自动显示-锁定/vREG/vESP/ESP:切换中断时自动显示的地址。
查找常量:在堆栈中查找常量,只支持4字节。
查找二进制字串:在堆栈中查找连续的数据。
查找下个:继续上一次的查找。
堆栈窗口快捷键
CTRL+B:和菜单 查找二进制字串 相同。
CTRL+L:和菜单 查找下个 相同。
数据窗口
添加了历史记录,可以回到查看过的地址。
数据窗口快捷键
ENTER:跟随选择内容到数据窗口。
+/-:转到上个或下个历史记录。
添加窗口 补丁
显示所有对虚拟程序的修改,包括汇编、插入指令等,这些修改在结束调试时会自动保存到对应模块的UDD文件。也可以在这里对补丁进行操作,比如恢复和激活等。
这里的一个补丁是在相同指令块中连续的代码修改,如果同一个指令块中两个补丁间隔小于5会自动合并。
插入指令会添加5个补丁,包括一个拆分补丁(在插入指令的位置拆分指令块)、一个添加补丁(添加保存插入代码的指令块)、三个修改补丁(两个补丁在拆分的前后修改,一个在添加指令块中修改,写入插入代码),这些补丁是有关联的,最好一起操作。如果想恢复插入指令时做的修改,应该先在补丁窗口中禁用对应的补丁,直接在指令窗口选择撤销选择处修改的话可能因拆分指令块产生问题。
激活从UDD文件中读取的补丁前应先分析补丁所在的虚拟程序,可以在分析点中重新分析。
如果插入指令时添加的指令块在不同模块,禁用类型为添加的补丁后不能激活。
禁用类型为添加的补丁时会禁用相关的修改补丁,激活插入指令块中的修改补丁时应该先激活对应的添加补丁。
禁用或激活插入指令相关的补丁时要先禁用拆分补丁,后激活拆分补丁,否则拆分的第一个指令块最后有不全的指令激活时可能会失败。
补丁窗口
地址:显示补丁的开始地址。
模块:显示补丁所属的模块。
长度:显示补丁长度。
类型:显示补丁类型,修改是虚拟指令的改变,添加是插入指令时添加的指令块,拆分是插入指令时对原指令块的拆分。
状态:补丁目前是否激活。
旧:修改前的指令。
新:修改后的指令。
注释:指令的注释
补丁窗口菜单
反汇编窗口中跟随:跟随补丁开始地址到指令窗口。
应用补丁/恢复原始代码:激活或禁用补丁。
删除记录:删除补丁记录,不会恢复原始代码。
删除模块中的所有记录:删除选择的补丁所属模块中的所有补丁记录,不会恢复原始代码。
复制选择部分到剪贴板:复制选中的行到剪贴板。
复制所有内容到剪贴板:复制整个表格到剪贴板。
补丁窗口快捷键
ENTER:和菜单 反汇编窗口中跟随 相同。
SPACE:和菜单 应用补丁/恢复原始代码 相同。
DELETE:和菜单 删除记录 相同。
CTRL+C:和菜单 复制选择部分到剪贴板 相同。
鼠标双击:和菜单 反汇编窗口中跟随 相同。
添加窗口 分析点
显示所有开始分析的地址,比如分析虚拟机、分析虚拟程序时选择的地址,分析所有导出函数时每个函数的地址等,这些内容在结束调试时会自动保存到对应模块的UDD文件。也可以在这里对分析点进行操作,比如重新分析等。
分析点窗口
地址:开始分析的地址。
模块:地址所属的模块。
类型:开始分析的类型,虚拟机或虚拟程序。
分析结果:是否分析成功,现在只要有一点不确定的就会显示失败,不一定都是分析错误。
vEIP/指令表:如果分析完成,显示虚拟程序的初始vEIP或虚拟机的指令表地址,如果有多个虚拟机只显示第一个。
分析点窗口菜单
跟随地址:跟随开始分析的地址到OD的反汇编窗口。
反汇编窗口中跟随vEIP:跟随虚拟程序的初始vEIP到指令窗口。
查看虚拟指令:打开虚拟指令窗口,查看相关虚拟机的虚拟指令处理程序。
删除记录:删除选择的分析点记录。
删除模块中的所有记录:删除地址所属的模块中的所有分析点记录。
重新分析:重新分析虚拟机或虚拟程序。
复制选择部分到剪贴板:复制选中的行到剪贴板。
复制所有内容到剪贴板:复制整个表格到剪贴板。
分析点窗口快捷键
ENTER:和菜单 跟随地址 相同。
SPACE:和菜单 重新分析 相同。
DELETE:和菜单 删除记录 相同。
CTRL+C:和菜单 复制选择部分到剪贴板 相同。
鼠标双击:和菜单 跟随地址 相同。
这次主要添加数据分析功能,可以分析每条指令引用和产生的数据地址、类型、数值,生成数据流图,计算真实ESP,化简常量和识别垃圾指令等。现在可以化简全部常量,标记大部分无效的和计算常量用的指令,没有用的指令越多化简率越大。这些指令在反汇编窗口和数据流图中显示为灰色,分析时可以不用看这些内容,减少了很大的分析量,普通保护化简率一般在20%~50%,最大保护+变形有时可以超过90%。现在几乎可以把最大保护化简到最小保护的程度,最小保护大部分都是正常计算需要的指令,化简效果差一点,只能以后做算法分析了。但是数据分析可以计算出指令执行前后的数据地址、类型、数值等,对分析还是有些帮助的。通过数据流图可以看出VMP的数据流一般都很长,经常是一个数压栈之后几十甚至几百条指令后才使用,在这之间堆栈会不停的改变,手动分析的话很难知道数据是哪里来的,这也是VMP强大的地方之一。现在通过看信息窗口中的数据栏和数据流图可以很容易找到这些数据的来源,简单的数据类型比如常量和vESP等分析时还会自动计算。
分析后会在log窗口记录化简常量数、无效指令数、计算常量的指令数、化简率等,可以看出化简的程度。现在认为对程序没有影响或只隐含修改vESP的指令是垃圾指令,有效结果全部为常量的指令是计算常量的指令,这些指令替换成AddVEsp或vPushImm不会对程序功能造成影响,可以不用分析,只看最后的结果就可以。
如果内存属性为只读或可执行,或包含虚拟机相关内容、输入表、资源等内容在数据分析时会被认为是常量,这不一定完全正确,有时会把变量当作常量或者相反,现在我也没有更好的方法了。
分析内容中显示的vESP和ESP是相对虚拟程序开始时的偏移,开始时vESP和ESP都为0,堆栈长度是初始堆栈内容长度,后面根据经过的指令修正,不一定和执行时的寄存器对应。
如果修改过指令的话(汇编、插入指令)最好重新反汇编后在分析,否则可能有一些问题,尤其是在修改真实ESP操作和转移相关的代码后,可以在分析点窗口选择重新分析。
有人说要添加旧版本支持,现在添加了一些,只在1.64 DEMO版做过测试,不一定完全可用。这个插件只是为了研究分析方法,也不想做成一个完整的破解工具,旧版本比如1.2结构差距很大,也不准备再添加支持了。
下面简单说一下数据分析的原理,这一版从原来的基础上增加了两遍分析,一共三遍。
第一遍 反汇编
反汇编虚拟指令,分析转移类型和目标,恢复程序结构,生成控制流图。
跟原来一样,遇到新指令块后反汇编,计算转移目标继续分析,按深度优先遍历顺序排序。如果程序逻辑有冲突,比如条件不成立时的转移目标不是下一个指令块,再单独作出调整。
第二遍 数据分析
做一次向后的数据跟踪,生成数据流图,分析数据地址、类型和数值,分析堆栈相关信息和真实ESP。
跨指令块分析:
维护一份已知数据列表,记录数据地址、类型、数值、来源(初始数据或产生数据的指令地址),程序开始的时候只有初始堆栈里的内容,vESP、真实ESP设为0。遇到新指令块时如果所有除循环以外的来源都已分析,就把多个来源结束时的数据合并,设为指令块开始时的数据,做指令块内分析。合并时如果发现地址有重叠但类型或数值不同,作为变量处理。现在没有处理循环,遇到循环不会继续分析,这可能会产生一些问题,比如 a=0; for(i=0;i<5;i++)a++; 分析结果可能会是a=1而不是a=5
指令块内分析:
从第一个指令开始向后跟踪,根据已知数据设置每个指令的引用数据、数据来源、执行前的堆栈信息,根据指令执行结果修正已知数据。如果一个指令引用不同类型的数据计算,结果为合并后的类型,现在是取未知程度最高的类型。规则是这样的:除真实寄存器外的类型与常量计算类型不变,堆栈偏移、标志与自己或常量计算类型不变,否则为变量,真实寄存器、变量和任何类型计算结果都是变量。生成指令块结束时的数据,用于跨指令块分析。
分析真实ESP:
分析真实ESP是基于这样几个假设:vPopVEsp、vPopVSp、vRet执行时堆栈中只有要弹出的内容,vPopVEsp、vPopVSp只用来修改真实ESP,这一般都是成立的。为了简单,还认为指令块开始时的堆栈长度和进入虚拟机时相同(JT、JNT目标会多一个要弹出的标志),其实应该根据结束时的堆栈长度来计算,这里直接认为是固定长度了,这样就可以和数据分析放在一遍,如果计算的话还要从后向前再分析一遍,比较麻烦。
VMP中真实ESP操作大部分都有固定的模式,分析起来容易一些,但是PUSH和POP会在虚拟堆栈为空时直接用vPush和vPop,有些时候无法准确识别。现在的方法是在指令块中从后向前查找vPopVEsp、vPopVSp、vRet,这些指令执行时堆栈长度是确定的。从这里向前修正堆栈长度直到前一个可以确定长度的指令停止,中间遇到堆栈长度小于0的情况说明是真实PUSH。如果停止时修正后的堆栈长度大于正常堆栈长度,说明有真实POP,再向后修正一遍直到下一个可以确定长度的指令。这可能并不总是正确,还有一些情况比如PUSH后再POP但是中间没有vPopVEsp,不会被看作真实堆栈操作,但是一般没有什么大问题。
第三遍 识别无效指令
标记垃圾指令(不会对程序造成影响的指令),标记计算常量用的指令,计算最终常量(数据来源全部为计算常量的指令,数据目标是有效指令)的数量和化简率。
跨指令块分析:
从第一个指令块开始递归分析所有目标,分析后检查当前指令块的结束数据是否有和目标指令块有效的开始数据重叠的部分,如果有说明数据有效,否则是无效数据。设置结束数据的有效性,做指令块内分析,把所有指令块分析结果相加计算化简率。现在算出的化简率是无效指令+计算常量的指令占总指令数的比例,在手动分析时可以不用考虑这些指令。
指令块内分析:
有了数据流图识别垃圾指令就很简单了,从最后的指令开始,如果一个指令有流向有效指令或结束数据的路径,标记为有效,否则就是垃圾指令。如果一个计算指令(比如vAdd、vNand等)有效结果全部为常量,常量目标为有效指令,说明这是一个最终常量,向前路径上所有指令都是计算常量的指令(不包括那些有数据流分支到其他有效指令的指令)。设置指令块开始时的数据是否有效,用于跨指令块分析。 沙发前排支持。。。。。。。。
好长的说明{:1_908:} Thank you. 不会用的说,希望出个教程{:1_937:} 收藏备用. 绝对好东西,谢谢 已经下过了,是不错的 下载下来看看了 {:1_908:}怎么用呢谢谢分享