首页
社区
课程
招聘
Asprotect 中的 X86 虚拟机代码分析[原创]
发表于: 2006-11-1 02:11 29112

Asprotect 中的 X86 虚拟机代码分析[原创]

2006-11-1 02:11
29112

Asprotect 中的 X86 虚拟机代码分析

作    者: blackeyes

1.  起因:

    最近跟踪一 Asprotect 保护的程序, 发现 stolen code 都是在 Asprotect 自己的虚拟机中执行,

非常不利于跟踪与分析, 于是把 Asprotect 的虚拟机代码进行了分析.

2.  代码处理概述

还是用例子来说明吧, 原始的一段 CODE 如下:

00D6FC1C    55              PUSH EBP
00D6FC1D    8BEC            MOV EBP,ESP
00D6FC1F    83C4 E0         ADD ESP,-20
...
00D6FD48    8BE5            MOV ESP,EBP
00D6FD4A    5D              POP EBP
00D6FD4B    C2 0C00         RETN 0C

Asprotect 将上面的每一行机器代码分析处理, 然后每一行保存到一个固定大小的结构中,

运行的时候这段代码就只需要下面四行:

00D6FC1C    68 00000000     PUSH 0
00D6FC21    68 1CFCD600     PUSH 0D6FC1C
00D6FC26    68 B432E600     PUSH 0E632B4
00D6FC2B    E8 18960000     CALL 00D79248

其中:
00D6FC1C ----- 代码起始地址
00E632B4 ----- 一结构起始地址, 包含处理后的代码信息
00D79248 ----- X86 虚拟机 Function 地址

3.  机器代码分析

每一行机器代码被分析处理后, 会分解成 10 项 保存到结构中, 如下:

   1   -   第 1 个 机器码的内存起始地址;

   2   -   机器码的第 1 个 BYTE, 如果不是前缀机器码, 就是真正的机器码的第 1 个 BYTE;

   3   -   机器码的第 2 个 BYTE, 并且前面是前缀机器码, 它是真正的机器码的第 1 个 BYTE;

   4   -   机器码中的立即数是否要调整, 相当于重定位, 例如;

               00400000  68 34124000   PUSH 00401234
          
           如果希望这行代码在 00500000 是这样工作的:

               00500000  68 34125000   PUSH 00501234

           即表示机器码中的立即数是随段起始地址而调整的.

   5   -   机器码中的 第 1 个 立即数;

   6   -   机器码中的 第 2 个 立即数;

   7   -   机器码中的算术/逻辑操作;

                0:ADD, 1:OR, 2:ADC, 3:SBB, 4:TEST, 5:SUB, 6:XOR, 7:CMP

   8   -   机器码中的 ModRM 操作码;

   9   -   机器码中的 SIM 操作码;

   10  -   机器码中的 Displacement 操作码;

     每一项都由一 Function 读出, 其中一些还要做一些变换.

     并不是每一项都存在于每一行机器码.

4.  机器代码数据结构

每一行机器代码对应的结构如下:

typedef struct {
        BYTE    FirstOpcode_0;
        BYTE    Unknown1;
        BYTE    SecondImmediateData;
        BYTE    Unknown2[5];
        BYTE    SIMOpcode;
        BYTE    Unknown3[2];
        DWORD   DisplacementOpcode;
        BYTE    Unknown4;
       
        BYTE    FirstOpcode_1; // if FirstOpCode is a prefix
        BYTE    Unknown5[3];
        DWORD   ImmediateDataOpcode;
        DWORD   Unknown6;
        BOOL    bAdjustValueFlag;
        BYTE    Unknown7;
        DWORD   EncryptedEIPAddress; //+1E , EncryptedEIPAddress + baseAddress + randxx ==>EIPAddress
        BYTE    Unknown8[2];
        BYTE    MathType; // Mathtype or an additional opcode
        BYTE    Unknown9[3];
        BYTE    ModRMOpcode;
        DWORD   Unknown10;
} ENC_LINE;

每一段代码由 n 行代码构成, 对应如下的结构:

typedef struct {
        DWORD   Unknown;
        DWORD   pFirstItem;
        DWORD   ItemNum;
        BYTE    FuncIndex[0x0A];         // 00E632C0  01 06 05 00 08 04 03 07 02 09
        BYTE    FuncIndex2[0x0A];
        DWORD   Funcs[0x0A];
/*
00E632D4  011F0000    // return __0014     Func3
00E632D8  011E0000    // return __10       Func0
00E632DC  01200000    // return __1C       Func8
00E632E0  011C0000    // return __08       Func6
00E632E4  01230000    // return __28       Func5
00E632E8  01220000    // return __24       Func2
00E632EC  011A0000    // return __00       Func1
00E632F0  011D0000    // return __000B     Func7
00E632F4  011B0000    // return __02           Func4
00E632F8  01210000    // return __001E     Func9
*/
        DWORD   ItemSize;
        DWORD   BaseAddr;
        DWORD   RandomXX; // +50
        DWORD   procID;
        DWORD   Size;
        ENC_LINE Lines[0];
} ENC_INFO;

其中 FuncIndex[], FuncIndex2[], Funcs[], 每次运行都会随机重新排序, 但是

for(i=0;i<0x0A;i++) {
   j = FuncIndex[i];
   Funcxx = Funcs[j];      // Funxx 跟 i 是一一对应的
}

这是Funcxx的返回值 与 i 的 对应图

00E63310  A1 88 00 4A 7B A2 B0 2F 00 0F 18 00 00 00 00 C9  1?4?????6??7777?
00E63320  00 54 F7 21 00 00 00 00 7D 05 54 89 00 9C 68 FD  0???3333????8?99
00E63330  EC A1 7B BC 00 25 45 BF 00 61 08 4A F5           99??2???5????

5.  虚拟机数据结构

typedef struct {
        DWORD   Unknown1[8];  // Drx[8]??
#define EAX    REGs[0]
#define ECX    REGs[1]
#define EDX    REGs[2]
#define EBX    REGs[3]
#define ESP    REGs[4]
#define EBP    REGs[5]
#define ESI    REGs[6]
#define EDI    REGs[7]
        DWORD   REGs[8];
        DWORD   Unknown2;
        DWORD   EIP;
        DWORD   EFlags;
        DWORD   Unknown3;
        DWORD   CS_base, SS_base, DS_base, ES_base, FS_base, GS_base; // +60
        WORD    CS, SS, DS, ES, FS, GS;  // +78
        BYTE    Unknown4[0x0C]; //+84
        DWORD   LowOpNum; //+90
        DWORD   HiOpNum;  //+94
        DWORD   CurSegbase; //+98
        BOOL    bAdjustValueFlag; //+9C
        DWORD   seh; // +9D
        BYTE    bPrefixFlag; //+A1

        OpDataInfo Src; // +A2
        OpDataInfo Dst; //+A7

        DWORD   OperandSize; // +AC:  1 - byte ptr, 2 - word ptr, 4 - dword ptr

#define X86_FLAGS_LOCK_PREFIX         0x0001
#define X86_FLAGS_REPNE_PREFIX        0x0002
#define X86_FLAGS_REP_PREFIX          0x0004
#define X86_FLAGS_CS_PREFIX           0x0008
#define X86_FLAGS_SS_PREFIX           0x0010
#define X86_FLAGS_DS_PREFIX           0x0020
#define X86_FLAGS_ES_PREFIX           0x0040
#define X86_FLAGS_FS_PREFIX           0x0080
#define X86_FLAGS_GS_PREFIX           0x0100
#define X86_FLAGS_OPERAND_PREFIX      0x0200
#define X86_FLAGS_ADDRESS_PREFIX      0x0400
        DWORD   BitFlags; // +B0
        BYTE    Opcode;
        DWORD   curCodeItem;
        DWORD   nextCodeItem;
} X86_INFO;

其中

typedef struct {
        union {
                DWORD   RegIndex;
                DWORD   Address;
        } u;
        BYTE RegMode; // 1: RegMode, 0: MemAddrMode, 2:ImmediateData, 4:MemAddrMode, dispx[Regx]?
} OpDataInfo;

当进入到 虚拟机 Function 地址后, 在栈上开出一片空间, 为X86_INFO, 代表了虚拟机的状态, 主要就是

CPU 的各个寄存器, 及一些标志.

6. 转储(DUMP) ENC_INFO 和 ENC_LINE[] 结构

用 OD 附加在运行的程序上, 然后在 ASPR 所在的内存段, SEARCH 下面的代码

addr_xx:
       68 00000000     PUSH 0
       68 xxxxxxxx     PUSH addr_xx
       68 yyyyyyyy     PUSH addr_yy
       E8 zzzzzzzz     CALL func_zz

其中 addr_yy 就是结构 ENC_INFO 的地址, 根据其中的 ItemNum 和 ItemSize 可以确定需要DUMP 的大小.

附件中的 aspr_x86_dump_info.txt 可用来帮助DUMP

7. 还原机器码

刚开始以为, 可以在各个 Funcxx 中 设 断点来DUMP 再还原机器码, 也不理想, 理由如下:

  A.) 各个 Funcxx, 有的对应机器码, 有的不对应机器码;

  B.) 有些即使对应机器码, 还要进行变换;

  C.) 有些 Funcxx , 可能对应 1 BYTE, 1WORD, 或者 1DWORD, 与参数有关;

  D.) 如果有循环, 就会有重复;
  
  E.) 如果有跳转, 未运行到的CODE 又没法 DUMP.

所以还是把它的X86处理CODE分析清楚, 再写 CODE 还原;

好在它是按标准的Intel的X86指令集来处理的, 参考 Intel 的 "Instruction Set Reference", 好多CODE 很容易明白,

刚开始还想把每一行机器码的反汇编弄出来, 试了以下, 有点累, 算了.

附件中的 aspr_x86.c 和 aspr_x86.h 可用来处理 DUMP 出来的二进制数据, 还原机器码

8. Reference

A. ) 24319102.PDF, "Intel Architecture Software Developer's Manual Volume2: Instruction Set Reference";

B. ) 被分析的程序: "Registry Defragmentation 8.2.6.11"

8.  后记

分析完ASPR的这段CODE后, 对 Intel 的X86 指令集又熟悉了一些, 也算是有所收获.

以下是附件中的各个文件:

A.) aspr_x86_code.txt           ASPR_X86 CODE 跟踪分析笔记

B.) aspr_x86_dump_info.txt      ODbgScript 脚本, DUMP X86_INFO

C.) aspr_x86.c & aspr_x86.h     C 代码, 处理DUMP 的 X86_INFO


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

上传的附件:
收藏
免费 7
支持
分享
最新回复 (45)
雪    币: 10
活跃值: (130)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
强人啊~有了这个就方便多了~
2006-11-1 02:52
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
shoooo II
Ding!!!
2006-11-1 02:56
0
雪    币: 214
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
牛人
2006-11-1 03:24
0
雪    币: 417
活跃值: (475)
能力值: ( LV9,RANK:1250 )
在线值:
发帖
回帖
粉丝
5
膜拜.
2006-11-1 07:35
0
雪    币: 47147
活跃值: (20410)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
6
当你不经意地一回头,就会发现身边都是强人  ;)
建议 blackeyes再进一步,能写个自动还原的工具就更棒了
2006-11-1 07:42
0
雪    币: 279
活跃值: (145)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
7
强人!学习
2006-11-1 07:55
0
雪    币: 4
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
牛x
2006-11-1 08:06
0
雪    币: 254
活跃值: (126)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
9
当我不经意地一回头,就发现屋里屋外都是强人  ;)
2006-11-1 09:01
0
雪    币: 236
活跃值: (69)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
都是强人,学习
2006-11-1 09:50
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
11
USER_BUFFER 算是废了
2006-11-1 10:32
0
雪    币: 405
活跃值: (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
12
看不懂。站个位置。支持下人气
2006-11-1 11:03
0
雪    币: 300
活跃值: (412)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
13
满眼尽是超强人
2006-11-1 11:52
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
14
支持强人,vm真是要泛滥成灾了
2006-11-1 12:32
0
雪    币: 331
活跃值: (56)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
15
几天不来就有新惊喜~~~强!
2006-11-1 14:20
0
雪    币: 215
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
真是好强啊,完全不懂,学习!
2006-11-1 16:09
0
雪    币: 70
活跃值: (74)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
好强啊!  
2006-11-1 18:06
0
雪    币: 107
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
顶贴膜拜ing
2006-11-1 18:48
0
雪    币: 191
活跃值: (41)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
19
强,跟着牛人一起顶一下。
2006-11-1 19:14
0
雪    币: 312
活跃值: (27)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
完全不懂,跟着强人走
2006-11-2 08:07
0
雪    币: 184
活跃值: (108)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
21


好久不来,来了就发现被强人来了个强身健体
2006-11-2 08:59
0
雪    币: 255
活跃值: (266)
能力值: ( LV12,RANK:220 )
在线值:
发帖
回帖
粉丝
22
最初由 heXer 发布
当我不经意地一回头,就发现屋里屋外都是强人 ;)

当我不经意地一回头,发现大人小孩都是强人
2006-11-2 09:44
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
整幢楼都是牛人、强人~~
唯我楼外做菜~
2006-11-2 12:22
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
都这么强,留个脚印
2006-11-2 16:04
0
雪    币: 150
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
看雪是个好地方让我成长很快~顶一下~
可惜没有早点来呀~
呵呵,好像现在也不晚呀~
2006-11-2 16:32
0
游客
登录 | 注册 方可回帖
返回
//