VM逆向学习

1.基本原理

1.概述

这里的vm基本逆向,是一种解释执行系统或者模拟器

(并非vmware之类的虚拟机)

2.基本结构

img

1.vm_start

虚拟机的入口函函数,初始化虚拟机环境

2.vm_dispatcher

调度器,解释opcode,并选择对应的handle函数执行3.opcode

当handle执行完后会条回到这里,形成一个循环

3.opcode

程序可执行的代码转换为操作码

3.逆向原理

1.对整个emulator结构进行逆向,理解程序功能

2.要结合opcode进行分析,进行逆向分析

2.分析方法

1.比赛常见思路

  • 给出可执行的程序和opcode,逆向emulator,结合opcode,得到flag
  • 只给出可执行程序,逆向emulator,构造opcode,得到flag

2.逆向思路记录

  • 分析虚拟机入口,明确虚拟机输入或者opcode的位置
  • 整理虚拟机结构,包括Dispatcher和Handler
  • 逆向各个Handler,分析opcode的意义

3.实现一个小型虚拟机的关键结构体具象理解结构

1.解释器的基本结构体

1.vm_cpu

typedef struct
{
  unsigned long r1;   //虚拟寄存器r1
  unsigned long r2;   //虚拟寄存器r2
  unsigned long r3;   //虚拟寄存器r3
  unsigned char *eip;   //指向正在解释的opcode地址
  vm_opcode op_list[OPCODE_N];   //opcode列表,存放了所有的opcode及其对应的处理函数
}vm_cpu;
  • 1-r3是定义的通用寄存器,用来传参或者是存放返回值
  • eip指向正在解释的opcode的地址

2.vm_opcode

typedef struct
{
  unsigned char opcode;
  void (*handle)(void*);
}vm_opcode;

实现解释器:

解释器的功能就是对opcode解析

选择相应的handle函数,并且将相应的参数传递给handle函数,由handle函数来解释执行一条指令

3.vm_init

void *vm_init()
{
  vm_vpu *cpu; // 声明一个指向 vm_vpu 结构的指针

  // 注意:此处可能会导致段错误,因为 cpu 尚未初始化
  cpu->r1 = 0; // 将寄存器 r1 初始化为 0
  cpu->r2 = 0; // 将寄存器 r2 初始化为 0
  cpu->r3 = 0; // 将寄存器 r3 初始化为 0

  // 将 eip 指向 opcode 的地址
  cpu->eip = (unsigned char *)vm_code;

  // 将操作码与对应的处理函数关联起来
  cpu->op_list[0].opcode = 0xf1; // 第一个操作码为 0xf1
  cpu->op_list[0].handle = (void (*)(void *))mov; // 将 mov 函数与操作码关联

  cpu->op_list[1].opcode = 0xf2; // 第二个操作码为 0xf2
  cpu->op_list[1].handle = (void (*)(void *))xor; // 将 xor 函数与操作码关联

  cpu->op_list[2].opcode = 0xf5; // 第三个操作码为 0xf5
  cpu->op_list[2].handle = (void (*)(void *))read_; // 将 read_ 函数与操作码关联

  // 分配内存给虚拟机栈
  vm_stack = malloc(0x512);
  memset(vm_stack, 0, 0x512); // 将分配的内存清零
}

4.vm_start

void vm_start(vm_cpu *cpu)
{
  /*
  进入虚拟机
  eip指向要被解释的opcode地址
  */
  cpu->eip = (unsigned char*)opcodes;
  while((*cpu->eip) != 0xf4)//如果opcode不为RET,就调用vm_dispatcher来解释执行
  {
      vm_dispatcher(*cpu->eip)
  }
}

5.vm_dispatcher

void vm_dispatcher(vm_cpu *cpu)
{
  int i;
  for(i = 0; i < OPCODE_N; i++)
  {    
      if(*cpu->eip == cpu->op_list[i].opcode)
      {
          cpu->op_list[i].handle(cpu);
          break;
      }
  }
}

6.handles

void mov(vm_cpu *cpu);
void xor(vm_cpu *cpu);   //xor flag
void read_(vm_cpu *cpu);   //call read, read the flag

void xor(vm_cpu *cpu)
{
  int temp;
  temp = cpu->r1 ^ cpu->r2;
  temp ^= 0x12;
  cpu->r1 = temp;
  cpu->eip += 1;   //xor指令占一个字节
}
void read_(vm_cpu *cpu)
{
  char *dest = vm_stack;
  read(0,dest,12);   //用于往虚拟机的栈上读取数据
  cpu->eip += 1;   //read_指令占一个字节
}
void mov(vm_cpu *cpu)
{
  //mov指令的参数都因曾在字节码也就是vm_code中,指令表示后的一个字节是寄存器表示,第二到
//第五是要mov的数据在vm_stack上的偏移
  //这里只是实现了从vm_stack上取数据和存数据到vm_stack上
  unsigned char *res = cpu->eip + 1;   //寄存器标识
  int *offset = (int *)(cpu->eip + 2);   //寄存器在vm_stack上的偏移
  char *dest = 0;
  dest = vm_stack;

  switch (*res) {
      case 0xe1:
          cpu->r1 = *(dest + *offset);
          break;  

      case 0xe2:
          cpu->r2 = *(dest + *offset);
          break;  

      case 0xe3:
          cpu->r3 = *(dest + *offset);
          break;  
      case 0xe4:
      {
          int x = cpu->r1;
          *(dest + *offset) = x;
          break;

      }
  }  
  cpu->eip += 6;
  //mov指令占六个字节,所以eip要向后移6位
}

2.将实现功能的伪代码转成自定义的vm_code的基本结构

1.伪代码的功能:

  1. 从标准输入中读取12个字节的字符串
  2. 将你个读入的字符串每个字符与0x0还有0x12进行异或
  3. 将结果存储在虚拟机的栈上/*
      call read_
      MOV R1,flag[0]
      XOR
      MOV R1,0x20;   //这是将R1的值送到vm_stack+0x20的位置,后面的同上
      MOV R1,flag[1]
      XOR
      MOV R1,0x21;
      MOV R1,flag[2]
      XOR
      MOV R1,0x22
      MOV R1,flag[3]
      XOR
      MOV R1,0x23;
      MOV R1,flag[4]
      XOR
      MOV R1,0x24;
      MOV R1,flag[5]
      XOR
      MOV R1,0x25;
      MOV R1,flag[6]
      XOR
      MOV R1,0x26;
      MOV R1,flag[7]
      XOR
      MOV R1,0x26
      MOV R1,flag[8]
      XOR
      MOV R1,0X27
      MOV R1,flag[9]
      XOR
      MOV R1,0x28
      MOV R1,flag[10]
      XOR
      MOV R1,0X29
      MOV R1,flag[11]
      XOR
      MOV R1,0x2A
      RET
    */2.vm_code用来存放操作码unsigned char vm_code[] = {
    0xf5,
      0xf1,0xe1,0x0,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x20,0x00,0x00,0x00,
      0xf1,0xe1,0x1,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x21,0x00,0x00,0x00,
      0xf1,0xe1,0x2,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x22,0x00,0x00,0x00,
      0xf1,0xe1,0x3,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x23,0x00,0x00,0x00,
      0xf1,0xe1,0x4,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x24,0x00,0x00,0x00,
      0xf1,0xe1,0x5,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x25,0x00,0x00,0x00,
      0xf1,0xe1,0x6,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x26,0x00,0x00,0x00,
      0xf1,0xe1,0x7,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x27,0x00,0x00,0x00,
      0xf1,0xe1,0x8,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x28,0x00,0x00,0x00,
      0xf1,0xe1,0x9,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x29,0x00,0x00,0x00,
      0xf1,0xe1,0xa,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x2a,0x00,0x00,0x00,
      0xf1,0xe1,0xb,0x00,0x00,0x00,0xf2,0xf1,0xe4,0x2b,0x00,0x00,0x00,
      0xf4
    };
  4. vm_start()函数中对opcode进行解析
  5. 把*cpu->eip做if/switch
  6. 在其嵌套中做一些操作并改变指针
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇