首页
社区
课程
招聘
[原创]代码乱序变形
发表于: 2012-9-12 23:14 22275

[原创]代码乱序变形

2012-9-12 23:14
22275

还记得很早以前看VMP一撮JMP搞晕了自己,那个时候太浮躁,现在静下心来看指令,比较容易有头绪,这个就算是个笔记,一步步的记录下自己分析的过程,本来想找个博客写,但懒得注册了,做事情莫着急,慢慢来~~~~~~~~~~~~~~

待虚拟的程序:CrackMe_ JE.exe
  VMP版本:VMProtect_Ultimate2.08
  虚拟后的程序:CrackMe_ JE.vmp.exe
  虚拟选择:仅虚拟 其他保护都不选
分析测试代码: OD插件辅助分析测试
虚拟前的指令代码:
.text:0040159E                 cmp     eax, 0D2360h
.text:004015A3                 pop     edi
.text:004015A4                 jz      short loc_4015BA
.text:004015A6                 push    0               ; uType
.text:004015A8                 push    offset Caption  ; "成功"
.text:004015AD                 push    offset Text     ; "注册成功"
.text:004015B2                 push    0               ; hWnd
.text:004015B4                 call    ds:MessageBoxA
.text:004015BA
.text:004015BA loc_4015BA:                             ; CODE XREF: sub_401560+44j
.text:004015BA                 add     esp, 100h
.text:004015C0                 retn

虚拟后变成类似这样
.vmp0:00406F08                 push    61D36029h
.vmp0:00406F0D                 pushf                   ; Push Flags Register onto the Stack
.vmp0:00406F0E                 mov     dword ptr [esp+4], 6DA8003Fh
.vmp0:00406F16                 jmp     loc_406EEC      ; Jump

这里的乱序是代码物理存储位置的顺序从新拼接
本来连续的几个指令被打散,然后用JMP CALL RETN连接

目前我只看了JMP和CALL
JMP 毫无疑问 就是无条件跳转去拼接代码
CALL 其实也是无条件跳转 只是多了ESP-4这个步骤 等价于PUSH JMP

所以我的思路是这样
分析过程中 如果遇到CALL 指令
首先记录CALL的目标地址,然后转换为对应的PUSH JMP 指令 再到CALL的目标地址继续解析
最终任务为替换这中间的所有CALL 为对应的PUSH JMP 指令 (分析发现PUSH内容是没有意义的,关键是要完成ESP -4)
这个为我想到解决代码乱序问题的一个思路

如果遇到JMP 指令 就直接分析目的地址的指令就可以了

如果遇到JMC指令(条件跳转)就需要用以上思路递归分析目的地址

程序中我自己加了个区段,用来存放乱序规整后的代码,我用OD插件完成这个工作,这样可以测试整理后的代码是不是对的,也可以利用OD的反汇编引擎确定指令长度等信息。

这只是第一步的第一步 后面还有好多要做 慢慢来

补充修正1:
需要采用递归下降反汇编条件跳转指令
如果碰到条件跳转,记录目标地址,继续往下反汇编直到遇到RETN指令
然后再继续反汇编条件跳转的目标地址,如此递归
目前发现VM开始代码部分JCC指令走的分枝虽然有不同,但是代码是等价的,
也就是无论走哪个分枝作用都是相同的,最后的RETN也起到拼接的作用

修正补充2:
retn 指令代表每个HANDLE的结束 不是拼接作用
前面说了代码乱序 现在说说代码变形
根据自己手工识别发现下面的规律

变形规律一.
1.有多个指令对特定寄存器操作
2.在这些指令中该寄存器是目的操作数
3.该寄存器在特定范围内没有为其他指令作为源操作数

如果同时满足 1 2 3条件
则该范围内只保留对特定寄存器操作的最后一个指令
,其他对该寄存器的操作指令会被最后一个指令覆盖掉,
所以其他指令都无效,都NOP掉

(注:一般最后一条指令是R32寄存器,
其他指令中包含该寄存器的32 16 8位版本都无效,都需要NOP)

变形规律二.
栈平衡
一般会有一条类似的指令平衡栈
lea     esp, [esp+XXh]
如果栈平衡,且栈的操作不影响后续指令,
那么平衡的栈指令都可以NOP掉

变形规律三.
操作标志寄存器的指令 EFL 一般都无效
在分析的HANDLE里面 发现跳转和不跳转指令都是等价的
所以标志寄存器的操作指令就没有意义了

下面结合一个具体的HANDLE说明下上述规律
(注:HANDL是解释VMPCODE的函数)

说明下 这个HANDLE是经过乱序整理后的,保存在我新加的节DEVMP上面
(根据IDA的栈指针功能 很快能定位出平衡栈的那段代码)

                  栈指针                                                 
DeVmp.tx:00449F08 038                 sal     dl, cl          ; EDX 操作 NOP
DeVmp.tx:00449F0A 038                 cmc                     ; NOP (符合变形规律三)
DeVmp.tx:00449F0B 038                 mov     eax, [ebp+0]
DeVmp.tx:00449F0E 038                 add     dh, bl          ; EDX 操作 NOP
DeVmp.tx:00449F10 038                 bsf     dx, di          ; EDX 操作 NOP
DeVmp.tx:00449F14 038                 cmc                     ; NOP (符合变形规律三)
DeVmp.tx:00449F15 038                 mov     edx, [ebp+4]    ; 以上的EDX操作全部取消 (符合变形规律一)
DeVmp.tx:00449F18 038                 cmc                     ; NOP (符合变形规律三)
DeVmp.tx:00449F19 038                 clc                     ; NOP (符合变形规律三)
DeVmp.tx:00449F1A 038                 cmp     ch, 65h         ; NOP (符合变形规律三)
DeVmp.tx:00449F1D 038                 not     eax
DeVmp.tx:00449F1F 038                 nop
DeVmp.tx:00449F20 038                 nop
DeVmp.tx:00449F21 038                 nop
DeVmp.tx:00449F22 038                 nop
DeVmp.tx:00449F23 038                 nop
DeVmp.tx:00449F24 038                 push    ecx             ; 栈操作 NOP
DeVmp.tx:00449F25 03C                 cmc                     ; NOP (符合变形规律三)
DeVmp.tx:00449F26 03C                 cmp     dh, 0D3h        ; NOP (符合变形规律三)
DeVmp.tx:00449F29 03C                 not     edx
DeVmp.tx:00449F2B 03C                 push    0               ; 栈操作 NOP
DeVmp.tx:00449F30 040                 and     eax, edx
DeVmp.tx:00449F32 040                 nop
DeVmp.tx:00449F33 040                 nop
DeVmp.tx:00449F34 040                 nop
DeVmp.tx:00449F35 040                 nop
DeVmp.tx:00449F36 040                 nop
DeVmp.tx:00449F37 040                 pusha                   ; 栈操作 NOP
DeVmp.tx:00449F38 060                 mov     word ptr [esp+0Ch], 5A02h ; 栈操作 NOP
DeVmp.tx:00449F3F 060                 mov     [ebp+4], eax
DeVmp.tx:00449F42 060                 mov     [esp+4], ah     ; 栈操作 NOP
DeVmp.tx:00449F46 060                 pushf                   ; 栈操作 NOP
DeVmp.tx:00449F47 064                 push    0               ; 栈操作 NOP
DeVmp.tx:00449F4C 068                 mov     byte ptr [esp], 77h ; 栈操作 NOP
DeVmp.tx:00449F50 068                 pushf                   ; 栈操作 NOP
DeVmp.tx:00449F51 06C                 pop     dword ptr [esp+2Ch] ; 栈操作 NOP
DeVmp.tx:00449F55 068                 push    0
DeVmp.tx:00449F5A 06C                 push    dword ptr [esp+30h] ; 以下两条指令等价于
DeVmp.tx:00449F5A                                             ; pushf
DeVmp.tx:00449F5A                                             ; pop [ebp]
DeVmp.tx:00449F5E 070                 pop     dword ptr [ebp+0]
DeVmp.tx:00449F61 06C                 pushf                   ; 栈操作 NOP
DeVmp.tx:00449F62 070                 push    esp             ; 栈操作 NOP
DeVmp.tx:00449F63 074                 lea     esp, [esp+3Ch]  ; 以上指令栈平衡 对栈的内存赋值都无效  (符合变形规律二)
                                                                                                                                                                                        ;(刚开始栈大小为 038 执行完这个指令后 栈也是 038 所以栈平衡了)
DeVmp.tx:00449F67 038                 nop
DeVmp.tx:00449F68 038                 nop
DeVmp.tx:00449F69 038                 nop
DeVmp.tx:00449F6A 038                 nop
DeVmp.tx:00449F6B 038                 nop
DeVmp.tx:00449F6C 038                 setns   al              ; NOP 00449F77覆盖AL
DeVmp.tx:00449F6F 038                 nop
DeVmp.tx:00449F70 038                 nop
DeVmp.tx:00449F71 038                 nop
DeVmp.tx:00449F72 038                 nop
DeVmp.tx:00449F73 038                 nop
DeVmp.tx:00449F74 038                 xadd    al, dl          ; NOP 00449F77覆盖AL
DeVmp.tx:00449F77 038                 mov     al, [esi-1]
DeVmp.tx:00449F7A 038                 cmc                     ; NOP
DeVmp.tx:00449F7B 038                 sub     dx, 4E62h       ; EDX操作无效 NOP
DeVmp.tx:00449F80 038                 shrd    edx, eax, 0Eh   ; EDX操作无效 NOP
DeVmp.tx:00449F84 038                 cmp     di, bp          ; NOP
DeVmp.tx:00449F87 038                 sub     al, bl
DeVmp.tx:00449F89 038                 clc                     ; NOP
DeVmp.tx:00449F8A 038                 btr     dx, dx          ; NOP
DeVmp.tx:00449F8E 038                 add     al, 9Dh
DeVmp.tx:00449F90 038                 mov     dx, 0F191h      ; EDX操作无效 NOP
DeVmp.tx:00449F94 038                 not     al
DeVmp.tx:00449F96 038                 cmc                     ; NOP
DeVmp.tx:00449F97 038                 rol     al, 3
DeVmp.tx:00449F9A 038                 sar     dl, cl          ; EDX操作无效 NOP
DeVmp.tx:00449F9C 038                 sub     bl, al
DeVmp.tx:00449F9E 038                 setl    dh              ; EDX操作无效 NOP
DeVmp.tx:00449FA1 038                 mov     dl, bl          ; EDX操作无效 NOP
DeVmp.tx:00449FA3 038                 dec     dx              ; EDX操作无效 NOP
DeVmp.tx:00449FA6 038                 dec     esi
DeVmp.tx:00449FA7 038                 btc     dx, ax          ; EDX操作无效 NOP
DeVmp.tx:00449FAB 038                 sub     dl, dh          ; EDX操作无效 NOP
DeVmp.tx:00449FAD 038                 sub     dl, 6Eh         ; EDX操作无效 NOP
DeVmp.tx:00449FB0 038                 ror     dl, 1           ; EDX操作无效 NOP
DeVmp.tx:00449FB2 038                 movzx   eax, al
DeVmp.tx:00449FB5 038                 lea     edx, ds:158B988Ch ; EDX操作无效 NOP
DeVmp.tx:00449FBC 038                 bts     dx, si          ; EDX操作无效 NOP
DeVmp.tx:00449FC0 038                 cmp     si, 0BD0Bh      ; NOP
DeVmp.tx:00449FC5 038                 push    0               ; NOP
DeVmp.tx:00449FCA 03C                 mov     edx, ds:dword_4053EF[eax*4] ; 以上对EDX的操作无效 (符合变形规律一)
DeVmp.tx:00449FD1 03C                 add     esp, 4          ; NOP
DeVmp.tx:00449FD4 038                 jb      loc_4483A4 ;目标地址和楼下地址 代码功能相同
DeVmp.tx:00449FDA 038                 pusha
DeVmp.tx:00449FDB 058                 push    0F83ECAE3h
DeVmp.tx:00449FE0 05C                 clc
DeVmp.tx:00449FE1 05C                 neg     edx
DeVmp.tx:00449FE3 05C                 pusha
DeVmp.tx:00449FE4 07C                 add     edx, 0
DeVmp.tx:00449FEA 07C                 push    0
DeVmp.tx:00449FEF 080                 mov     [esp+80h+var_80], ecx
DeVmp.tx:00449FF2 080                 mov     [esp+80h+var_3C], edx
DeVmp.tx:00449FF6 080                 push    esi
DeVmp.tx:00449FF7 084                 push    [esp+84h+var_84]
DeVmp.tx:00449FFA 088                 push    [esp+88h+var_3C]
DeVmp.tx:00449FFE 08C                 retn    50h                ;retn 50 相当于 jmp 和 ESP + 54
                                                                ;所以到00449FD4 之间的栈也是平衡的  (符合变形规律二)
                                                                ;这段代码等价于
                                                                ;neg     edx
                                                                ;jmp edx

这里注意 EBP 是非常重要的 所以要根据上文推到处[esp + 30] 存放着 EFL
06C                 push    dword ptr [esp+30h] ; 以下两条指令等价于
                                                    ; pushf
                                                ;pop [ebp]
070                 pop     dword ptr [ebp+0]

这段代码根据以上规律整理后变成如下这样
该段指令的等价指令 (可以在原HANDLE上做跳转到这里来验证优化代码是否有问题,验证通过)
mov     eax, [ebp+0]
mov     edx, [ebp+4]
not     eax                                                        ;这里就是传说中的或非门
not     edx                                                        ; 直接看指令是先非再与 但是 NOT(A) AND NOT(B)=  NOT(A AND B)
and     eax, edx                                                ; 也就是先非再与 等价于 先或再非 也就是或非门
mov     [ebp+4], eax                                       
pushf
pop [ebp]

mov     al, [esi-1]                                                  ;以下是通用指令 计算并跳转到下一个HANDLE (每个HANDLE 都有同样的指令)
sub     al, bl
add     al, 9Dh
not     al
rol     al, 3
sub     bl, al
dec     esi
movzx   eax, al
mov     edx, ds:dword_4053EF[eax*4]
neg     edx
jmp     edx

X86OPCODE
8B 45 00 8B 55 04 F7 D0 F7 D2 21 D0 89 45 04 9C
8F 45 00 8A 46 FF 2A C3 04 9D F6 D0 C0 C0 03 2A
D8 4E 0F B6 C0 8B 14 85 EF 53 40 00 F7 DA FF E2

这里补充下或非门知识
或非门可以表达一切逻辑运算
维基百科
http://zh.wikipedia.org/zh/%E6%88%96%E9%9D%9E%E9%97%A8
逻辑运算公式
http://course.cug.edu.cn/21cn/%CA%FD%D7%D6%B5%E7%D7%D3%BC%BC%CA%F5%BB%F9%B4%A1/800x600/web/text_web/03/03030000.htm

按照上面的思路 代码变形就可以人眼识别了
修正补充3:
我记得最初喜欢上破解,还是喜欢爆破的方式,让输入正确的注册码和错误的注册码,都走正确的流程
这个在注册码不参与功能函数解密的情况下是非常通用的方法,简单而暴力,就像年轻的激情,这种甜蜜的
岁月直到遇到vmp。年轻有精力但是浮躁,现在好了些,(是不是代表自己老了 ~~)所以静下来研究,当时的困惑迎刃而解。
大家都知道条件跳转是爆破的关键,每个条件跳转都有两个分支,走A分支或者走B分支,这里假定A分支是注册
成功的流程,这里就出现个问题,如何知道是A分支是呢,一般的方法是取反条件跳转,看程序的现象,弹出注册成功之类的
,其实还有个方法,你有正确的注册码,输入上后发现走到了A分支,那么A分支必然是成功的分支了,如果取反这个条件跳转
那么即使输入错误的,也会走A分支,这就完成了爆破!
具体到当前文章讨论的VMP,VMP里面有个HANDLE 代表跳转,具体跳到哪里取决于前面的运算
HANDLE指令如下
DeVmp.tx:004B9F08     ; 函数等价于 (验证通过)
DeVmp.tx:004B9F08     ; mov     esi, [ebp+0]
DeVmp.tx:004B9F08     ; add     ebp, 4
DeVmp.tx:004B9F08     ; mov     ebx, esi
DeVmp.tx:004B9F08     ; add     esi, [ebp+0]

ESI 是VMPOPCODE的指针 作用相当于X86指令的EIP
这里ESI = [ebp+0] + [ebp+4]
EBP和EBP+4里面保存的内容来自前面HANDLE的运算
聪明的你想到怎么回事了没
假定你有正确的注册码,你就可以确定正确流程的ESI
然后你就可以patch 这个HANDLE ,即使输入错误的注册码,ESI 也是对的
然后就爆破成功了

具体到这个例子就是
004B9F08 执行到这里 且 ESI == 004B9F08 的时候
执行完这个HANDLE的时候  ESI == 00406FDE 注册成功 ESI == 00406F8A 注册失败

因为两个分支之间的OPCODE 大小未知(除非代码已经还原),也就是已知错误的分支无法得到正确的分支
所以没有正确的注册码就没有正确的分支地址,也就是此爆破的前提是有一个有效的注册码
~~


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 6
支持
分享
最新回复 (22)
雪    币: 114
活跃值: (180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
看起来很强大,前排支持~~
2012-9-12 23:34
0
雪    币: 602
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
先占前排漫漫看
2012-9-12 23:37
0
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark一下,希望lz写得让新手看着容易明白
2012-9-13 01:01
0
雪    币: 316
活跃值: (128)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
5
顶楼上,BTW:插件不是DLL,怎么测试.
2012-9-13 08:42
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
一听到代码乱序变形,就感觉很强大的样子,过来顶一下~
2012-9-13 09:10
0
雪    币: 217
活跃值: (68)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
7
插件源码是个VC6的工程 可以编译出个DLL 放到OD的plugin 目录 就会在OD的插件菜单出现
2012-9-13 10:09
0
雪    币: 1844
活跃值: (35)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
8
下班后测试一下,不管好用否,老毛子都很淡定。。。。
2012-9-13 18:37
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
太厉害...过来顶一下
2012-9-15 05:56
0
雪    币: 37
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
好东西呀,学习学习
2012-9-15 09:18
0
雪    币: 73
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
围观学习~~
2012-9-15 14:17
0
雪    币: 88
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
虽然不懂楼主在说什么,但是看起来好像很牛逼的样子
2012-9-18 00:29
0
雪    币: 347
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
有码~支持一下!
2012-9-18 09:04
0
雪    币: 49
活跃值: (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
把代码 连接起来 好看多了
2012-9-20 21:27
0
雪    币: 29
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
新手有点看不懂
2012-9-21 07:49
0
雪    币: 3750
活跃值: (1807)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
不错,支持一下
2012-9-21 10:38
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
17
话说,这个搞搞得出什么,如果是“仅虚拟”的话。
2012-9-21 11:32
0
雪    币: 230
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
这是编译好的!!
上传的附件:
2012-9-22 11:35
0
雪    币: 264
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
TTP的乱序变形 能还原出来么 困扰中
2012-9-22 20:34
0
雪    币: 217
活跃值: (68)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
20
补充变形方面的研究 欢迎大家拍砖~~~ 中秋快乐
2012-9-30 11:23
0
雪    币: 64
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
MARK~~~
2012-9-30 14:14
0
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
22
简单的乱序代码拼接我也试着做过,不过没搞下去,哈哈~
2012-10-2 10:57
0
雪    币: 217
活跃值: (68)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
23
增加VMP的简单爆破
2012-10-12 21:49
0
游客
登录 | 注册 方可回帖
返回
//