VMProtect2.04加壳程序从入门到精通

软件:正版VMProtect2.04加密的Win98记事本
加密选项:除了编译--调试模式与水印以外,全部打钩;虚拟计算机--数量为默认值1;编译类型:超级(变异+虚拟)
调试器:官网下载的OllyDbg2.0
加壳程序下载:NOTEPAD.rar下载此附件需要消耗2Kx,下载中会自动扣除。[谁下载?]
UDD文件:NOTEPAD-udd.rar下载此附件需要消耗2Kx,下载中会自动扣除。[谁下载?](这是是整理了伪指令的UDD文件,后续还会更新)
本文的结构:
序言
文章简介
1.基础知识
1.1.VMProtect虚拟机简介
1.2.VM堆栈
1.3.伪指令汇总
2.综合运用
2.1.常见伪指令组合
2.2.NAND(与非门)
2.3.EFLAGS标志位检测+跳转
3.NOTEPAD全程跟踪
3.1.TLS
3.2.VMP外壳函数获取
3.3.虚拟执行环境与调试器检测
3.4.HASH值分块检测与API获取
3.5.重点解密循环
4.实验室(暂定)
尾声


1.基础知识
1.1.VMProtect虚拟机简介

虚拟机加密,是指像VMP这样的保护程序,它会把源程序的X86指令变成自定义的伪指令,等到执行的时候,VMP内置在保护程序中的VM就会启动,读取伪指令,然后解析执行。
VMP是一个堆栈虚拟机,它的一切操作都是基于堆栈传递的。在VMP中,每一个伪指令就是一个handler,VM中有一个核心的Dispatch部分,它通过读取程序的bytecode,然后在DispatchiTable里面定位到不同的handler中执行。绝大多数情况下,在一个handler中执行完成后,程序将回到Dispatch部分,然后到nexthandler中执行。

在上面的框架中,核心的部件就是Dispatch部分,下面并列的部件就是handlers。
经过VMP加密的X86指令,一条简单的指令被分解成数条VMP的伪指令,它按照自己的伪指令排列去实现原指令的功能,在加上其他的花指令混乱等等,你将完全看不到源程序指令。VMP自带的各种机制都不再是以X86指令的形式去实现,而是用自己的伪指令去测试。
在VMP的VM运行过程中,各个寄存器的基本用途是:EBP和EDI是VM堆栈指针(不是常规的堆栈);ESI是伪指令指针(相当于常规的EIP);EAX是VM解密数据的主运算寄存器;EBX是VM解密数据的辅运算寄存器;ECX是常规的循环计数器;ESP是常规的堆栈栈顶指针。EDX是读取伪指令表数据;
EDI、EBP分别指向VM堆栈的上下限位置,EBP指向堆栈的下限并向上发展,EDI指向堆栈的上限并使用[EDI+EAX]的方式向下发展;ESI指向的内存块里包括要执行的伪指令序列,而不同的是,当VM要是使用到立即数时,也是从ESI读取。可见ESI内存块里面是精心构建的数据块,只有VM自身执行过程中,才能知道下一个数据是代表伪指令还是立即数;在VM运算中EAX寄存器很多时候通常只有AL参与运算然后在存取时再以AX或EAX得方式存取;EBX在很多加密数据运算中,都会参与到EAX值的计算中,协助运算中正确的值。而每次EAX的值运算结束后,反过来会计算好下一次运算中EBX的值。所以EBX的数据一旦出错,下一个数据解密必然错误;在VM运行中,通常一切操作都是在VM堆栈内完成的,所以绝大多数情况下对ESP的操作都是花指令或junkcode。在一些虚拟与现实(比如说调用系统函数)交接的地方,系统并不知道VM堆栈的存在,这就需要把数据(比如系统函数的调用参数)移动到常规ESP栈顶。EDX是一个较少使用的寄存器,只在一些解密循环里面参与运算。而它的一个主要的运用是在DISPATCH部件里,根据ESI的值来获取DispatchTable的数据,让VM执行下一条伪指令。

1.2.VM堆栈
VMP的VM是基于堆栈的虚拟家,理解好VM的堆栈空间划分和操作,是理解整个VM运行的基础。
VMProtect2.04加壳程序是从TLS开始运行的,我们首先点击OD的options菜单,修改Startupandexit选项,让OD中断在TLScallback里。加壳程序运行后,VMP初始化VM,并进入DISPATCH部分。这里我们就以初始化后的堆栈为例。
VM的堆栈一共使用61个DWORD,上下分别有2个堆栈指针,下面为EBP指针,上面为EDI指针。下面是VM初始化时,给EDI和EBP指针赋值后的堆栈。
EDI=0013F8BC
EBP=0013F9B0
CPUStack
LockedValueASCIIComments
0013F8BC009539E89.;这里是EDI指向
0013F8C000950000...
0013F8C400150000...
0013F8C800000080...
0013F8CC019314D6
0013F8D00013F8A8.
0013F8D47C92E920|
0013F8D800000000....
0013F8DC00000000....
0013F8E000000000....
0013F8E4FFFFFFFF
0013F8E87C98FEFF|
0013F8EC7C00ADE7.|
0013F8F000000000....
0013F8F400150000...
0013F8F80013F6F0.
0013F8FC0013F940@.
0013F9000013F944D.
0013F9047C92E920|
0013F9087C9301E0|
0013F90CFFFFFFFF
0013F9107C9301DB|
0013F9147C9314D6|
0013F9187C931514|
0013F91C7C99E120|
0013F9207C9314EA|
0013F9245ADF1158XZ
0013F92800000001...
0013F92C00000000....
0013F9307FFDA000.
0013F9347FFDF000.
0013F93800158070p.
0013F93C0013F890.
0013F94000000000....
0013F9440043D759YC.
0013F9480000E9ED..
0013F94C409B0002.@
0013F95000000020...
0013F9540013F9CC.
0013F9580013F96Cl.
0013F95C0043E9EDC.
0013F960000359F4Y.
0013F96400000020...
0013F968004253CDSB.
0013F96C409B0000..@
0013F97000000020...
0013F9740013F9CC.
0013F9780013F98C.
0013F97C00000000....;这里是EBP指向
0013F98000000000....;这里是VM初始化保存的各个寄存器
0013F98400000246F..
0013F988000359F4Y.
0013F98C00000020...
0013F99000000000....
0013F9940013F9CC.
0013F998004253CDSB.
0013F99C000359F4Y.
0013F9A000400000..@.
0013F9A40013F9C0.
0013F9A8C456C619V;这里是VMP的2个加密数据
0013F9AC2EF6420A.B.
0013F9B07C92118A|;RETURNtontdll.7C92118A;这里是TLS进来时的栈顶
关于2个加密数据和初始化的过程我们后续来说,这里我们主要关注VM的堆栈划分。
我把上面的EDI指向的堆栈称为EDISTACK,把EBP指向的堆栈称为EBPSTACK。在VM中,EBPSTACK是运算区,各类数据的运算操作在这里完成;EDISTACK是存储区包括长期存储数据和临时存储EBPSTACK的运算数。
下面我们来看一条数据移动伪指令:
命名:
VM_MOVdw_EDISTACKdw_EBPSTACKdw
代码:
0043DC19|.F6D8NEGAL;*
0043DC26|.C0C807RORAL,7;*
0043DC34|.2C20SUBAL,20;*
0043DC41|.243CANDAL,3C;*

0043E080|$8B5500MOVEDX,DWORDPTRSS:[EBP];*
0043E086|.83C504ADDEBP,4;*

0043D3D7/>/891438MOVDWORDPTRDS:[EDI+EAX],EDX;*
功能:
把1个dword的数据从EBPSTAK栈顶移动到EDISTACK,使用EAX作为偏移量

在EDISTACK的数据移动中,使用[EDI+EAX]的方式来存储与获取各个值。通过计算不同的EAX的值,可以到达EDISTACK中不同位置。在计算EAX值时,实际真正计算的是AL的值,我们来考虑一下AL的最小值和最大值,AL=00时[EDI+EAX]=[0013F8BC+00000000]=0013F8BC,AL=FF时[EDI+EAX]=[0013F8BC+000000FF]=0013F9BB,这是使用[EDI+EAX]可以读取的上下限。但是,在VM的AL值计算过程中,最后有一条ANDAL,0x3C指令,0x3C=00111100bit由于这条指令的限制,无论AL为任何值(从00000000bit到11111111bit),通过AND操作,只能有1111bit的活动空间大小,1111bit相当于16,所以EDISTACK最多可以读取16个dword;由于00111100bit最后两个00位的限制,任何数字与它AND后,后两位都必然为0,变成与4对齐的值,说明VM都是按照0013F8BC0013F8C00013F8C40013F8C8这样的4对齐来读取。在读取时,VM可以读取byteworddword,但是VM将不会去读取0013F8BE。
由于EDISTACK堆栈向下发展,EBPSTACK堆栈向上发展,EDISTACK有0x3C控制着范围,而EBPSTACK是操作区,没有硬性的范围控制。为了预防两个空间相撞,在每次往EBPSTACK移动数据后,VM都有相应的边界检测指令如下:
0043CE5A|.8D4750LEAEAX,[EDI+50];*
0043EE5D|.39C5CMPEBP,EAX;*
0043F08C|.^\0F8729F6FFFFJA0043E6BB;*
比较结果大于,这个正常的情况,在这个VM跟踪过程中,我没有发现一次小于的情况。如果小于,也就是EBPSTACK栈顶已经到达[EDI+50]位置,VM将会重新分配堆栈空间。0x50的偏移量比0x3C的偏移量多5个dword的缓冲区。我们来手动修改EBP指针,看看VM的对于两个堆栈空间即将相撞的处理情况:
CPUDisasm
AddressHexdumpCommandComments
0043F092|.52PUSHEDX;
0043D6C1|.8D542408LEAEDX,[ARG.2];*EDX获得的是原来EDI指针地址0013F8BC
0043DF38|.8D4F40LEAECX,[EDI+40];*0x40的偏移量是0x3C的偏移量数据1个dword结束后的位置
0043DF46|.29D1SUBECX,EDX;*减法计算出数据存储量
0043DF4B|.8D4580LEAEAX,[EBP-80];*增加0x80的空间
0043DF5C|.24FCANDAL,FC;*按4对齐
0043DF68|.29C8SUBEAX,ECX;*在增加原来数据大小的堆栈空间
0043DF6E|.89C4MOVESP,EAX;*
0043DF7E|.56PUSHESI;|Arg1=NOTEPAD.425748,*
0043DF87|.89D6MOVESI,EDX;|*
0043DB3A/$8D7C01C0LEAEDI,[EAX+ECX-40];*
0043EC1E.89C7MOVEDI,EAX;*
0043EEED|.F3:A4REPMOVSBYTEPTRES:[EDI],BYTEPTRDS:[;*移动原来EDISTACK中存储的数据
0043EEF7|.8B7C2410MOVEDI,DWORDPTRSS:[ESP+10];*
0043EEFF|.8B742410MOVESI,DWORDPTRSS:[ESP+10];*
这里我们可以看到,每次发现两个堆栈空间即将相撞,VM都重新给EBP分配堆栈,并把原来EDISTACK存储的数据移动到新的空间内。
下面是使用OD跟踪VM堆栈的几个小技巧:
在OD中跟踪VM数据移动时,双击0013F8BC地址,OD将会以0013F8BC为基址,显示上下各个地址与它的偏移量,如图:
CPUStack
LockedValueASCIIComments
$-C759D0000..u
$-800000001...
$-40013F8FC.
$==>009539E89.;这里是0013F8BC,双击后的效果
$+400950000...
$+800150000...
$+C00000080...
$+10019314D6
在跟踪VM时,在数据移动伪指令中的ANDAL,0x3C的下一条指令下断点,这样每次进行数据移动,你都可以在这个断点看到,数据的去向和来源,这是极其有用的。在很多复杂的运算地方,你需要在草稿纸上记下,EDISTACK中一些空间的数据时来自于什么时候?比如标志位ZF检测+跳转是VM的一个重要操作,而EFLAGS标志数都是相差不多或类似的0000028600000246等等,如果你不能准确知道[EDI+EAX]存储或读取的位置,你将无法理解VM的操作。这非常的重要,请牢记!必要时连OD得数据窗口也一起配合显示VM堆栈

把OD里的堆栈窗口拉高,让它竟可能多的显示数据,在高分辨率的电脑上,最好是能够显示出整个VM的堆栈。默认情况下,堆栈窗口是随着ESP指针的变化而自动显示的,这对于我们要时刻盯着VM堆栈的需求不相符,在堆栈窗口-->右键-->Lockaddress打钩,这样OD就会锁定堆栈窗口。
到这里,关于堆栈空间的介绍就结束了。对堆栈的理解是本文的根基。

1.3.伪指令汇总
调试VMP前期的一个重要的体力活是,识别出所有的伪指令,并根据它的用途给它相应的命名。以后就可以在DISPATCH部件的最后跳转地址:
0043E11F|.C25000RETN50
下断点,再盯着VM堆栈就可以知道VM的所有操作。
我们先来了解所有伪指令的DISPATCH(调遣)部件:
0043E6BF|.8A46FFMOVAL,BYTEPTRDS:[ESI-1];*
0043E6C4|.30D8XORAL,BL;*
0043E6CE|.F6D0NOTAL;*
0043E6D6|.FEC8DECAL;*
0043E6DA|.C0C807RORAL,7;*
0043E6E1|.83EE01SUBESI,1;*
0043E6ED|.30C3XORBL,AL;*
0043D02F|.0FB6C0MOVZXEAX,AL;*

0043F124|.8B1485DBE143MOVEDX,DWORDPTRDS:[EAX*4+43E1DB];*
0043E100|>/81C26B197FB6ADDEDX,B67F196B;*

0043E10A|.8954243CMOVDWORDPTRSS:[ESP+3C],EDX;*
0043E11B|.FF74244CPUSHDWORDPTRSS:[ESP+4C];*
0043E11F|.C25000RETN50
首先从ESI中解密获得下一条伪指令在DispatchTable(调遣表)中的偏移量,使用[EAX*4+43E1DB]来读取出伪指令地址,简单的ADD解密后,把真正的伪指令地址压入ESP栈顶,最后用RETN50跳转到相应的伪指令。
MOVEDX,DWORDPTRDS:[EAX*4+43E1DB]给我们提供的信息:DispatchTable的起始地址是0043E1DB,最后一个dword的开始地址是以AL的最大值FF作为偏移量[FF*4+43E1DB]=0043E5D7。我们把记事本0043E1DB--0043E5D7的数据粘贴:
CPUDump
AddressHexdumpASCII
0043E1D009|BAC449D0|.I
0043E1E0BAC4491E|B7C449E6|C4C44953|D1C44905|IIISI
0043E1F0BEC44975|D1C449D4|CEC4490C|D6C449C3|IuII.I
0043E200BDC4497B|CEC44967|BEC44926|BFC449EB|I{IgI&I
0043E210C2C44982|D0C4493A|BAC4491E|B5C449A8|II:II
0043E220C4C4491E|B5C4492E|C8C449B9|BBC449E9|II.II
0043E230C3C4492D|B8C44995|C1C44982|D0C44975|I-IIIu
0043E240D1C449C3|BEC44916|B6C4492D|B8C44975|I镁II-Iu
0043E250D1C44995|C1C449EB|C2C44952|BFC449B4|IIIRI
0043E260D3C4498B|D3C44905|CEC44952|BFC449D4|IIIRI
0043E270CEC449E8|B8C449C3|BDC449C3|BEC4495E|II媒I镁I^
0043E280B4C449B1|B8C44961|BDC4495D|BFC449E9|IIaI]I
0043E290C3C44926|BFC4495F|D0C449B4|D3C449E6|I&I_II
0043E2A0C4C449EC|B7C4491E|B5C4490D|C0C4490D|III.I.
0043E2B0C0C449C3|BDC4495D|BFC4497B|CEC449C3|I媒I]I{I
0043E2C0BDC4491E|B5C44982|D0C4498A|B9C449A6|IIII
0043E2D0D1C449EB|C2C449D4|CEC44961|BDC44909|IIIaI.
0043E2E0BAC44953|D1C44961|BDC4493A|BAC4493A|ISIaI:I:
0043E2F0D0C4490C|D6C4493A|BAC4491E|B7C44905|I.I:II
0043E300CEC4490D|C0C44982|D0C44927|D2C4497C|I.II'I|
0043E310BDC449E8|B8C44941|C2C449E9|C3C44925|IIAII%
0043E320CEC44953|C6C44961|BDC44953|C6C44983|ISIaISI
0043E330D6C44953|C6C4495D|BFC44953|C6C449A8|ISI]ISI
0043E340C4C44953|C6C4495F|D0C44953|C6C449E6|ISI_ISI
0043E350C4C44953|C6C4493A|BAC44953|C6C44900|ISI:ISI.
0043E360C7C44953|C6C4492D|B8C44953|C6C44925|ISI-ISI%
0043E370CEC44953|C6C44983|D6C44953|C6C4491E|ISIISI
0043E380B7C44953|C6C449C3|BDC44953|C6C44962|ISI媒ISIb
0043E390CFC44953|C6C44912|D3C44953|C6C449E8|ISIISI
0043E3A0B8C44905|CEC4491E|B7C4498A|B9C449B4|IIII
0043E3B0D3C449B9|BBC449A6|D1C449E8|B8C449FE|IIII
0043E3C0C0C44982|D0C44953|D1C4492D|B8C44952|IISI-IR
0043E3D0BFC4493A|D0C449C3|BEC449A6|C1C449C3|I:I镁II
0043E3E0BEC4495E|B4C44982|D0C4497C|BDC449C3|I^II|I
0043E3F0BDC449C3|BEC4491E|B7C44961|BDC449A6|I镁IIaI
0043E400C1C44982|D0C44912|D3C449FE|C0C44925|IIII%
0043E410CEC4490C|D6C44909|BAC4492E|C8C44967|I.I.I.Ig
0043E420BEC4498A|B9C449EB|C2C449D4|CEC44995|IIII
0043E430C1C449D1|C7C44909|BAC44900|C7C449B9|II.I.I
0043E440BBC449D1|C7C449B4|D3C4495E|B4C449D1|III^I
0043E450C7C449A8|C4C449A6|C1C44912|D3C449B4|IIII
0043E460D3C449D0|BAC44941|C2C44982|D0C449B1|I泻IAII
0043E470B8C4491E|B5C44927|D2C44982|D0C44975|II'IIu
0043E480D1C4495E|B4C44925|CEC44916|B6C4497B|I^I%II{
0043E490CEC4497C|BDC449EB|C2C44927|D2C44983|I|II'I
0043E4A0D6C44912|D3C4492E|C8C4491E|B5C449E6|II.II
0043E4B0C4C449C3|BDC44995|C1C4491E|B5C449EC|I媒III
0043E4C0B7C449B9|BBC4495F|D0C44983|D6C4498A|II_II
0043E4D0B9C449A6|C1C449D4|CEC4498B|D3C4490D|IIII.
0043E4E0C0C449E8|B8C4492D|B8C44961|BDC44982|II-IaI
0043E4F0D0C44912|D3C4491E|B5C4497C|BDC449D1|III|I
0043E500C7C4497C|BDC44905|CEC449A6|C1C4495F|I|III_
0043E510D0C4491E|B7C4497B|CEC4490C|D6C44905|II{I.I
0043E520BEC4499F|C2C449B9|BBC4499F|C2C449D4|IIII
0043E530CEC4499F|C2C449EC|B7C4499F|C2C44962|IIIIb
0043E540CFC4499F|C2C4492D|B8C4499F|C2C4490C|II-II.
0043E550D6C4499F|C2C4490D|C0C4499F|C2C44905|II.II
0043E560BEC4499F|C2C449C3|BDC4499F|C2C44953|II媒IIS
0043E570D1C4499F|C2C44975|D1C4499F|C2C44905|IIuII
0043E580CEC4499F|C2C44975|D1C4499F|C2C44927|IIuII'
0043E590D2C4499F|C2C44909|BAC4499F|C2C449B9|II.II
0043E5A0B5C449E6|C4C44909|BAC4498B|D3C44925|II.II%
0043E5B0CEC4490D|C0C449B9|B5C449E9|C3C44912|I.III
0043E5C0D3C449FE|C0C44905|CEC44983|D6C4490D|IIII.
0043E5D0C0C449EC|B7C449D0|BAC449II泻I
虽然DispatchTable的数据很多,但是很多不同的偏移量指向的相同的数据,这是一种保护方式。我们反过来想,如果DispatchTable中每个dword指向的是不同的伪指令,这就意味着每个EAX偏移量指向着唯一的一条伪指令,进一步的来说就是每个ESI值代表着唯一的一条伪指令。那么,如果有人逆向这个算法,就可以知道每个ESI值对应的是哪个伪指令,这样就可以直接读取ESI值而了解VMP的执行伪指令,基本等于半自动识别VMP。一名对VMP经验丰富的人,只要看着VM执行的伪指令盯着EBPSTACK堆栈,就可以理解VM的情况。现在,由于多个ESI值指向相同的伪指令,还有动态EBX解码,将会艰难的多。
我们在OD中寻找一个空间,写一段循环代码,把DispatchTable的数据全部解密出来:
原来的代码:
0043F11F\38F5CMPCH,DH
0043F12166:FFC2INCDX
0043F1248B1485DBE14300MOVEDX,DWORDPTRDS:[EAX*4+43E1DB];*
0043F12BF9STC
0043F12C84F4TESTAH,DH
0043F12E60PUSHAD
0043F12F^E9CC5EFCFFJMP0043E100
把最后一条指令修改为:
0043F12F^\E9CC5EFCFFJMP00405000
在00405000添加循环代码:
CPUDisasm
AddressHexdumpCommandComments
0040500060PUSHAD
00405001BEDBE14300MOVESI,0043E1DB;DispatchTable地址
00405006BF00514000MOVEDI,00405100;解密循环地址
0040500BB900010000MOVECX,100
0040501031DBXOREBX,EBX
004050128B0433MOVEAX,DWORDPTRDS:[ESI+EBX]
00405015056B197FB6ADDEAX,B67F196B;解密指令只有1条ADD
0040501A89043BMOVDWORDPTRDS:[EDI+EBX],EAX
0040501D83C304ADDEBX,4
0040502049DECECX
00405021^75EFJNESHORT00405012
0040502361POPAD
00405024E98E900200JMP0043E100
循环结束后,在00405100中就还原了整个DispatchTable:
CPUDump
AddressHexdumpASCII
0040510074D34300|3BD44300|89D04300|51DE4300|tC.;C.C.QC.
00405110BEEA4300|70D74300|E0EA4300|3FE84300|C.pC.C.?C.
0040512077EF4300|2ED74300|E6E74300|D2D74300|wC..C.C.C.
0040513091D84300|56DC4300|EDE94300|A5D34300|C.VC.C.C.
0040514089CE4300|13DE4300|89CE4300|99E14300|C.C.C.C.
0040515024D54300|54DD4300|98D14300|00DB4300|$C.TC.C..C.
00405160EDE94300|E0EA4300|2ED84300|81CF4300|C.C..C.C.
0040517098D14300|E0EA4300|00DB4300|56DC4300|C.C..C.VC.
00405180BDD84300|1FED4300|F6EC4300|70E74300|C.C.C.pC.
00405190BDD84300|3FE84300|53D24300|2ED74300|C.?C.SC..C.
004051A02ED84300|C9CD4300|1CD24300|CCD64300|.C.C.C.C.
004051B0C8D84300|54DD4300|91D84300|CAE94300|C.TC.C.C.
004051C01FED4300|51DE4300|57D14300|89CE4300|C.QC.WC.C.
004051D078D94300|78D94300|2ED74300|C8D84300|xC.xC..C.C.
004051E0E6E74300|2ED74300|89CE4300|EDE94300|C..C.C.C.
004051F0F5D24300|11EB4300|56DC4300|3FE84300|C.C.VC.?C.
00405200CCD64300|74D34300|BEEA4300|CCD64300|C.tC.C.C.
00405210A5D34300|A5E94300|77EF4300|A5D34300|C.C.wC.C.
0040522089D04300|70E74300|78D94300|EDE94300|C.pC.xC.C.
0040523092EB4300|E7D64300|53D24300|ACDB4300|C.C.SC.C.
0040524054DD4300|90E74300|BEDF4300|CCD64300|TC.C.C.C.
00405250BEDF4300|EEEF4300|BEDF4300|C8D84300|C.C.C.C.
00405260BEDF4300|13DE4300|BEDF4300|CAE94300|C.C.C.C.
00405270BEDF4300|51DE4300|BEDF4300|A5D34300|C.QC.C.C.
00405280BEDF4300|6BE04300|BEDF4300|98D14300|C.kC.C.C.
00405290BEDF4300|90E74300|BEDF4300|EEEF4300|C.C.C.C.
004052A0BEDF4300|89D04300|BEDF4300|2ED74300|C.C.C..C.
004052B0BEDF4300|CDE84300|BEDF4300|7DEC4300|C.C.C.}C.
004052C0BEDF4300|53D24300|70E74300|89D04300|C.SC.pC.C.
004052D0F5D24300|1FED4300|24D54300|11EB4300|C.C.$C.C.
004052E053D24300|69DA4300|EDE94300|BEEA4300|SC.iC.C.C.
004052F098D14300|BDD84300|A5E94300|2ED84300|C.C.C..C.
0040530011DB4300|2ED84300|C9CD4300|EDE94300|C..C.C.C.
00405310E7D64300|2ED74300|2ED84300|89D04300|C..C..C.C.
00405320CCD64300|11DB4300|EDE94300|7DEC4300|C.C.C.}C.
0040533069DA4300|90E74300|77EF4300|74D34300|iC.C.wC.tC.
0040534099E14300|D2D74300|F5D24300|56DC4300|C.C.C.VC.
004053503FE84300|00DB4300|3CE14300|74D34300|?C..C.<C.tC.
004053606BE04300|24D54300|3CE14300|1FED4300|kC.$C.<C.C.
00405370C9CD4300|3CE14300|13DE4300|11DB4300|C.<C.C.C.
004053807DEC4300|1FED4300|3BD44300|ACDB4300|}C.C.;C.C.
00405390EDE94300|1CD24300|89CE4300|92EB4300|C.C.C.C.
004053A0EDE94300|E0EA4300|C9CD4300|90E74300|C.C.C.C.
004053B081CF4300|E6E74300|E7D64300|56DC4300|C.C.C.VC.
004053C092EB4300|EEEF4300|7DEC4300|99E14300|C.C.}C.C.
004053D089CE4300|51DE4300|2ED74300|00DB4300|C.QC..C..C.
004053E089CE4300|57D14300|24D54300|CAE94300|C.WC.$C.C.
004053F0EEEF4300|F5D24300|11DB4300|3FE84300|C.C.C.?C.
00405400F6EC4300|78D94300|53D24300|98D14300|C.xC.SC.C.
00405410CCD64300|EDE94300|7DEC4300|89CE4300|C.C.}C.C.
00405420E7D64300|3CE14300|E7D64300|70E74300|C.<C.C.pC.
0040543011DB4300|CAE94300|89D04300|E6E74300|C.C.C.C.
0040544077EF4300|70D74300|0ADC4300|24D54300|wC.pC..C.$C.
004054500ADC4300|3FE84300|0ADC4300|57D14300|.C.?C..C.WC.
004054600ADC4300|CDE84300|0ADC4300|98D14300|.C.C..C.C.
004054700ADC4300|77EF4300|0ADC4300|78D94300|.C.wC..C.xC.
004054800ADC4300|70D74300|0ADC4300|2ED74300|.C.pC..C..C.
004054900ADC4300|BEEA4300|0ADC4300|E0EA4300|.C.C..C.C.
004054A00ADC4300|70E74300|0ADC4300|E0EA4300|.C.pC..C.C.
004054B00ADC4300|92EB4300|0ADC4300|74D34300|.C.C..C.tC.
004054C00ADC4300|24CF4300|51DE4300|74D34300|.C.$C.QC.tC.
004054D0F6EC4300|90E74300|78D94300|24CF4300|C.C.xC.$C.
004054E054DD4300|7DEC4300|69DA4300|70E74300|TC.}C.iC.pC.
004054F0EEEF4300|78D94300|57D14300|3BD44300|C.xC.WC.;C.
Intel的LittleEndian(小尾)方式存储让我们看的非常的别扭。在OD的主窗口(CPU窗口)中来到00405000.data段,看一下00405100的显示:
004050F20000ADDBYTEPTRDS:[EAX],AL
004050F40000ADDBYTEPTRDS:[EAX],AL
004050F60000ADDBYTEPTRDS:[EAX],AL
004050F80000ADDBYTEPTRDS:[EAX],AL
004050FA0000ADDBYTEPTRDS:[EAX],AL
004050FC0000ADDBYTEPTRDS:[EAX],AL
004050FE0000ADDBYTEPTRDS:[EAX],AL
00405100^74D3JESHORT004050D5
0040510243INCEBX
00405103003BADDBYTEPTRDS:[EBX],BH
00405105D443AAM43
00405110BEEA430070MOVESI,700043EA
OD把我们的数据当做代码来显示了。点击右键-->Analysis-->AnalysecodeCtrl+A,弹出对话框是否分析,点击确定。显示如下:
004050FB00DB00
004050FC00DB00
004050FD00DB00
004050FE00DB00
004050FF00DB00
00405100.74D34300DD0043D374
00405104.3BD44300DD0043D43B
00405108.89D04300DD0043D089
0040510C.51DE4300DD0043DE51
00405110.BEEA4300DD0043EABE
00405114.70D74300DD0043D770
00405118.E0EA4300DD0043EAE0
0040511C.3FE84300DD0043E83F
00405120.77EF4300DD0043EF77
00405124.2ED74300DD0043D72E
00405128.E6E74300DD0043E7E6
0040512C.D2D74300DD0043D7D2
OD正确的以数据方式显示,并且已经按照我们日常的习惯把数据按照BigEndian(大尾)方式显示。
对于DispatchTable中重复的数据,我们也要把他清除,在刚才00405000的汇编代码下面继续:
CPUDisasm
AddressHexdumpCommandComments
0040500060PUSHAD
00405001BEDBE14300MOVESI,0043E1DB;DispatchTable地址
00405006BF00514000MOVEDI,00405100;解密循环地址
0040500BB900010000MOVECX,100
0040501031DBXOREBX,EBX
004050128B0433MOVEAX,DWORDPTRDS:[ESI+EBX]
00405015056B197FB6ADDEAX,B67F196B;解密指令只有1条ADD
0040501A89043BMOVDWORDPTRDS:[EDI+EBX],EAX
0040501D83C304ADDEBX,4
0040502049DECECX
00405021^75EFJNESHORT00405012
0040502361POPAD
00405024EB03JMPSHORT00405029
0040502690NOP
0040502790NOP
0040502890NOP
0040502960PUSHAD
0040502ABE00514000MOVESI,00405100;TispatchTable
0040502FBF005A4000MOVEDI,00405A00;newDispatchTable
00405034B900010000MOVECX,100
00405039BA00000000MOVEDX,0
0040503E8D1C8D00000000LEAEBX,[ECX*4]
004050458B06MOVEAX,DWORDPTRDS:[ESI]
0040504783F800CMPEAX,0
0040504A741AJESHORT00405066
0040504C8907MOVDWORDPTRDS:[EDI],EAX
0040504E83C704ADDEDI,4
0040505183C204ADDEDX,4
0040505439DACMPEDX,EBX
00405056740EJESHORT00405066
004050583B0432CMPEAX,DWORDPTRDS:[ESI+EDX]
0040505B^75F4JNESHORT00405051
0040505DC7043200000000MOVDWORDPTRDS:[ESI+EDX],0
00405064^EBEBJMPSHORT00405051
0040506683C604ADDESI,4
0040506949DECECX
0040506A^75CDJNESHORT00405039
0040506C61POPAD
0040506DE98E900200JMP0043E100
第一部分是前面解密代码,第二部分是分别比较00405100中的数据,把相同的全部放00000000,同时把非0的数据存入00405A00中。
执行完这些代码后,00405A00中生成了VM中所有的伪指令,在通过OD把它按照数据显示出来如下:
CPUDisasm
AddressHexdumpCommandComments
00405A00.\74D34300DD0043D374
00405A04.3BD44300DD0043D43B
00405A08.89D04300DD0043D089
00405A0C.51DE4300DD0043DE51
00405A10.BEEA4300DD0043EABE
00405A14.70D74300DD0043D770
00405A18.E0EA4300DD0043EAE0
00405A1C.3FE84300DD0043E83F
00405A20.77EF4300DD0043EF77
00405A24.2ED74300DD0043D72E
00405A28.E6E74300DD0043E7E6
00405A2C.D2D74300DD0043D7D2
00405A30.91D84300DD0043D891
00405A34.56DC4300DD0043DC56
00405A38.EDE94300DD0043E9ED
00405A3C.A5D34300DD0043D3A5
00405A40.89CE4300DD0043CE89
00405A44.13DE4300DD0043DE13
00405A48.99E14300DD0043E199
00405A4C.24D54300DD0043D524
00405A50.54DD4300DD0043DD54
00405A54.98D14300DD0043D198
00405A58.00DB4300DD0043DB00
00405A5C.2ED84300DD0043D82E
00405A60.81CF4300DD0043CF81
00405A64.BDD84300DD0043D8BD
00405A68.1FED4300DD0043ED1F
00405A6C.F6EC4300DD0043ECF6
00405A70.70E74300DD0043E770
00405A74.53D24300DD0043D253
00405A78.C9CD4300DD0043CDC9
00405A7C.1CD24300DD0043D21C
00405A80.CCD64300DD0043D6CC
00405A84.C8D84300DD0043D8C8
00405A88.CAE94300DD0043E9CA
00405A8C.57D14300DD0043D157
00405A90.78D94300DD0043D978
00405A94.F5D24300DD0043D2F5
00405A98.11EB4300DD0043EB11
00405A9C.A5E94300DD0043E9A5
00405AA0.92EB4300DD0043EB92
00405AA4.E7D64300DD0043D6E7
00405AA8.ACDB4300DD0043DBAC
00405AAC.90E74300DD0043E790
00405AB0.BEDF4300DD0043DFBE
00405AB4.EEEF4300DD0043EFEE
00405AB8.6BE04300DD0043E06B
00405ABC.CDE84300DD0043E8CD
00405AC0.7DEC4300DD0043EC7D
00405AC4.69DA4300DD0043DA69
00405AC8.11DB4300DD0043DB11
00405ACC.3CE14300DD0043E13C
00405AD0.0ADC4300DD0043DC0A
00405AD4.24CF4300DD0043CF24
这个VM一共有52条伪指令,在本节中我将一一列出这52条伪指令。每个分析VMP的人都有自己对伪指令的命名方式。
移动到EBPSTACK的数据使用PUSH指令,移动到EDISTACK的数据使用MOV指令。在VM中,对数据的操作包括byte和dword,而存储的方式是word和dword,当遇到byte和word交织在一起的时候,可以就把数据作为word操作来看。
下面我以:VM_PUSHw_MEMORYb为例说明我的命名规则:
伪指令的命名统一使用VM_开头;并接上直观的助记符PUSH;EBPSTACK是移动的目的地;MEMORY是移动的来源地;w代表word、b代表byte、dw代表dword;这条指令的表示:这是一条移动指令,把1个byte的数据从内存块移动到EBPSTACK,存储时是按照word来存储。
在VMP的伪指令中包含有大量的花指令和junkcode,在本文列出的伪指令都是去除了这些无用代码。
0043DC0A命名:
VM_MOVdw_EDISTACKdw_EBPSTACKdw
代码:
0043DC19|.F6D8NEGAL;*
0043DC26|.C0C807RORAL,7;*
0043DC34|.2C20SUBAL,20;*
0043DC41|.243CANDAL,3C;*
0043E080|$8B5500MOVEDX,DWORDPTRSS:[EBP];*
0043E086|.83C504ADDEBP,4;*
0043D3D7/>/891438MOVDWORDPTRDS:[EDI+EAX],EDX;*
功能:
把EBPSTACK栈顶1个dword的数据存储到EDISTACK

0043E7E6命名:
VM_MOVw_EDISTACKw_EBPSTACKw
代码:
0043E7EC0FB646FFMOVZXEAX,BYTEPTRDS:[ESI-1];*
0043E7F628D8SUBAL,BL;*
0043E7FEC0C805RORAL,5;*
0043E80CF6D8NEGAL;*
0043E816340EXORAL,0E;*
0043E82228C3SUBBL,AL;*
0043E82E66:8B5500MOVDX,WORDPTRSS:[EBP];*
0043E83583C502ADDEBP,2;*
0043F03F4EDECESI;*
0043F04566:891438MOVWORDPTRDS:[EDI+EAX],DX;*
功能:
把EBPSTACK栈顶1个word的数据存储到EDISTACK

0043D374命名:
VM_MOVb_EDISTACKb_EBPSTACKw
代码:
0043D377|.8A46FFMOVAL,BYTEPTRDS:[ESI-1];*
0043F148/>\30D8XORAL,BL;*
0043D460|.FEC0INCAL;|*
0043D469|.C0C807RORAL,7;|*
0043D473|.FEC0INCAL;|*
0043D215|.30C3XORBL,AL;*
0043EA9C|.4EDECESI;*
0043EAA0|.66:8B5500MOVDX,WORDPTRSS:[EBP];*
0043EAAC|.83C502ADDEBP,2;*
0043DBDA/>/881438MOVBYTEPTRDS:[EDI+EAX],DL;*
把EBPSTACK栈顶1个word的数据按照byte的方式存储到EDISTACK

0043D21C命名:
VM_PUSHw_IMMEDIATEw
代码:
0043D21F66:8B46FEMOVAX,WORDPTRDS:[ESI-2];*
0043D22D86E0XCHGAL,AH;*
0043E01A66:29D8SUBAX,BX;*
0043E02266:0571F2ADDAX,0F271;*
0043E03666:F7D8NEGAX;*
0043E03D66:35A61CXORAX,1CA6;*
0043E05466:29C3SUBBX,AX;*
0043E05466:29C3SUBBX,AX;*
0043E9768D76FELEAESI,[ESI-2];*
0043D094/66:894500MOVWORDPTRSS:[EBP],AX;*
功能:
从ESI数据中取得1个word的立即数压入EBPSTACK

0043E83F命名:
VM_PUSHdw_IMMEDIATEdw
代码:
0043E845.8B46FCMOVEAX,DWORDPTRDS:[ESI-4];*
0043E84D.0FC8BSWAPEAX;*
0043E852.01D8ADDEAX,EBX;*
0043E857.C1C804ROREAX,4;*
0043D952.8D76FCLEAESI,[ESI-4];*
0043D956.2DE131FF38SUBEAX,38FF31E1;*
0043D967.C1C00AROLEAX,0A;|*
0043D96C.01C3ADDEBX,EAX;|*
0043D970.83ED04SUBEBP,4;|*
0043D710|$894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
从ESI数据中获得1个dword的立即数,压入EBPSTACK

0043DB11命名:
VM_PUSHdw_IMMEDIATEw
代码:
0043DB1E66:8B46FEMOVAX,WORDPTRDS:[ESI-2];*
0043D171/86E0XCHGAL,AH;*
0043E94866:29D8SUBAX,BX;*
0043E95166:0571F2ADDAX,0F271;*
0043E95C66:F7D8NEGAX;*
0043E9698D76FELEAESI,[ESI-2];*
0043D62C66:35A61CXORAX,1CA6;*
0043D640\66:29C3SUBBX,AX;*
0043D64898CWDE;*
0043D19083ED04SUBEBP,4;*
0043D933894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
从ESI数据中获得1个word的立即数,按照dword的方式压入EBPSTACK

0043D978命名:
VM_PUSHw_IMMEDIATEb
代码:
0043D979.0FB646FFMOVZXEAX,BYTEPTRDS:[ESI-1];*
0043D97E.30D8XORAL,BL;*
0043D1ED.FEC8DECAL;*
0043D1F0.F6D8NEGAL;*
0043D1F7.F6D0NOTAL;*
0043D1FD.30C3XORBL,AL;*
0043CEE8>/83ED02SUBEBP,2;*
0043DD79|.66:894500MOVWORDPTRSS:[EBP],AX;|*
0043DD62/$4EDECESI;*
功能:
从ESI数据中获得1个byte的立即数,按照word的方式压入EBPSTACK

0043D3A5命名:
VM_PUSHdw_IMMEDIATEb
代码:
0043D3A70FB646FFMOVZXEAX,BYTEPTRDS:[ESI-1];*
0043D3AC30D8XORAL,BL;*
0043D848FEC8DECAL;*
0043D855F6D8NEGAL;*
0043D866F6D0NOTAL;*
0043D86D30C3XORBL,AL;*
0043ED8C66:98CBW;*
0043CF7B98CWDE;*
0043EC008D76FFLEAESI,[ESI-1];*
0043DB9483ED04SUBEBP,4;*
0043DB9F894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
从ESI数据中获得1个byte的立即数,按照dword的方式压入EBPSTACK

0043CF24命名:
VM_ADDdw_EBPSTACK
代码:
0043CF2F|.8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043EED3|.014504ADDDWORDPTRSS:[EBP+4],EAX;*
0043CE4F|.9CPUSHFD;*
0043CE50|.8F44243CPOPDWORDPTRSS:[ESP+3C];*
0043D1BF/>\FF74243CPUSHDWORDPTRSS:[ESP+3C];*
0043D1C3|.8F4500POPDWORDPTRSS:[EBP];*
功能:
把EBPSTACK栈顶的2个dword数据相加,结果存储在[EBP+4],EFLAGS标志存储在栈顶。
例:
0013F97C8021D2F0!
0013F98000000000....
VM_ADDdw_EBPSTACK
0013F97C00000286..
0013F9808021D2F0!

0043D43B命名:
VM_PUSHdw_MEMORYdw
代码:
0043D43F8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D10A8B00MOVEAX,DWORDPTRDS:[EAX];*
0043D447894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
把EBPSTACK栈顶数据作为内存地址,从中读取1个dword的数据压入EBPSTACK

0043E9CA命名:
VM_PUSHw_MEMORYw
代码:
0043E9D08B4500MOVEAX,DWORDPTRSS:[EBP];*
0043E9D983C502ADDEBP,2;*
0043DEBB66:36:8B00MOVAX,WORDPTRSS:[EAX];*
0043DDC466:894500MOVWORDPTRSS:[EBP],AX;*
功能:
把EBPSTACK栈顶数据作为内存地址,从中读取1个word的数据压入EBPSTACK

0043D8BD命名:
VM_PUSHw_MEMORYb
代码:
0043D57B|.8B5500MOVEDX,DWORDPTRSS:[EBP]
0043D589|.83C502ADDEBP,2;*
0043D591|.8A02MOVAL,BYTEPTRDS:[EDX];*
0043E70B|.66:894500MOVWORDPTRSS:[EBP],AX;*
功能:
把EBPSTACK栈顶数据作为内存地址,从中读取1个byte的数据,按照word的方式压入EBPSTACK

0043DC56命名:
VM_PUSHw_EDISTACKw
代码:
0043DC5C8A46FFMOVAL,BYTEPTRDS:[ESI-1];*
0043DC6628D8SUBAL,BL;*
0043DC6DC0C805RORAL,5;*
0043EADA4EDECESI;*
0043EE2E\F6D8NEGAL;*
0043EE34340EXORAL,0E;*
0043E74028C3SUBBL,AL;*
0043E74666:8B0438MOVAX,WORDPTRDS:[EDI+EAX];*
0043D9E483ED02SUBEBP,2;*
0043EE4466:894500MOVWORDPTRSS:[EBP],AX;*
功能:
从EDISTACK中读取1个word数据压入EBPSTACK

0043CF81命名:
VM_PUSHw_EDISTACKb
代码:
0043CF848A46FFMOVAL,BYTEPTRDS:[ESI-1];*
0043CF8E30D8XORAL,BL;*
0043EE0A\FEC0INCAL;*
0043EE11C0C807RORAL,7;*
0043EE1EFEC0INCAL;*
0043D59C30C3XORBL,AL;*
0043D2CE4EDECESI;*
0043D2D78A0438MOVAL,BYTEPTRDS:[EDI+EAX];*
0043D2E683ED02SUBEBP,2;*
0043D07566:894500MOVWORDPTRSS:[EBP],AX;*
功能:
从EDISTACK中读取1个byte数据,按照word方式压入EBPSTACK

0043D72E命名:
VM_PUSHdw_EBP
代码:
0043D72F/.89E8MOVEAX,EBP;*
0043E613/$83ED04SUBEBP,4;*
0043E61C|.894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
复制EBP指针到EBPSTACK栈顶
例:
EBP0013F9AC
0013F9AC00000000....
VM_PUSHdw_EBP
0013F9A80013F9AC.
0013F9AC00000000....

0043EABE命名:
VM_COPYdw_EBPSTACK
代码:
0043EACC8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043DE1B36:8B00MOVEAX,DWORDPTRSS:[EAX];*
0043DE25894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
把EBPSTACK栈顶数据作为堆栈地址,从中读取一个dword的数据压入EBPSTACK
例:
0013F998F99E
0013F99C02460013.F
0013F9A00000...
VM_COPYdw_EBPSTACK
0013F9980246
0013F99C02460000..F
0013F9A00000...

0043E790命名:
VM_COPYw_EBPSTACK
代码:
0043E79C|.8B5500MOVEDX,DWORDPTRSS:[EBP];*
0043E7A7|.83C502ADDEBP,2;*
0043E7AE|.36:8A02MOVAL,BYTEPTRSS:[EDX];*
0043D01B|.66:894500MOVWORDPTRSS:[EBP],AX;*
功能:
把EBPSTACK栈顶数据作为堆栈地址,从中读取一个byte的数据,按照word的方式压入EBPSTACK
例:
0013F9A80013F9AC.
0013F9AC0000....
VM_COPYw_EBPSTACK
0013F9A80000
0013F9AC0000....

0043D198命名:
VM_NANDdw
代码:
0043D1A38B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D1AD8B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043DEAEF7D0NOTEAX;*
0043DDE1/F7D2NOTEDX;*
0043CDC221D0ANDEAX,EDX;*
0043E0F8894504MOVDWORDPTRSS:[EBP+4],EAX;*
0043D0FB/9CPUSHFD;*
0043D0FC8F44242CPOPDWORDPTRSS:[ESP+2C];*
0043EC46FF742434PUSHDWORDPTRSS:[ESP+34];*
0043EC4A8F4500POPDWORDPTRSS:[EBP];*
功能:
dword版的与非门,从EBPSTACK的栈顶读取2个dword作为操作数,结果存储在第二个操作数位置,EFLAGS标志存储在栈顶。
例:
0013F9A800000286..
0013F9AC00000286..
VM_NANDdw
0013F9A800000282..
0013F9ACFFFFFD79y

0043EB92命名:
VM_NANDw
代码:
0043EB94|.66:8B4500MOVAX,WORDPTRSS:[EBP];*
0043EBA5|.66:8B5502MOVDX,WORDPTRSS:[EBP+2];*
0043EBB3|.F6D0NOTAL;*
0043EBBB|.F6D2NOTDL;*
0043EBC1|.83ED02SUBEBP,2;*
0043EBC5|.20D0ANDAL,DL;*
0043EBCD|.66:894504MOVWORDPTRSS:[EBP+4],AX;*
0043EBD5|.9CPUSHFD;*
0043D26F|$FF742428PUSHDWORDPTRSS:[ESP+28];*
0043D273|.8F4500POPDWORDPTRSS:[EBP];*
功能:
word版的与非门,从EBPSTACK的栈顶读取2个word作为操作数,结果存储在第二个操作数位置,EFLAGS标志存储在栈顶。
例:
EBP0013F9AA
0013F9A80000
0013F9AC0000....
VM_NANDw
0013F9A800000286..
0013F9AC00FF...

0043EB11命名:
VM_ADDw_EBPSTACK
代码:
0043EB14|.8A4500MOVAL,BYTEPTRSS:[EBP];*
0043EB1C|.83ED02SUBEBP,2;*
0043EB21|.004504ADDBYTEPTRSS:[EBP+4],AL;*
0043EB26|.9CPUSHFD;*
0043EB27|.8F442420POPDWORDPTRSS:[ESP+20];*
0043E8F9|>/FF742440PUSHDWORDPTRSS:[ESP+40];*
0043E8FD|.|8F4500POPDWORDPTRSS:[EBP];*
功能:
把EBPSTACK栈顶的2个word数据中的低byte相加,结果存储在第二个操作数位置,EFLAGS标志存储在栈顶。
例:
0013F9AC000000FF...
VM_ADDw_EBPSTACK
0013F9A80286
0013F9AC00FF0000...

0043DFBE命名:
VM_PUSHdw_EDISTACKdw
代码:
0043DFC1F6D8NEGAL;*
0043DFCDC0C807RORAL,7;*
0043DFDA2C20SUBAL,20;*
0043DFDD243CANDAL,3C;*
0043CE6C8B1438MOVEDX,DWORDPTRDS:[EDI+EAX];*
0043CE7183ED04SUBEBP,4;*
0043CE79895500MOVDWORDPTRSS:[EBP],EDX;*
功能:
把1个dword的数据从EDISTACK压入EBPSTACK

0043D7D2命名:
VM_SHRdw_EBPSTACK
代码:
0043D7DA8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D7E68A4D04MOVCL,BYTEPTRSS:[EBP+4];*
0043D4F883ED02SUBEBP,2;*
0043D504D3E8SHREAX,CL;*
0043F17D\894504MOVDWORDPTRSS:[EBP+4],EAX;*
0043EA2E9CPUSHFD;*
0043EA30FF742420PUSHDWORDPTRSS:[ESP+20];*
0043EA348F4500POPDWORDPTRSS:[EBP];*
功能:
把EBPSTACK栈顶1个dword作为操作数,[EBP+4]作为移动位数,逻辑右移。结果dword存储在第二个操作数和第一个操作数的高byte,EFLAGS标志存储在栈顶。
例:
0013F99C0040
0013F9A000040000...
VM_SHRdw_EBPSTACK
0013F99C00000202..
0013F9A000000004...

0043E9A5命名:
VM_SHLdw_EBPSTACK
代码:
0043E9A98B4500MOVEAX,DWORDPTRSS:[EBP];*
0043E9B58A4D04MOVCL,BYTEPTRSS:[EBP+4];*
0043E0B2>83ED02SUBEBP,2;*
0043E0BCD3E0SHLEAX,CL;*
0043CDEA894504MOVDWORDPTRSS:[EBP+4],EAX;*
0043DD1A\9CPUSHFD
0043DD1B8F442428POPDWORDPTRSS:[ESP+28]
0043DD24FF74242CPUSHDWORDPTRSS:[ESP+2C];*
0043DD288F4500POPDWORDPTRSS:[EBP];*
功能:
把EBPSTACK栈顶1个dword作为操作数,[EBP+4]作为移动位数,逻辑左移。结果dword存储在第二个操作数和第一个操作数的高byte,EFLAGS标志存储在栈顶。

0043DE51命名:
VM_SHRDdw_EBPSTACK
代码:
0043DE5D8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043DE698B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043DE6E8A4D08MOVCL,BYTEPTRSS:[EBP+8];*
0043DE7383C502ADDEBP,2;*
0043DE7A0FADD0SHRDEAX,EDX,CL;*
0043D38F894504MOVDWORDPTRSS:[EBP+4],EAX;*
0043D66C9CPUSHFD;*
0043D66D8F442434POPDWORDPTRSS:[ESP+34];*
0043D67FFF742440PUSHDWORDPTRSS:[ESP+40];*
0043D6838F4500POPDWORDPTRSS:[EBP];*
功能:
EBPSTACK双精度右移指令,执行完毕后,结果和EFLAGS存储到EBPSTACK

0043D524命名:
VM_SHLDdw_EBPSTACK
代码:
0043D5298B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D5378B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043D5458A4D08MOVCL,BYTEPTRSS:[EBP+8];*
0043D55083C502ADDEBP,2;*
0043D5580FA5D0SHLDEAX,EDX,CL;*
0043D637894504MOVDWORDPTRSS:[EBP+4],EAX;*
0043CED39CPUSHFD
0043D8F4\FF742434PUSHDWORDPTRSS:[ESP+34];*
0043D8F88F4500POPDWORDPTRSS:[EBP];*
功能:
EBPSTACK双精度左移指令,执行完毕后,结果和EFLAGS存储到EBPSTACK

0043D089命名:
VM_JMP
代码:
0043D7228B7500MOVESI,DWORDPTRSS:[EBP];*
0043EF1F\83C504ADDEBP,4;*
0043E6A989F3MOVEBX,ESI;*
0043E6B8037500ADDESI,DWORDPTRSS:[EBP];*
功能:
把EBPSTACK栈顶地址移动到ESI,重新初始化EBX和ESI。

0043EF77命名:
VM_EBPSTACK_CALL
代码:
0043EF7B0FB646FFMOVZXEAX,BYTEPTRDS:[ESI-1];*
0043EF8230D8XORAL,BL;*
0043EF8DFEC8DECAL;*
0043EF99F6D8NEGAL;*
0043EFAF8D76FFLEAESI,[ESI-1];*
0043EFB3F6D0NOTAL;*
0043EFC430C3XORBL,AL;*
0043EFCD0FB6C8MOVZXECX,AL;*
0043EFDC894DFCMOVDWORDPTRSS:[EBP-4],ECX;*

0043ECEA31C0XOREAX,EAX;*
0043E0C687448D00XCHGDWORDPTRSS:[ECX*4+EBP],EAX;*parameter
0043E0CD89442424MOVDWORDPTRSS:[ESP+24],EAX;*
0043EE8983E901SUBECX,1;*
0043EE9C^\0F853FFEFFFFJNE0043ECE1;*
0043CF5B29C0SUBEAX,EAX;*
0043CF6AC7442404B7EE4MOVDWORDPTRSS:[ESP+4],0043EEB7;*
0043CF60874500XCHGDWORDPTRSS:[EBP],EAX;*
0043DDF989442408MOVDWORDPTRSS:[ESP+8],EAX;*
0043DDFDFF742404PUSHDWORDPTRSS:[ESP+4];*
0043DE0CFF742434PUSHDWORDPTRSS:[ESP+34];*
0043DE10C23800RETN38;VM_APICALL
功能:
VM中最复杂的伪指令,用于系统API调用和程序过程调用。ESI数据中取得参数的个数,EAX循环取得参数,压入ESP指针指向的常规堆栈。大量使用[ESP+X]的方式调用,掺杂着废压栈操作,静态看代码难以看出。返回地址是常量压入的0043EEB7。这条伪指令涉及内容众多,分支庞大,系统API调用和程序过程调用的走向都是不同的,在后面章节详述。我这里列举的是一次只有1个参数的系统API调用

0043D891命名:
VM_MOVdw_MEMORYdw_EBPSTACKdw
代码:
0043D8978B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D8A18B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043D8A683C508ADDEBP,8;*
0043D8AA8910MOVDWORDPTRDS:[EAX],EDX;*
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个dword存储到地址内

0043EFEE命名:
VM_MOVdw_MEMORYdw_EBPSTACKdw
代码:
0043EFF38B4500MOVEAX,DWORDPTRSS:[EBP];*
0043F0058B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043F01083C508ADDEBP,8;*
0043D33536:8910MOVDWORDPTRSS:[EAX],EDX;*
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个dword存储到地址内。与上一条伪指令完全相同

0043D157命名:
VM_MOVdw_MEMORYdw_EBPSTACKdw
代码:
0043D1598B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D1698B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043CDF783C508ADDEBP,8;*
0043CE0926:8910MOVDWORDPTRES:[EAX],EDX;*
EBPSTACK栈顶数据作为地址,把栈顶的第二个dword存储到地址内。与上两条伪指令完全相同

0043E9ED命名:
VM_MOVw_MEMORYw_EBPSTACKw
代码:
0043E9F78B4500MOVEAX,DWORDPTRSS:[EBP];*
0043E9FD66:8B5504MOVDX,WORDPTRSS:[EBP+4];*
0043EA0283C506ADDEBP,6;*
0043EA0D66:8910MOVWORDPTRDS:[EAX],DX;*
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个word存储到地址内

0043D6CC命名:
VM_MOVb_MEMORYb_EBPSTACKb
代码:
0043D6D38B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D6DB8A5504MOVDL,BYTEPTRSS:[EBP+4];*
0043EC6C83C506ADDEBP,6;*
0043D49536:8810MOVBYTEPTRSS:[EAX],DL;*
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个byte存储到地址内

0043CE89命名:
VM_HASH
代码:
0043CE988B5500MOVEDX,DWORDPTRSS:[EBP];*
0043CEA083C504ADDEBP,4;*
0043CEA631C0XOREAX,EAX;*
0043DCC089C1MOVECX,EAX;*
0043E6FAC1E007SHLEAX,7;*
0043E701C1E919SHRECX,19;*
0043D2BD/09C8OREAX,ECX;*
0043D7EF\3202XORAL,BYTEPTRDS:[EDX];*
0043D7F242INCEDX;*
0043DD12FF4D00DECDWORDPTRSS:[EBP];*
0043F023^\0F857FDEFFFFJNE0043CEA8;*
0043D9FA894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
计算一段数据的HASH值,EBPSTACK栈顶第一个dword是数据地址,第二个dword是数据大小

0043DE13命名:
VM_MOVdw_EBPreg_EBPSTACK
代码:
0043F134\8B6D00MOVEBP,DWORDPTRSS:[EBP];*
功能:
给EBP寄存器赋值EBPSTACK栈顶数据

0043DD54命名:
VM_FS:[EBPSTACK]
代码:
0043DD5A8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043F10E64:8B00MOVEAX,DWORDPTRFS:[EAX];*
0043F112894500MOVDWORDPTRSS:[EBP],EAX;*
功能:
读取FS[X]数据,X=EBPSTACK栈顶数据

0043D8C8命名:
VM_SEH
代码:
0043D8CF8B4500MOVEAX,DWORDPTRSS:[EBP];*
0043D8DE8B5504MOVEDX,DWORDPTRSS:[EBP+4];*
0043D8E783C508ADDEBP,8;*
0043D24364:8910MOVDWORDPTRFS:[EAX],EDX;*
功能:
给FS[X]传递Y数据,X=EBPSTACK栈顶数据,Y=EBPSTACK栈顶第2个数据。在实践中都是用于给FS[0]赋值,构建SEH

0043DA69命名:
VM_EXIT
代码:
0043DA6F89ECMOVESP,EBP;*
0043DA7358POPEAX;*
0043DA7E59POPECX;*
0043DA879DPOPFD;*
0043DA8D5DPOPEBP;*
0043CDB1/59POPECX;*
0043CDB88B5C2408MOVEBX,DWORDPTRSS:[ESP+8];*
0043F0688B6C2414MOVEBP,DWORDPTRSS:[ESP+14];*
0043F06D8B442438MOVEAX,DWORDPTRSS:[ESP+38];*
0043F06D8B442438MOVEAX,DWORDPTRSS:[ESP+38];*
0043DC998B7C2444MOVEDI,DWORDPTRSS:[ESP+44];*
0043DCA75EPOPESI;*
0043DCB6FF742404PUSHDWORDPTRSS:[ESP+4];*
0043DCBAC20800RETN8;*
功能:
给各个寄存器赋值EBPSTACK中的数据,EBPSTACK中的最后一个数据是跳转地址

0043EC7D命名:
VM_MOVdw_EFLreg_EBPSTACK
代码:
0043EC80FF7500PUSHDWORDPTRSS:[EBP];*
0043EC838F442408POPDWORDPTRSS:[ESP+8];*
0043EC8EFF742428PUSHDWORDPTRSS:[ESP+28];*
0043EC929DPOPFD;*
功能:
给EFLAGE寄存器赋值EBPSTACK栈顶数据

在F7跟踪加壳记事本的过程中,并不是所有的伪指令都使用到了,以下是没有被执行到的伪指令:
00405A14.70D74300DD0043D770
00405A18.E0EA4300DD0043EAE0
00405A48.99E14300DD0043E199
00405A58.00DB4300DD0043DB00
00405A5C.2ED84300DD0043D82E
00405A68.1FED4300DD0043ED1F
00405A6C.F6EC4300DD0043ECF6
00405A70.70E74300DD0043E770
00405A74.53D24300DD0043D253
00405A78.C9CD4300DD0043CDC9
00405A94.F5D24300DD0043D2F5
00405AA4.E7D64300DD0043D6E7
00405AA8.ACDB4300DD0043DBAC
00405AB8.6BE04300DD0043E06B
00405ABC.CDE84300DD0043E8CD
00405ACC.3CE14300DD0043E13C
由于没有实际的走过这些伪指令,静态分析后觉得,有个别伪指令的代码怕提取错了。把这些指令写成简介模式:
0043D770
EBPSTACK的byte逻辑右移指令
0043EAE0
VM_JMP跳转指令,重新给ESI赋值EBPSTACK栈顶数据
0043E199
复制EBPSTACK栈顶1个word的数据
0043DB00
把EBPSTACK栈顶数据作为地址,读取其中1个word的数据压入EBPSTACK
0043D82E
VM_DIV除法指令
0043ED1F
CPUID指令,结果压入EBPSTACK。
0043ECF6
把EBPSTACK数据1个byte移动到栈顶内存地址内
0043E770
给EBP寄存器的低word位赋值栈顶数据
0043D253
把SS段寄存器压入EBPSTACK栈顶
0043CDC9
另一种方式的word版NAND,不过这个是在EBPSTACK堆栈内完成运算过程
0043D2F5
EBPSTACK的byte逻辑左移指令
0043D6E7
EBPSTACK的word逻辑左移指令
0043DBAC
EBPSTACK的word逻辑右移指令
0043E06B
EBPSTACK的word加法
0043E8CD
把EAX和EDX压入EBPSTACK
0043E13C
把EBPSTACK数据1个word移动到栈顶内存地址内

到这里,所有的伪指令都罗列完毕,真的是体力活呀!
2.综合运用
2.1.常见伪指令组合

在VMP的伪指令的执行中有一些常见的组合套路,熟悉它们能让我们在跟踪VMP时更加的得心应手。这些组合与操作数的长度是无关的,下面的伪指令将去掉bwdw等标记。在例子部分,我将使用dword操作数来举例,直观明了。
2.1.1.
VM_PUSH_EBP;复制EBP指针到EBPSTACK栈顶
VM_COPY_EBPSTACK;把EBPSTACK栈顶数据作为堆栈地址,从中读取一个数据压入EBPSTACK
这两条指令是VMP中结合的极其紧密的组合,它们几乎总是一起出现的,用于把EBPSTACK堆栈中的数据复制起来到EBPSTACK。而很多情况下它们复制的就是原来的栈顶数据。在使用NAND来完成NOT(A)的运算中,它们是必备的前奏。凡是需要把操作数一个变两个的地方都有它们的身影。
例:
EBP0013F9AC
0013F9AC00000000....
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSH_EBP
EBP0013F9A8
0013F9A80013F9AC.
0013F9AC00000000....
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_COPY_EBPSTACK
EBP0013F9A8
0013F9A800000000....
0013F9AC00000000....
0013F9B07C92118A|;RETURNtontdll.7C92118A
2.1.2.
VM_NAND|VM_ADD_EBPSTACK|VM_SHLD_EBPSTACK|VM_SHR_EBPSTACK等等
VM_MOV_EDISTACK_EBPSTACK;把1个数据从EBPSTAK栈顶移动到EDISTACK,使用EAX作为偏移量
在VMP所有的运算伪指令中都是统一的模式,运算后的EFLAGS寄存器值位于EBPSTACK栈顶,运算结果位于紧接栈顶的[EBP+4]。在运算结束后,跟上一条VM_MOV_EDISTACK_EBPSTACK把运算后的标志位移动到EDISTACK,在很多时候,这都是一条废指令操作,纯粹是为去掉栈顶数据,以便继续操作运算结果。
如果接下来VM进行检测标志位的相关操作,这条指令就变得异常重要。例如:在对系统函数的CC码int3断点检测中,取出系统函数开头的第一个byte数据XX,把它与CC相减,再跟上一个ZF标志位检测+跳转。在这个时候反过来,运算结果完全无用,而我们一定要在移动指令的EAX偏移量哪里下好断点,观察好EFLAGS寄存器值的走向与来源。
2.1.3.
在进行跳转时,围绕VM_JMP的前后,有大量无价值的数据移动操作。假设现在我们刚进行了一次条件判断,VM刚刚把要跳转的地址确定并解密出来:
EBP0013F9A8
0013F9A800000202..;最后一次解密运算得到的EFLAGS
0013F9AC0043651AeC.;跳转地址
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
EBP0013F9B0
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
EBP0013F980
0013F9808021D2F0!
0013F9840013F9C0.
0013F98800000246F..
0013F98C00000020...
0013F990000359F4Y.
0013F9940013F9CC.
0013F99800400000..@.;OFFSETNOTEPAD
0013F99C00000000....
0013F9A0004253CDSB.;RETURNfromNOTEPAD.004255DBtoNOTEPAD.004253CD
0013F9A4000359F4Y.
0013F9A800400000..@.;该带着走的数据都要在EBPSTACK里面带着走,到这里还没有完毕的。
0013F9AC0043651AeC.;还有其他的数据要放入,8021D2F0要隐藏一下
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F97C7FDE2D10-
0013F9808021D2F0!
0013F9840013F9C0
VM_ADDdw_EBPSTACK
0013F97C00000247G..
0013F98000000000....
0013F9840013F9C0
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F98000000000....
0013F9840013F9C0
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
0013F9780043651AeC.
0013F97C00000000....
0013F98000000000....
0013F9840013F9C0.
0013F98800000246F..
0013F98C00000020...
0013F990000359F4Y.
0013F9940013F9CC.
0013F99800400000..@.;OFFSETNOTEPAD
0013F99C00000000....
0013F9A0004253CDSB.;RETURNfromNOTEPAD.004255DBtoNOTEPAD.004253CD
0013F9A4000359F4Y.
0013F9A800400000..@.;OFFSETNOTEPAD.B
0013F9AC0043651AeC.
0013F9B07C92118A|;RETURNtontdll.7C92118A

VM_JMP;带着14个数据,VM终于跳转,除了栈顶0043651A放入ESI,其他13个数据要重新保存

VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F98000000000....
0013F9840013F9C0.
VM_PUSHdw_IMMEDIATEdw
0013F97C8021D2F0!
0013F98000000000....
0013F9840013F9C0.
VM_ADDdw_EBPSTACK
0013F97C00000286..
0013F9808021D2F0!
0013F9840013F9C0.;重新恢复出来
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw;到这里停一下,搞个小运算,原EDX=000359F4XOR4DFD2FC2
0013F990000359F4Y.
0013F9940013F9CC.
0013F99800400000..@.;OFFSETNOTEPAD.B
0013F99C00000000....
0013F9A0004253CDSB.;RETURNfromNOTEPAD.004255DBtoNOTEPAD.004253CD
0013F9A4000359F4Y.
0013F9A800400000..@.;OFFSETNOTEPAD.B
0013F9AC0043651AeC.
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_EBP
0013F98C0013F990.
0013F990000359F4Y.
0013F9940013F9CC.
VM_COPYdw_EBPSTACK
0013F98C000359F4Y.
0013F990000359F4Y.
0013F9940013F9CC.
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F990000359F4Y.
0013F9940013F9CC.
VM_PUSHdw_EBP
0013F98C0013F990.
0013F990000359F4Y.
0013F9940013F9CC.
VM_COPYdw_EBPSTACK
0013F98C000359F4Y.
0013F990000359F4Y.
0013F9940013F9CC.
VM_NANDdw
0013F98C00000282..
0013F990FFFCA60B
0013F9940013F9CC.
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F990FFFCA60B
0013F9940013F9CC.
VM_PUSHdw_IMMEDIATEdw
0013F98CB202D03D=
0013F990FFFCA60B
0013F9940013F9CC.
VM_NANDdw
0013F98C00000206..
0013F990000109C0..
0013F9940013F9CC.
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F990000109C0..
0013F9940013F9CC.
VM_PUSHdw_IMMEDIATEdw
0013F98C4DFD2FC2/M
0013F990000109C0..
0013F9940013F9CC.
VM_PUSHdw_EDISTACKdw
0013F988000359F4Y.
0013F98C4DFD2FC2/M
0013F990000109C0..
0013F9940013F9CC.
VM_NANDdw
0013F98800000286..
0013F98CB2008009..
0013F990000109C0..
0013F9940013F9CC.
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F98CB2008009..
0013F990000109C0..
0013F9940013F9CC.
VM_NANDdw
0013F98C00000206..
0013F9904DFE76366vM
0013F9940013F9CC.
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
EBP0013F9B0
0013F9B07C92118A|;RETURNtontdll.7C92118A
每一次VM_JMP跳转,都要带着14个数据转,而其中呢VM还要搞上一点暗码转移。以后看到是VM_JMP跳转的状况,就是看着EBP指针,哗哗哗的让它执行,完毕了才停下来。中间的操作完全可以无视,我也是为了完整的表达才把它粘贴出了代码,实际看的时候,不用管。整个过程:要带着走的数据移到EBPSTACK-->VM_JMP跳转-->重新把数据保存到EDISTACK。关于其中000359F4XOR4DFD2FC2的过程,请参考下一节2.2.NAND。
由于其他的组合都和NAND或标志位检测+跳转相关,放在下两节中。这一节中的3个组合熟悉后,已经可以无视掉一部分VM的操作。

2.2.NAND(与非门)
本文的两节重头戏来了,NAND(与非门)与EFLAGS标志位检测+跳转,理解完了这两节后,对于VM就可以无视了,一切伪指令在你眼里都是正常的指令。跟踪VMP就和跟踪普通程序一样,想看API获取就看API获取,想看看程序的anti方式就看anti方式。一切都回到了正常,你可以看穿VM(虚拟机)这个吓人的外衣。
2.2.1.NAND起源
NAND(与非门)和NOR(或非门)来源于deMorgan'sLaws(德·摩根定律),运用于逻辑、数字电路等方面,本节专注于它与andorxornot之间的联系。
德·摩根定律是属于逻辑学的定律。德·摩根定律(或称德·摩根定理)是形式逻辑中有关否定所描述的系统方式中的逻辑运算符对偶对的一系列法则。由此引出的关系也就被称为“德·摩根二重性”。
奥古斯都·德·摩根首先发现了在命题逻辑中存在着下面这些关系:
非(P且Q)=(非P)或(非Q)
非(P或Q)=(非P)且(非Q)
德·摩根的发现影响了乔治·布尔从事的逻辑问题代数解法的研究,这巩固了德·摩根作为该规律的发现者的地位,尽管亚里士多德也曾注意到类似现象、且这也为古希腊与中世纪的逻辑学家熟知(引自Bocheński《形式逻辑历史》)。(引自维基百科,关键字:德·摩根定律)
我们再来看它在数学逻辑中的表示:

(引自:MathWorld,关键字:deMorgan'sLaws)
由于不是用我们熟悉的计算机方式来表达,上面的两段解说比较抽象,请看2.2.2.
2.2.2.NAND与逻辑运算
在加壳记事本中使用的是NAND,下面部分将专注于NAND。对于NOR,理论都是一样的,只是不用NAND来实现。
NAND(A,B):
NOT(A)
NOT(B)
AND(A,B)
这就是NAND的操作方式。NAND的价值在于:使用NAND可以实现NOTANDORXOR这4个逻辑运算。
NOT(A):
NAND(A,A)
AND(A,B):
NAND(NAND(A,A),NAND(B,B))
OR(A,B):
NAND(NAND(A,B),NAND(A,B))
XOR(A,B):
NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
2.2.3.VMP伪指令执行过程
NOT(4DBE4AD8):
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_EBP
0013F9A80013F9AC.
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_COPYdw_EBPSTACK
0013F9A84DBE4AD8JM
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_NANDdw
0013F9A800000286..
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(A,A)
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A
NOT(4DBE4AD8)=B241B527

AND(4DBE4AD8,4DFD2FC2):
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_EBP
0013F9A80013F9AC.
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_COPYdw_EBPSTACK
0013F9A84DBE4AD8JM
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_NANDdw
0013F9A800000286..
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(A,A),NAND(B,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F9A8B202D03D=
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(A,A),NAND(B,B))**B202D03D=NAND(B,B)**
VM_NANDdw
0013F9A800000206..
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(A,A),NAND(B,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A
VMP的B数据是直接传递它的相反数B202D03D给VM,相当于隐藏了一次NAND(B,B)的过程。AND(4DBE4AD8,4DFD2FC2)=4DBC0AC0

OR(00000293,00000100):
0013F78000000293..
0013F78400000100...
VM_NANDdw;NAND(NAND(A,B),NAND(A,B))
0013F784FFFFFC6Cl
VM_PUSHdw_EBP
VM_COPYdw_EBPSTACK;复制结果,就相当于NAND(NAND(A,B),NAND(A,B))
0013F780FFFFFC6Cl
0013F784FFFFFC6Cl
VM_NANDdw;NAND(NAND(A,B),NAND(A,B))
0013F78400000393..
OR(00000293,00000100)=00000393

XOR(4DBE4AD8,4DFD2FC2):
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_EBP
0013F9A80013F9AC.
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_COPYdw_EBPSTACK
0013F9A84DBE4AD8JM
0013F9AC4DBE4AD8JM
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_NANDdw
0013F9A800000286..
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F9A8B202D03D=
0013F9ACB241B527'A
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))**B202D03D=NAND(B,B)**
VM_NANDdw
0013F9A800000206..
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_EDISTACKdw
0013F9A84DBE4AD8JM
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F9A44DFD2FC2/M
0013F9A84DBE4AD8JM
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_NANDdw
0013F9A400000282..
0013F9A8B2009025%.
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A8B2009025%.
0013F9AC4DBC0AC0.M
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_NANDdw
0013F9A800000202..
0013F9AC0043651AeC.
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC0043651AeC.
0013F9B07C92118A|;RETURNtontdll.7C92118A
上面这条XOR指令,就是VM在确定跳转地址后的解密指令。加密地址是A数据4DBE4AD8,XOR运算后得到ESI跳转地址0043651A。

在VMP中,减法是采用迂回的方式实现的:
A-B:
NOT(A)
A=A+B
NOT(A)
而NOT运算又要使用NAND来完成
A-B:
NAND(A,A)
A=A+B
NAND(A,A)

2.3.EFLAGS标志位检测+跳转
这一节看完后,就可以畅通无阻的浏览VMP的伪指令了。
2.3.1.判断两个数是否相同
举例数据:
把立即数0000和内存00427D51中的1个word数据比较,检测是否为0。
整个过程分为两个阶段:
第一阶段:执行减法运算
A-B:
NAND(A,A);这里的标志位是无用的
A=A+B;获得标志位A
NAND(A,A);获得标志位B
第二阶段:合并两个标志位
A=AND(A,00000815)
B=AND(B,FFFFF7EA)
A=A+B
第三阶段:检测ZF位+跳转
构建跳转地址结构
检测ZF位
获得加密跳转地址
解密跳转地址
调用VM_JMP
在开始这个部分前,把所有VM_MOV_EDISTACK_EBPSTACK伪指令中的ANDAL,3C指令的下一条指令处下好断点,我们要记录下各个标志位的走向!000000286-->14(表示EFL存储到偏移量14的[EDI+EAX]位置)
第一阶段:
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHw_IMMEDIATEb
0013F9AC0000
0013F9B07C92118A|;RETURNtontdll.7C92118A;立即数IMM0000
VM_PUSHdw_IMMEDIATEdw
0013F9A87D51
0013F9AC00000042B...
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHw_MEMORYb
0013F9AC00000000
0013F9B07C92118A|;RETURNtontdll.7C92118A;内存数MEM0000。很明显,我们看到两个数是相同的
VM_PUSHdw_EBP
0013F9A80013F9AC.
0013F9AC00000000....
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_COPYw_EBPSTACK
0013F9A80000
0013F9AC00000000....
0013F9B07C92118A|;RETURNtontdll.7C92118A;复制内存数MEM0000
VM_NANDw
0013F9A800000286..
0013F9AC000000FF...
0013F9B07C92118A|;RETURNtontdll.7C92118A;NOT(MEM0000)=MEM00FF
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC000000FF...
0013F9B07C92118A|;RETURNtontdll.7C92118A;000000286-->14(表示EFL存储到偏移量14的[EDI+EAX]位置)
VM_ADDb_EBPSTACK
0013F9A80286
0013F9AC00FF0000...
0013F9B07C92118A|;RETURNtontdll.7C92118A;00FF=IMM0000+MEM00FF
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC00FF
0013F9B07C92118A|;RETURNtontdll.7C92118A;标志位A000000286-->04
VM_PUSHdw_EBP
0013F9A8F9AE
0013F9AC00FF0013..
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_COPYw_EBPSTACK
0013F9AC00FF00FF..
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_NANDw
0013F9A80246
0013F9AC00000000....
0013F9B07C92118A|;RETURNtontdll.7C92118A;NOT(00FF)
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC0000
0013F9B07C92118A|;RETURNtontdll.7C92118A;标志位B00000246-->3C
VM_MOVw_EDISTACKb_EBPSTACKw
0013F9B07C92118A|;RETURNtontdll.7C92118A;
第一阶段结束。
两个操作数都是0000,很明显这次判断将是两个数相同,减法后的ZF位置1。
运算的结果都是无用的,关键在于它的标志位,继续看标志位ZF的检测+跳转
第二阶段:
VM_PUSHdw_EDISTACKdw
0013F9AC00000286..
0013F9B07C92118A|;RETURNtontdll.7C92118A;标志位A000000286<--04
VM_PUSHdw_EDISTACKdw
0013F9A800000286..
0013F9AC00000286..
0013F9B07C92118A|;RETURNtontdll.7C92118A;再来一次标志位A
VM_NANDdw
0013F9A800000282..
0013F9ACFFFFFD79y
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(A,A)=NOT(A)=FFFFFD79
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9ACFFFFFD79y
0013F9B07C92118A|;RETURNtontdll.7C92118A
VM_PUSHdw_IMMEDIATEw
0013F9A8FFFFF7EA
0013F9ACFFFFFD79y
0013F9B07C92118A|;RETURNtontdll.7C92118A;NAND(X,X)=NOT(00000815)=FFFFF7EA传递相反数,隐藏NOT(00000815)
VM_NANDdw
0013F9

你可能感兴趣的:(数据结构,C++,c,C#,vc++)