安全中国首页 > 文章中心 > 基础知识
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:download.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

Asprotect 中的 X86 虚拟机代码分析

更新时间:2007-12-8 0:38:45
责任编辑:阿loosen
热 点:
作    者: 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????

1 2 下一页

 
相关文章
一日一文章
 
一日一软件
一日一动画