首页
社区
课程
招聘
[原创]Themida 2260 虚拟机 FISH 初探 (二)
2016-3-3 14:35 13829

[原创]Themida 2260 虚拟机 FISH 初探 (二)

2016-3-3 14:35
13829
标 题: 【原创】Themida 2260 虚拟机 FISH 初探(一)
作 者: xiaohang
时 间: 2016-03-03,00:39:37
链 接: http://bbs.pediy.com/showthread.php?t=208207


由于时间久远再加上硬盘内存的东西太多,一时我找不到一个type2类型的经过处理完整可读的handler例程,只找到了一个type1类型的handler例程,并且,当初被用来调试的opcode也不知道保存在哪里,这里我们就还是假设opcode为X的方式来分析这个type1 handler吧,反正如果代入opcode 还牵扯到很多运算,也不便于阅读。虽然type1 类型不是handler中最繁琐的,但是它也就比type2类型少了两个opcode参数以及相应的处理例程,原理上也是相通的。

前篇的传说门:
【原创】Themida 2260 VM FISH 初探(一)

以下接前文:
VM FISH是如何处理OPcode的
虚拟机被创造出来的目的就是以Opcode来替代原来PC中的指令,并加入混淆欺骗等等,以增加逆向的难度,这里我们就以VM处理Opcode为引,探索一下Themida Fish VM 是如何处理Opcode的,从而对Fish VM有个概念上的了解,并且其中还将详细的解说subhandle和加密寄存器的作用以及他们的运作机制。
这里,我就举handler中比较复杂的type1类型handler处理一段Opcode为例子,进行详细说明:

废话不多少先上一段,整理过的handler原码:

// FISH Virtual Handler 0000 00422A0F
00422A17 MOV DWORD PTR [EBP+0x53],0x0
00422A25 MOV DWORD PTR [EBP+0xa7],0x0
00422A33 MOV DWORD PTR [EBP+0x89],0x0
00422A41 MOV DWORD PTR [EBP+0x14],0x0
00422A4F MOV DWORD PTR [EBP+0x8f],0x0
00422A5D MOV WORD PTR [EBP+0xa0],0x0
00422A6A MOV BYTE PTR [EBP+0x57],0x0
00422A75 MOV DWORD PTR [EBP+0x68],0x0
00422A88 MOV ESI,DWORD PTR [EBP+0x8]
00422A8A ADD ESI,0x0
00422A90 MOV DI,WORD PTR [ESI]
00422A93 SHL EDI,0x2
00422A9E MOV ESI,DWORD PTR [EBP+0x4f]
00422AA0 ADD ESI,EDI
00422AA2 MOV EDX,DWORD PTR [ESI]
00422AAB ADD DWORD PTR [EBP+0x8],0x2
00422AB1 JMP EDX
这段是前文中提到的一段,典型的初始化handler,我贴在这里是让大家能快速阅读记忆一下红色的部分,这部分是对FISH VM的加密寄存器清0,请大概记忆一下这些寄存器的偏移量,就是ebp+??这些数字,后面会经常看到。后面看到他们,你就知道VM又在加密或解密了。

下面这段是我整理的type1 handler 的原码,正题开始了:


0041A677 MOV EBX,DWORD PTR [EBP+0x8] //读取opcode 指针
0041A679 ADD EBX,0x9 //opcode指针加上偏移
0041A67F MOVZX ECX,BYTE PTR [EBX] //读取BYTE 位的opcode ,这里我们暂且称之为参数A
0041A682 OR DWORD PTR [EBP+0x8f],0x5df470f2
0041A690 ADD ECX,DWORD PTR [EBP+0x53]
0041A69A ADD ECX,DWORD PTR [EBP+0xa7]
0041A6A4 ADD ECX,DWORD PTR [EBP+0x68]
0041A6AE OR DWORD PTR [EBP+0x53],ECX //利用读取的Opcode对加密寄存器再加密
0041A6B8 OR DWORD PTR [EBP+0xa7],0x2a3b9783
0041A6C6 SUB ECX,DWORD PTR [EBP+0x89] //解密读取的Opcode 参数A
0041A6D0 OR DWORD PTR [EBP+0x89],0x5570350f
0041A6DE SUB BYTE PTR [EBP+0x57],CL//这里对参数A进行加密保存
//-------分割线---(以下代码都是连续的,我人为的分开是方便大家阅读)-----------------------------
0041A6EE MOV ESI,DWORD PTR [EBP+0x8] //读取opcode 指针
0041A6F9 ADD ESI,0xa //opcode指针加上偏移
注:Opcode指令序列可以由不同的几个部分组成的,包括subhandle 立即数虚拟寄存器号参数大小的控制参数等,长度是可变的,但具体到哪个偏移量存放那种参数却不一定,完全由handler决定
0041A6FF MOVZX EAX,BYTE PTR [ESI] //读取BYTE 位的opcode ,这里我们暂且称之为参数B
0041A71E SUB EAX,DWORD PTR [EBP+0x53]
0041A751 XOR EAX,DWORD PTR [EBP+0xa7]
0041A785 OR DWORD PTR [EBP+0x53],EAX //利用读取的Opcode对加密寄存器再加密
0041A7A7 ADD DWORD PTR [EBP+0xa7],0x1b9bae7c
0041A7CA SUB EAX,DWORD PTR [EBP+0x89]
0041A7EC XOR DWORD PTR [EBP+0x89],0x257e4392
0041A7F2 MOV DL,AL
0041A7F8 AND AL,0xf0//下面两个指令,分别获取参数B的高地位
0041A7FC AND DL,0xf
0041A7FF ADD DL,0x55//对指令B高地位分别加密
0041A81B SHR AL,0x4
0041A824 XOR AL,0x71
0041A851 MOV BYTE PTR [EBP+0x4c],AL//这两个指令保存指令到VMcontext
0041A873 MOV BYTE PTR [EBP+0x70],DL//注意,没有两个相同的handler这里是一样的,也就是说,具体临时变量保存在VMcontext哪里,要依据handler来定
//-------分割线---(以下代码都是连续的,我人为的分开是方便大家阅读)-----------------------------
0041A893 MOV EDX,DWORD PTR [EBP+0x8] //读取opcode 指针
0041A895 ADD EDX,0x2 //opcode指针加上偏移
0041A8AC MOV EBX,DWORD PTR [EDX]//读取参数C
0041A8D6 XOR EBX,DWORD PTR [EBP+0x53]
0041A8F0 XOR EBX,DWORD PTR [EBP+0x14]
0041A929 OR DWORD PTR [EBP+0x53],EBX
0041A93B AND DWORD PTR [EBP+0xa7],0x7f25fef3
0041A972 ADD EBX,DWORD PTR [EBP+0x89]
0041A99A OR DWORD PTR [EBP+0x89],0x231e01c4
0041A9B7 XOR EBX,0x8f063b2
0041A9F7 ADD EBX,DWORD PTR [EBP+0x60]
0041AA13 MOV DWORD PTR [EBP+0x26],EBX
//-------分割线---(以下代码都是连续的,我人为的分开是方便大家阅读)-----------------------------
0041AA45 MOV AL,BYTE PTR [EBP+0x4c] 读取参数B的高位
0041AA47 XOR AL,0x71 //简单的解密,对应前面0041A824的代码可见
0041AA49 CMP AL,0x2 //从这里开始,可以看出参数B是一个flag,他的高位控制后面的操作数是什么类型的,2为虚拟地址,1为虚拟寄存器,3为立即数(由于type1 handler中不需要立即数,所以你们不会在后面看到相应的例程)
0041AA4B JNZ 0x1d0(0041AC21)
0041AA72 MOV ECX,DWORD PTR [EBP+0x26]//读取参数C,如果参数B高位是2,参数C就是虚拟地址的偏移
0041AAA6 SUB ECX,DWORD PTR [EBP+0x60]
0041AAB2 XOR ECX,0x8f063b2
0041AABC AND ECX,0xffff
0041AAC9 ADD ECX,EBP
0041AAF5 MOV DL,BYTE PTR [EBP+0x70]//取出参数B的低位
0041AB0C SUB DL,0x55//简单的解密
0041AB1F MOV ECX,DWORD PTR [ECX]
0041AB21 MOV EAX,ECX
0041AB23 ADD EAX,0xbdc57aa
0041AB4B MOV DWORD PTR [EBP+0x4],EAX
0041AB4D CMP DL,0x1 //显然这是一个读取虚拟寄存器的例程,参数B的低位用来控制读取的大小
0041AB50 JNZ 0x12(0041AB68)
0041AB5A MOV CL,BYTE PTR [ECX]
0041AB68 CMP DL,0x2
0041AB6B JNZ 0x9(0041AB7A)
0041AB77 MOV CX,WORD PTR [ECX]
0041AB7A CMP DL,0x3
0041AB7D JNZ 0x1b(0041AB9E)
0041AB89 MOV ECX,DWORD PTR [ECX]
0041ABB7 XOR ECX,0x8f063b2
0041ABF2 ADD ECX,DWORD PTR [EBP+0x60]
0041AC19 MOV DWORD PTR [EBP+0x26],ECX//把从虚拟寄存器中读取到的数值保存
0041AC30 AND DWORD PTR [EBP+0x68],0x1a49977a
0041AC38 MOV BYTE PTR [EBP+0x47],0x0
0041AC43 MOV DL,BYTE PTR [EBP+0x4c]
0041AC45 XOR DL,0x71
0041AC48 CMP DL,0x1
0041AC4B JNZ 0x50(0041ACA1)//如果参数B的高位是1的话,就是对虚拟寄存器的读取
0041AC59 MOV ECX,DWORD PTR [EBP+0x26]//读取参数C
0041AC63 SUB ECX,DWORD PTR [EBP+0x60]
0041AC65 XOR ECX,0x8f063b2
0041AC6B AND ECX,0xffff
0041AC71 ADD ECX,EBP
0041AC73 MOV EDX,ECX
0041AC75 ADD ECX,0xbdc57aa
0041AC83 MOV DWORD PTR [EBP+0x4],ECX//读取的地址后面还要用到,所以先加密保存
0041AC85 MOV ECX,DWORD PTR [EDX]//读取地址中的数值
0041AC87 XOR ECX,0x8f063b2
0041AC95 ADD ECX,DWORD PTR [EBP+0x60]
0041AC9F MOV DWORD PTR [EBP+0x26],ECX//保存地址中的数值
0041ACA9 SUB DWORD PTR [EBP+0x8f],0x18830b95
0041ACB7 OR DWORD PTR [EBP+0x8f],0x2223c95f
//-------分割线---(以下代码都是连续的,我人为的分开是方便大家阅读)-----------------------------
0041ACC5 MOV BL,BYTE PTR [EBP+0x57]//读取参数A
0041ACC7 SUB BL,0xb2
0041ACCA CMP BL,0x72//好了,我们可以确定参数A就是subhandle了,如果subhandle是0x72,那么就是DEC的操作
0041ACCD JNZ 0x2a(0041ACFD)
0041ACDB MOV EAX,DWORD PTR [EBP+0x26]
0041ACE5 SUB EAX,DWORD PTR [EBP+0x60]
0041ACE7 XOR EAX,0x8f063b2
0041ACEC DEC EAX
0041ACED PUSHFD
0041ACEE ADD EAX,0x5d85656d
0041ACFB MOV DWORD PTR [EBP+0x7f],EAX//保存计算后的结果
0041AD17 MOV DL,BYTE PTR [EBP+0x57]
0041AD1B SUB DL,0xb2
0041AD1E CMP DL,0x96 //如果subhandle是96,就进行NEG操作
0041AD21 JNZ 0x94(0041ADBB)
0041AD55 MOV EDX,DWORD PTR [EBP+0x26]
0041AD78 SUB EDX,DWORD PTR [EBP+0x60]
0041AD8C XOR EDX,0x8f063b2
0041AD92 NEG EDX
0041AD94 PUSHFD
0041AD95 ADD EDX,0x5d85656d
0041ADA9 MOV DWORD PTR [EBP+0x7f],EDX
0041ADF5 MOV AL,BYTE PTR [EBP+0x57]
0041AE04 SUB AL,0xb2
0041AE06 CMP AL,0x39 同上,这里是NOT
0041AE08 JNZ 0x9a(0041AEA8)
0041AE32 MOV EBX,DWORD PTR [EBP+0x26]
0041AE5D SUB EBX,DWORD PTR [EBP+0x60]
0041AE65 XOR EBX,0x8f063b2
0041AE6B NOT EBX
0041AE6D CMP ECX,EDX
0041AE6F PUSHFD
0041AE72 ADD EBX,0x5d85656d
0041AEA6 MOV DWORD PTR [EBP+0x7f],EBX
0041AEBD SUB DWORD PTR [EBP+0x53],0x47572fe7
0041AEBF MOV CL,BYTE PTR [EBP+0x57]
0041AEC3 SUB CL,0xb2
0041AEC6 CMP CL,0x41 //下面的我就不一一说明了,大家自己看看,看看能不能找到是什么操作
0041AEC9 JNZ 0x2c(0041AEFB)
0041AED7 MOV ESI,DWORD PTR [EBP+0x26]
0041AEE1 SUB ESI,DWORD PTR [EBP+0x60]
0041AEE3 XOR ESI,0x8f063b2
0041AEE9 INC ESI
0041AEEA PUSHFD
0041AEEB ADD ESI,0x5d85656d
0041AEF9 MOV DWORD PTR [EBP+0x7f],ESI
0041AF01 AND DWORD PTR [EBP+0x14],0x47572fe7
0041AF2B MOV BL,BYTE PTR [EBP+0x57]
0041AF32 SUB BL,0xb2
0041AF35 CMP BL,0xc1
0041AF38 JE 0x165(0041B0A3)
0041AF3E CMP BL,0x6
0041AF41 JE 0x15c(0041B0A3)
0041AF8A MOV DL,BYTE PTR [EBP+0x70]//再次读取参数B的低位
0041AF9D MOV EAX,DWORD PTR [EBP+0x4]
0041AFB1 SUB EAX,0xbdc57aa
0041AFCA SUB DL,0x55
0041AFF4 MOV EBX,DWORD PTR [EBP+0x7f]
0041B000 SUB EBX,0x5d85656d
0041B006 CMP DL,0x1//进行大小的判断,下面就是要保存计算结果了
0041B009 JNZ 0xf(0041B01E)
0041B014 MOV BYTE PTR [EAX],BL
0041B01E CMP DL,0x2
0041B021 JNZ 0x7(0041B02E)
0041B029 MOV WORD PTR [EAX],BX
0041B02E CMP DL,0x3
0041B031 JNZ 0x3d(0041B074)
0041B043 MOV DWORD PTR [EAX],EBX
0041B0B6 POP EDX
//-------分割线---(以下代码都是连续的,我人为的分开是方便大家阅读)-----------------------------
//以下这段不经常用到,就是如果opcode中设置另外个flag的话,是可以读取计算中的EFLAGS寄存器的结果的
0041B0C4 MOV ECX,DWORD PTR [EBP+0x8]
0041B0D9 XOR DWORD PTR [EBP+0x53],0x1e4ffb66
0041B10A SUB DWORD PTR [EBP+0xa7],0x1d2ace37
0041B112 ADD ECX,0x6
0041B129 MOVZX ECX,BYTE PTR [ECX]
0041B12C CMP ECX,0x0
0041B132 JE 0x85(0041B1BD)
0041B180 MOV EDI,DWORD PTR [EBP+0x8]
0041B182 ADD EDI,0x0
0041B196 MOV CX,WORD PTR [EDI]
0041B19E ADD ECX,EBP
0041B1AD MOV DWORD PTR [ECX],EDX
//-------分割线---(以下代码都是连续的,我人为的分开是方便大家阅读)-----------------------------
//最后这段,每个handle都会有,就是重新设置opcode的偏移,读取下一个handler号并跳转,大家可以自己阅读一下
0041B1DF MOV EDX,DWORD PTR [EBP+0x8]
0041B1E8 MOV ESI,DWORD PTR [EBP+0x4f]//这里是handler指针表
0041B1EC ADD EDX,0x7
0041B1FA MOVZX EDX,WORD PTR [EDX]
0041B20E ADD EDX,DWORD PTR [EBP+0x53]
0041B242 XOR EDX,0xf833c89
0041B274 XOR DWORD PTR [EBP+0x53],EDX
0041B278 AND EDX,0xffff
0041B280 SHL EDX,0x2
0041B28D ADD ESI,EDX
0041B298 MOV EDI,DWORD PTR [ESI]
0041B2A4 ADD DWORD PTR [EBP+0x8],0xb
0041B2AA JMP EDI
看完这段代码,我想大家也应该对type1 handler有了一个大概了理解,我在这里总结一下
1、 handler 代码通常由读取opcode ,处理opcode ,保存结果和最后的跳转四部分组成。
2、 加密寄存器无时不刻在对opcode 处理过程中的中间数和结果加密,可以说走一步加密一下,直到最后使用这些结果的时候才解密,真是结果出现的时间被竟可能的压缩。而加密寄存器本身也在不断的被加密,所以想要爆破是不可能的。
3、 Opcode指令序列可以由不同的几个部分组成的,包括 handleid subhandle flag 立即数虚拟寄存器号参数大小的控制参数等,长度是可变的,但具体到哪个偏移量存放那种参数却不一定,完全由handler决定,最长应该不超过12位。
4、 Opcode中的flag说明了后面参数的类型和大小。高位控制类型,1为虚拟寄存器,2为虚拟地址,3为立即数。低位控制大小,3为DWORD,2为WORD,1为BYRE。

最后,我们通过阅读一段从OU中截取的type1 handler处理opcode后的中间代码,来对应的比较和学习一下flag的意义。我们从前到后依次分析。
{001B} [00000039-7D54B913-0000001A-00000000-00000000] NOT EBP
1、前面的001B是handler 的ID,也可以说是handler在handler表中的序号。
2、00000039是subhandle ,其实在Opcode中它只占1位。对应前面代码中的0041AE06位置,可以看到,这是个NOT操作。
3、7D54B913这个其实就是flag,我们只需要看最后1位 0x13,高位的1表示操作数是虚拟寄存器,低位的3代表DOWRD。前面的是解密过程中产生的垃圾代码,当然也不能说完全垃圾,他们是还会被用来加密加密寄存器的。
4、0000001A 这个就是虚拟寄存器的代号了,这里的1A代表EBP,你要问这个代号是哪里来的,抱歉,这个是虚拟机生成时随机产生的,不会有两个加密的程序会完全一样。
5、以上合起来就是后面的一个简单的操作,NOT EBP。化简为繁themida是做的很到位的。

到这里,本文的第二部分也差不多结束了,后面其实我还想把VM FISH中最麻烦的关于EFLAGS的操作和处理,连带条件跳转的判断处理一起整理一下,毕竟不把这块说清楚就不算彻底弄明白FISH VM,当然这也不属于初探的范畴了。只可惜笔记时间久远,而且比较分散,可能需要各位耐心等待一段时间,我有时间一定尽快整理发布。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
打赏
分享
最新回复 (15)
雪    币: 393
活跃值: (224)
能力值: ( LV8,RANK:140 )
在线值:
发帖
回帖
粉丝
BinGzL 1 2016-3-3 14:51
2
0
沙发,前排出售广告位
雪    币: 272
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AASSMM 2016-3-3 14:57
3
0
最强的是 黑老虎 吧,谢谢分享
雪    币: 35388
活跃值: (19185)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2016-3-3 15:31
4
0
精华鼓励!

标 题: 【原创】Themida 2260 虚拟机 FISH 初探(一)  //精华
标 题: 【原创】Themida 2260 虚拟机 FISH 初探(二)  //优秀

说明:由于同一主题分多帖,因此只设第1帖精华,其他的设置优秀。(若续集精彩,会视情况再设精华)
雪    币: 2072
活跃值: (2835)
能力值: (RANK:260 )
在线值:
发帖
回帖
粉丝
xiaohang 3 2016-3-3 15:36
5
0
谢谢看雪老大的鼓励,由于第二篇汇编代码太长,直接接在第一篇后面可能阅读起来不方便,所以分开了。
其实说起来挺惭愧的,当初看雪老大开坛我就来了,这么多年只汲取不贡献,也实在是过意不去,究其原因也许是我懒吧
雪    币: 99
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xmlpull 2016-3-3 18:24
6
0
支持!!!!!!!!
雪    币: 576
活跃值: (1419)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
我是土匪 4 2016-3-3 20:54
7
0
广告招租!
雪    币: 6782
活跃值: (4441)
能力值: (RANK:600 )
在线值:
发帖
回帖
粉丝
gjden 14 2016-3-3 21:23
8
0
楼主加油,长期关注了!
雪    币: 134
活跃值: (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
XXxiaofeng 2016-3-4 10:20
9
0
赞! 期待后续
雪    币: 225
活跃值: (88)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
rock 2016-3-4 10:47
10
0
留名关注
雪    币: 60
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
white、、 2016-3-5 20:20
11
0
非也,复杂度而言 当属黑老鹰, 乱序加混淆等综合体。
雪    币: 65
活跃值: (17)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
jelence 2016-3-10 09:32
12
0
学习中,
雪    币: 346
活跃值: (129)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
毁灭 2 2016-3-11 15:33
13
0
支持一下
雪    币: 160
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
释然 2016-5-23 13:50
15
0
叼啊。楼主qq多少,加你学些
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
rpdgeorge 2016-6-16 11:39
16
0
同求楼主qq
雪    币: 90
活跃值: (80)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
hulucc 2016-12-8 15:52
17
0
opcode的解密其实相当简单,中间那些对opcode的加解密都是可以化简掉的。你根本就没分析出关键的部分。这帖子就是一个空壳
游客
登录 | 注册 方可回帖
返回