• VMP分析笔记(cmp命令在VM中的表达)


    之前在6月份的时候,突然心血来潮去研究VM,当然那时候全网搜索找相关的文章,但是很多是对我来说是比较深奥的,最起码就是只能看懂一些。然后就写了一篇关于VM的爆破文章,如果你感兴趣的话,可以在本博客找到。
     
    然后经过几天思考,觉得VM还不是我玩的时候,又转向了算法的分析,大家可以去看看文章的发表日期就知道我最近做什么了,O(∩_∩)O哈哈~ 其实就是自娱自乐吧。。找虐 T~T
     

    VMP的破解

    http://www.52pojie.cn/thread-205189-1-1.html

    (出处: 吾爱破解论坛)

    看了这个帖子,自己重新理解了帖子的话,就是下面这句。

    vma-b就是

    temp= add(a, nor(b,b))

    nor (temp, temp)

    所以调用VM_Handle的顺序应该是

    Nor(X1,X2)à add(Y1,Y2)à nor(Z1,Z2)àseteip

    其中X1==X2 , Z1==Z2

    这个笔记可以看作是cmp指令在VM中的表达过程

    自己写了一个demo

    易语言代码如下

    .版本 2

    .子程序 _按钮1_被单击

    .局部变量 a, 整数型

    置入代码 ({ 235, 16, 86, 77, 80, 114, 111, 116, 101, 99, 116, 32, 98, 101, 103, 105, 110, 0 })

    a = 到整数 (编辑框1.内容)

    .如果真 (a 123)

    信息框 (success, 0, , )

    返回 ()

    .如果真结束

    信息框 (fail, 0, , )

    置入代码 ({ 235, 14, 86, 77, 80, 114, 111, 116, 101, 99, 116, 32, 101, 110, 100, 0 })

    然后分别找到Nor32 handler add32 handler 这个可以用FKvmp插件找到

    得出 00499BDE 0049B565 2个地址

    现在我要的数据是

    0049B565 0145 04 add dword ptr ss:[ebp+0x4],eax 这句的eax [ebp+4]

    还有就是 nor32 中的

    Not eax

    Not edx

    And eax,edx

    详细看脚本代码

    脚本代码

    loop:

    run

    cmp esi,00499BDE

    jnz nor32

    nor32:

    var a

    log "nor32"

    log eax

    log edx

    mov a,eax

    not a

    log a

    cmp esi,0049B565

    jnz test_start

    test_start:

    var tmp

    mov tmp,[ebp+4]

    log "add32"

    log eax

    log tmp

    jmp loop

    好了,运行脚本然后下00499BDE 0049B565 2个地址的断点,点击按钮 让程序自己跑起来,这时候脚本开始记录下我想要的数据。

    点击OD中的L 就可以看到所有输出的数据。

    我是输入123 得出以下数据

    00499BDE [PhantOm_iNFO] > Breakpoint [sti]

    00499BDE Breakpoint at VMadd_vm.00499BDE

    nor32

    eax: 0000007B

    edx: 0000007B

    a: FFFFFF84

    add32

    eax: 0000007B

    tmp: 0000007B

    0049B565 [PhantOm_iNFO] > Breakpoint [sti]

    0049B565 Breakpoint at VMadd_vm.0049B565

    nor32

    eax: FFFFFF84

    edx: 00000286

    a: 0000007B

    add32

    eax: FFFFFF84

    tmp: 0000007B

    00499BDE [PhantOm_iNFO] > Breakpoint [sti]

    00499BDE Breakpoint at VMadd_vm.00499BDE

    nor32

    eax: FFFFFFFF

    edx: FFFFFFFF

    a: 00000000

    add32

    eax: FFFFFFFF

    tmp: FFFFFFFF

    可以看到7BH=123D

    这时a=not 7B = FFFFFF84

    Add FFFFFF847B = FFFFFFFF

    Not FFFFFFFF=0

    然后我又自己思考了一下,为什么最后是等于0呢,因为上面已经说了,cmp命令相当于a-b 比如我这个程序对比输入的数据是否等于123 16进制7B 。只要将输入的数和7B相减等于0的话,说明输入对了。

    然而在VM中,先not 后再and not 最后结果等于0 最后还是等于0

    以上就是正确的流程,最后是等于0的 所以为什么吾爱的那个帖子,走到and eax,edx 0 就是这个原因

    错误的流程:

    00499BDE [PhantOm_iNFO] > Breakpoint [sti]

    00499BDE Breakpoint at VMadd_vm.00499BDE

    nor32

    eax: 000001C8

    edx: 000001C8

    not eax

    temp: FFFFFE37

    not edx

    temp1: FFFFFE37

    add32

    eax: 000001C8

    tmp: 000001C8

    0049B565 [PhantOm_iNFO] > Breakpoint [sti]

    0049B565 Breakpoint at VMadd_vm.0049B565

    nor32

    eax: FFFFFE37

    edx: 00000282

    not eax

    temp: 000001C8

    not edx

    temp1: FFFFFD7D

    add32

    eax: FFFFFE37

    tmp: 0000007B

    00499BDE [PhantOm_iNFO] > Breakpoint [sti]

    00499BDE Breakpoint at VMadd_vm.00499BDE

    nor32

    eax: FFFFFEB2

    edx: FFFFFEB2

    not eax

    temp: 0000014D

    not edx

    temp1: 0000014D

    add32

    eax: FFFFFEB2

    tmp: FFFFFEB2

    上面就是下面这句话的解释

    temp= add(a, nor(b,b))

    nor (temp, temp)

    总结:

    如果遇到2个数进行对比的话,可以找到add32 add dword ptr ss:[ebp+0x4],eax 关注eax[ebp+0x4]的值 可以找到是和哪个数据对比

    Demo and pdf download:

    https://yunpan.cn/c6i72JaATqvBR 访问密码 b0b7

    附录:

    在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伪指令中的AND AL,3C指令的下一条指令处下好断点,我们要记录下各个标志位的走向!000000286-->14(表示EFL存储到偏移量14的[EDI+EAX]位置)
    第一阶段:
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_PUSHw_IMMEDIATEb
    0013F9AC 0000
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;立即数IMM0000
    VM_PUSHdw_IMMEDIATEdw
    0013F9A8 7D51
    0013F9AC 00000042 B...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_PUSHw_MEMORYb
    0013F9AC 00000000
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;内存数MEM0000。很明显,我们看到两个数是相同的
    VM_PUSHdw_EBP
    0013F9A8 0013F9AC .
    0013F9AC 00000000 ....
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_COPYw_EBPSTACK
    0013F9A8 0000
    0013F9AC 00000000 ....
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;复制内存数MEM0000
    VM_NANDw
    0013F9A8 00000286 ..
    0013F9AC 000000FF ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NOT(MEM0000)=MEM00FF
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC 000000FF ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;000000286-->14(表示EFL存储到偏移量14的[EDI+EAX]位置)
    VM_ADDb_EBPSTACK
    0013F9A8 0286
    0013F9AC 00FF0000 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;00FF=IMM0000+MEM00FF
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC 00FF
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;标志位A 000000286-->04
    VM_PUSHdw_EBP
    0013F9A8 F9AE
    0013F9AC 00FF0013 ..
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_COPYw_EBPSTACK
    0013F9AC 00FF00FF ..
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_NANDw
    0013F9A8 0246
    0013F9AC 00000000 ....
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NOT(00FF)
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC 0000
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;标志位B 00000246-->3C
    VM_MOVw_EDISTACKb_EBPSTACKw
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;
    第一阶段结束。
    两个操作数都是0000,很明显这次判断将是两个数相同,减法后的ZF位置1。
    运算的结果都是无用的,关键在于它的标志位,继续看标志位ZF的检测+跳转
    第二阶段:
    VM_PUSHdw_EDISTACKdw
    0013F9AC 00000286 ..
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;标志位A 000000286<--04
    VM_PUSHdw_EDISTACKdw
    0013F9A8 00000286 ..
    0013F9AC 00000286 ..
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;再来一次标志位A
    VM_NANDdw
    0013F9A8 00000282 ..
    0013F9AC FFFFFD79 y
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(A,A)=NOT(A)=FFFFFD79
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC FFFFFD79 y
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_PUSHdw_IMMEDIATEw
    0013F9A8 FFFFF7EA
    0013F9AC FFFFFD79 y
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(X,X)=NOT(00000815)=FFFFF7EA 传递相反数,隐藏NOT(00000815)
    VM_NANDdw
    0013F9A8 00000202 ..
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(A,A),NAND(X,X))=标志位A 00000286 AND 00000815
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_PUSHdw_EDISTACKdw
    0013F9A8 00000246 F..
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;标志位B 00000246<--3C
    VM_PUSHdw_EDISTACKdw
    0013F9A4 00000246 F..
    0013F9A8 00000246 F..
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;再来一次标志位B
    VM_NANDdw
    0013F9A4 00000282 ..
    0013F9A8 FFFFFDB9
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(B,B)=NOT(B)=FFFFFDB9
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9A8 FFFFFDB9
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_PUSHdw_IMMEDIATEw
    0013F9A4 00000815 ..
    0013F9A8 FFFFFDB9
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(Y,Y)=NOT(FFFFF7EA)=00000815 传递相反数,隐藏NOT(FFFFF7EA)
    VM_NANDdw
    0013F9A4 00000206 ..
    0013F9A8 00000242 B..
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(B,B),NAND(Y,Y))=标志位B 00000246 AND FFFFF7EA
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9A8 00000242 B..
    0013F9AC 00000004 ...
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_ADDdw_EBPSTACK
    0013F9A8 00000202 ..
    0013F9AC 00000246 F..
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;两个AND后的标志位相加
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC 00000246 F..
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;00000246-->00 暂存结果
    第二阶段结束
    现在VMP已经把两个标志位合并成了一个,关于标志位合并的解析结束后再来看。继续看第三阶段:检测ZF+跳转,注意看好堆栈数据的构造,堆栈虚拟机的跳转判断有他独特的地方!同时它巧妙的运用了ZF位在EFLAGS中的位置。
    第三阶段:
    VM_PUSHdw_IMMEDIATEdw
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳转地址1
    VM_PUSHdw_IMMEDIATEdw
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳转地址2
    VM_PUSHdw_EBP
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳转地址指针
    VM_PUSHw_IMMEDIATEb
    0013F9A0 0004
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM

    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;传递4,看好堆栈的构造,下面的几个操作是独立的
    VM_PUSHdw_EDISTACKdw
    0013F99C 0246
    0013F9A0 00040000 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;第二阶段结果00000246<--00
    VM_PUSHdw_EBP
    0013F998 F99E
    0013F99C 02460013 .F
    0013F9A0 00040000 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_COPYdw_EBPSTACK
    0013F998 0246
    0013F99C 02460000 ..F
    0013F9A0 00040000 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;复制标志位
    VM_NANDdw
    0013F998 0282
    0013F99C FDB90000 ..
    0013F9A0 0004FFFF .
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(A,A)=NOT(A)=NOT(00000246)=FFFFFDB9
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F99C FDB9
    0013F9A0 0004FFFF .
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_PUSHdw_IMMEDIATEb
    0013F998 FFBF
    0013F99C FDB9FFFF
    0013F9A0 0004FFFF .
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(B,B)=NOT(00000040)=FFFFFFBF 传递相反数,隐藏NOT(000000040)
    VM_NANDdw
    0013F998 0202
    0013F99C 00400000 ..@. ; OFFSET NOTEPAD.B
    0013F9A0 00040000 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(B,B),NAND(B,B))=标志位 00000246 AND 00000040
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F99C 0040
    0013F9A0 00040000 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;AND结果是00000040,说明ZF位是1,两个数相等;想想如果不相等,结果是00000000
    VM_SHRdw_EBPSTACKb
    0013F99C 00000202 ..
    0013F9A0 00000004 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;右移4位刚好把00000040移动成00000004;如果不相等,右移后是00000000
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9A0 00000004 ...
    0013F9A4 0013F9A8 .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
    VM_ADDdw_EBPSTACK
    0013F9A0 00000206 ..
    0013F9A4 0013F9AC .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;00000004+0013F9A8=0013F9AC;如果不相等,00000000+0013F9A8=0013F9A8
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9A4 0013F9AC .
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳转地址指针指向的就是判断后的跳转地址
    VM_COPYdw_EBPSTACK
    0013F9A4 4DBE4AD8 JM
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳转地址指针指向的跳转地址复制出来
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9A8 4DBE49D5 IM
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;把最终的跳转地址暂存到EDISTACK,4DBE4AD8-->18
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;扫尾工作,释放EBPSTACK
    VM_MOVdw_EDISTACKdw_EBPSTACKdw
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;扫尾工作,释放EBPSTACK
    VM_PUSHdw_EDISTACKdw
    0013F9AC 4DBE4AD8 JM
    0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;压入判断的跳转地址4DBE4AD8<--18
    第三阶段结束
    接下来VM将使用一次XOR运算解密4DBE4AD8数据(详见2.2.3.XOR举例),然后是VM_JMP指令调用的组合(详见2.1.3.举例),全过程结束。
    两个操作数都是0000,1个来自内存空间,一个来自ESI的编译数据,同时这段代码是在VM刚刚启动就进行的了,都是定量。但是VM还要进行检测,说明两个数据是不确定的,VM在运行过程中要知道它是不是0,可以把它猜测为VMP内部的一个信号。VM一开始就要知道到底应该走向哪个分支。到后面我们会进行测试,如果这个信号比较结果不为0,VM的走向是怎样的。
    下面我们来详解上面的操作过程,从第二阶段合并标志位来看
    第一阶段:执行减法运算
    IMM0000-MEM0000:
    NAND(IMM0000,IMM0000) ;这里的标志位是无用的
    00FF=IMM00FF+MEM0000 ;获得标志位A 000000286
    NAND(00FF,00FF) ;获得标志位B 000000246
    第二阶段:合并两个标志位
    00000004=AND(00000286,00000815)
    00000242=AND(00000246,FFFFF7EA)
    00000246=00000004+00000242
    把两个标志位分别AND后相加,AND操作时用于保留想要的标志位,加法把它合并起来。

     

     

    By 星空之上

    2016-8-14 15:02:55

  • 相关阅读:
    VS 2010 制作 Windows Service 安装包
    Postback 之后保持浏览器滚动条的位置
    Stream之list转map及问题解决
    List.sort()排序功能
    The content of element type "resultMap"
    MyBatis 一对一,一对多,多对多
    Java Array、List、Set互相转化
    Java流(Stream)操作实例-筛选、映射、查找匹配
    JAVA系列笔记十八之nohup实现后台运行程序
    VSCode汇总
  • 原文地址:https://www.cnblogs.com/Sendige/p/9600968.html
Copyright © 2020-2023  润新知