首页
社区
课程
招聘
[分享]VMP学习笔记之反汇编引擎学习(三)
发表于: 2019-8-8 00:42 17440

[分享]VMP学习笔记之反汇编引擎学习(三)

2019-8-8 00:42
17440

参考资料:

本文大量内容抄袭看雪作者:waiWH的VMP系列

1、名称:谈谈vmp的还原(1)

网址:https://bbs.pediy.com/thread-225278.htm

2、名称:汇编指令之OpCode快速入门

网址:https://bbs.pediy.com/thread-113402.htm

3、名称:X86指令编码内幕 --- 指令 Opcode 码

网址:https://blog.csdn.net/xfcyhuang/article/details/6230542


说明:

0、我调试的是VMProtect1.21,系统是win7 32位。

1、将struct_VmFunctionAddr结构体称为需要二次解析的称为:特殊Opcode(SetDisassemblyFunction_Address函数填充)

2、将struct_DisassemblyFunction结构体称为:基础Opcode(Vmp_Disassembly填充)

3、壳模板代码和用户加密代码都是调用Vmp_AllDisassembly函数解析,只是保存的位置不一样而已

4、注意struct_DisassemblyFunction是按顺序存放的

例如:

push 1

push 2

push 3

那么push 1 肯定存放在Address[0]

那么push 2 肯定存放在Address[1]

那么push 3 肯定存放在Address[2]


正文:

1、Vmp_AllDisassembly框架详解

总结:

1、核心部分在于Vmp_Disassembly函数,里面就是解析Opcode指令

2、部分Opcode需要二次处理

3、解析壳自身代码跟用户Opcode都是调用这个函数


2、Vmp_Disassembly解析Opcode函数分析

0、随便拿条Opcode实例说明:

举例Opcode:0047497B 8B7424 28 mov esi,dword ptr ss:[esp+0x28]

1、读取主操作码或则前缀,因为Prefix 与 Opcode 共同占用这个空间

由于 x86/x64 是 CISC 架构,指令不定长。解码器解码的唯一途径就是按指令编码的序列进行解码,关键是第 1 字节是什么? 如:遇到 66h,它就是 prefix,遇到 89h,它就是 Opcode。

1、1:GetSize_0函数

函数作用:用来区别读取字节长度

哪里找到赋值的?

Vmp_ReadPEInformation函数,我整个文件导了个遍都只发现赋值为1,没有2?

1、2:三个比较重要的变量(legacy prefix 的作用)

UPX0:00481DDE ; 546: v529 = 0;

UPX0:00481DDE 094 C6 45 F5 00 mov [ebp+var_B], 0

UPX0:00481DE2 ; 547: v531 = 0;

UPX0:00481DE2 094 C6 45 F7 00 mov [ebp+var_9], 0

UPX0:00481DE6 ; 548: v530 = 0;

UPX0:00481DE6 094 C6 45 F6 00 mov [ebp+var_A], 0

1、它们在哪里赋值?

v529 赋值的地方:

case 0x66u: // 指令前缀:66H—操作数大小重载前缀,也可被用作某些指令的强制性前缀.

v529 = 1;

v531 赋值的地方:

case 0x67u: // 指令前缀:67H—地址尺寸重载前缀

v531 = 1;

v530 赋值的地方:(大概猜测是Rex前缀,因为没有Magic=2)

REX前缀是16个编码操作码的集合,包含40H到4FH。这些操作码在IA-32模式和兼容模式中代表有意义的指令。在64位模式中,相同的操作码则代表REX前缀,不再当做单独的指令看待。


2、根据switch执行不同的流程解析Opcode

2、1:通过手册我们得知8B对应的是MOV r32,r/m32(Gv, Ev)

2、2: Register_Or_Memory = (v13 & 2) == 2这句代码是什么意思?(只针对我举例的,这里只是说明如何找)

我们翻看手册发现了规律是判断目标操作数:G 是寄存器或则E 是寄存器或者内存操作数

Gv, Ev 表示:


(1)两个 Operands 分别是:目标操作数 Gv,源操作数 Ev 或说:frist operand 是 Gv, second operand 是 Ev


(2)Gv 表示:G 是寄存器操作数,v 是表示操作数大小依赖于指令的 Effective Operand-Size,可以是 16 位,32 位以及 64 位。


(3)Ev 表示:E 是寄存器或者内存操作数,具体要依赖于 ModRM.r/m,操作数大小和 G 一致。

4 个字符便可以很直观的表示出:操作数的个数以及寻址方式,更重要的信息是这个 Opcode 的操作数需要 ModRM 进行寻址。


举例子说明:

0047497B    8B7424 28       mov esi,dword ptr ss:[esp+0x28]

0047497B    8A7424 28       mov dh,byte ptr ss:[esp+0x28]

 

0047497B    887424 28       mov byte ptr ss:[esp+0x28],dh

0047497B    897424 28       mov dword ptr ss:[esp+0x28],esi

 

v14 =88 Register_Or_Memory =0

v14 =89 Register_Or_Memory =0

v14 =8a Register_Or_Memory =1

v14 =8b Register_Or_Memory =1

总结:

1、这句代码是判断目标操作数是:G 是寄存器或则E 是寄存器或者内存操作数

2、Ev是包含不确定性具体要依赖于 ModRM.r/m


2、3:通过上文描述就可以解释作者为何设计成要区分Register_Or_Memory来区分先执行SetReg跟ModRm

因为假设是Mov Gv,Ev这种类型的:目标操作数是确定Gv,但是源操作数是Ev是包含不确定性具体要依赖于 ModRM.r/m

我们举例的很明显就是MOV r32,r/m32(Gv, Ev),目标已知,源带有未知性

2、4:我们先来分析Decode_SetReg函数

一共有3组,每组0x17个字节,包含结尾表示0xFFFFFFFF,这些都是保存目标操作数或则源操作数信息的

v6 = (v9 >> 3) & 7;

首先v9=0x74,继续我们的查表

转换成二进制如下:

0x74=‭‭01 110 100‬

很明显v6=ModRM.reg(Esi)


2、5:分析Decode_ModRM结构

1、首先解析ModRm判断寻址模式


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

最后于 2019-8-8 18:36 被黑手鱼编辑 ,原因:
上传的附件:
收藏
免费 5
支持
分享
最新回复 (4)
雪    币: 1431
活跃值: (4413)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
2
后面的没整理完就没发了,还有部分自己没看懂。以后再发
2019-8-8 00:45
0
雪    币: 12332
活跃值: (5103)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

多谢楼主分享哟
2019-8-8 08:21
0
雪    币: 3279
活跃值: (3326)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
很厉害,谢谢分享
2019-8-8 16:52
0
雪    币: 401
活跃值: (1186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼  下载链接失效了吗?
2023-2-25 21:28
0
游客
登录 | 注册 方可回帖
返回
//