作 者: 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 Math