wai1216 发表于 2018-4-14 19:24:16

谈谈vmp的还原(3)

本帖最后由 wai1216 于 2018-4-15 18:25 编辑

0x00 前言
写在5.4之前

vs2010,代码放在了github:vmp

0x01 寄存器
先说context
init初始化context分两种情况

那么第二种,就是随机生成了

关于pushad pushfd的保存

如何选取空闲的context代码如下:
int n = 0, k = 0;
char cpu = {0};

int _getContextIndex( int type)
{
      if( tpye - 1 >= 2)
      {
                switch(type)
                {
                        case 0:
                        case 4:
                              return dis->reg;
                        case 1:
                        case 5:
                              return dis->reg;
                        case 2:
                        case 6:
                              return dis->reg;
                        case 3:
                        case 7:
                              return dis->reg;                        
                        case 9:
                        case 0xD:
                              return dis->reg;               
                        default:
                              return 0;                                                                                                
                }
      }
      else
      {
                switch(type)
                {
                case 0:
                case 1:
                case 2:
                case 3:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                case 0xa:
                        return dis->reg;
                case 4:
                        return -1;
                default:
                        return 0;
                }
      }
}

for( int i = 0; i < 16; i++)
{
      if( dis->reg < 0xff)
      {
                        cpu[ dis->reg] = 1;
      }
}
i = 0;
while( cpu || rand()%2 != 1)
{
      i++;
      return _getContextIndex(tpye);
}
dis->reg = n;
return _getContextIndex(tpye);这里说下,看到这部分实现的时候,脑子里的想法是当成树结构,用dfs去识别判断,因为以为是真轮转。记得,还给校长说,加密与解密那个目录处理的方法好像是图算法。
再注意到a2/v13 == 2, 才会去get context, 随意看一个调用

关系到lval
那么其他的情况在只是return context,那么就好办了,因为可以得到,并不是真轮转

0x10 变形
下面这个东西,也是我最佩服vmp作者的地方,因为就这点东西,就可以做很多事情了。

首先这个函数,我也不知道怎么命名好,所以就当成fixed吧,可能叫plasmodium比较好

_BOOL1 __fastcall CHECK_POP_ESP_OR_CONTEXT(struct_esi *a1)
{
return a1->_var1_vm_interpreter_mnemonic == 2
      && a1->_var2_addressing_mode == 2
      && (!a1->_var3_lval_type_byte_word_dword_or_vm_displacement || a1->_reg_index != 4);
}

_BOOL1 __fastcall Check_PUSH_ESP_OR_CONTEXT(struct_esi *a1, char a2, char a3)
{
return a1->_var1_vm_interpreter_mnemonic == 1
      && a1->_var2_addressing_mode == 2
      && a2 == a1->_var3_lval_type_byte_word_dword_or_vm_displacement
      && a3 == a1->_reg_index;
}

_BOOL1 __fastcall CHECK_POP_AX(struct_esi *a1)
{
return a1->_var1_vm_interpreter_mnemonic == 2 && a1->_var2_addressing_mode == 1;
}

//函数倒叙判断
_BOOL1 __usercall Vmp_Fixed@<al>(int *_index@<eax>, char a2@<dl>, int a3)
{
bool _ret = 0;

if ( *_index >= 2 )
{
    _cur_index = *_index;
    _struct_cur = getStruct_1(*(a3 - 4), *_index);
      //判断当前vm_mnemonic是否为0x4,0xf6
    if ( _struct_cur->_var1_vm_interpreter_mnemonic != 4
                && _struct_cur->_var1_vm_interpreter_mnemonic != 0xF6
                || _struct_cur->_var3_lval_type_byte_word_dword_or_vm_displacement != __var3_lval_type_byte_word_dword_or_vm_displacement )// ?
    {
                //为其他mnemonic 高于0x2e
      if ( _struct_cur->_var1_vm_interpreter_mnemonic>= 0x2A )
      {
      if ( CHECK_POP_ESP_OR_CONTEXT(_struct_cur) && getlen(*(a3 - 4)) - 1 > _cur_index )
      {
          if ( Check_PUSH_ESP_OR_CONTEXT(_struct_next,_struct_cur->_var3_lval_type_byte_word_dword_or_vm_displacement,_struct_cur->_reg_index)
                              && System::__linkproc__ RandInt(2) == 1 )
          {
            _struct_next->_reg_index = 4;
            _struct_next->_var3_lval_type_byte_word_dword_or_vm_displacement = 2;
            _struct_next->_other |= 4u;
            _struct_new = (*(**(a3 - 4) + 16))();
            _struct_new->_var1_vm_interpreter_mnemonic = 1;
            _struct_new->_var2_addressing_mode = 3;
            _struct_new->_var4_index = 3;
            _struct_new->_var3_lval_type_byte_word_dword_or_vm_displacement = _struct_cur->_var3_lval_type_byte_word_dword_or_vm_displacement;
            _struct_new->_other |= 4u;
            _mov_var2_to_var3(*(a3 - 4), _struct_new, _struct_cur);
            _mov_var2_to_var3(*(a3 - 4), _final, _struct_new);// add_to_list
          }
      }
      else if ( CHECK_POP_AX(_struct_cur) )          // _fix var2 == 2 && var3 == 1
      {
          _next = _cur_index + 1;
          _size = getlen(*(a3 - 4)) - 1;
                  __ = __OFSUB__(_size,_next);
          _len = _size - _next;
          if ( !((_len < 0) ^ __) )
          {
            _pos = _len + 1;
            _next = _next;
            do
            {
            _struct_next = getStruct_1(*(a3 - 4), _next);
            if ( !CHECK_POP_ESP_OR_CONTEXT(_struct_next)
                                  || CHECK_POP_ESP(_struct_next)
                                  || _struct_next->_var3_lval_type_byte_word_dword_or_vm_displacement != _struct_cur->_var3_lval_type_byte_word_dword_or_vm_displacement )
            {
                break;
            }
            if ( System::__linkproc__ RandInt(2) == 1 )
            {
                _struct_cur->_var2_addressing_mode = 2;
                _struct_cur->_reg_index = _struct_next->_reg_index;
                _struct_cur->_other |= 4u;
                return _ret;
            }
            ++_next;
            --_pos;
            }
            while ( _pos );
          }
      }
      }//0x1d之前 basic....
      else if ( _struct_cur->_var3_lval_type_byte_word_dword_or_vm_displacement == __var3_lval_type_byte_word_dword_or_vm_displacement )//?
      {
      _struct_prev_prev = getStruct_1(*(a3 - 4), _cur_index - 2);
      _struct_prev = getStruct_1(*(a3 - 4), _cur_index - 1);
      if ( CHECK_LODSW_PUSH_AX(_struct_prev_prev, 1) && CHECK_LODS_PUSH_CONTEXT(_struct_prev, __var3_lval_type_byte_word_dword_or_vm_displacement) )
      {
          _cur_index -= 2;//移动到_prev_prev,指针
          if ( !_struct_prev_prev->_rand_switch && !_struct_prev->_rand_switch )
          {
            _ret = !(_cur->_other & 1) && !CHECK_POP_ESP(_struct_prev);
          }
      }
      }
    }
    else
    {
      _struct_prev_prev = getStruct_1(*(a3 - 4), _cur_index - 2);
      _struct_prev = getStruct_1(*(a3 - 4), _cur_index - 1);
      if ( CHECK_LODS_PUSH_CONTEXT(_struct_prev_prev, __var3_lval_type_byte_word_dword_or_vm_displacement) && CHECK_LODS_PUSH_CONTEXT(_struct_prev, __var3_lval_type_byte_word_dword_or_vm_displacement) )
      {
      _cur_index -= 2;
      if ( _struct_prev_prev->_rand_switch || _struct_prev->_rand_switch || System::__linkproc__ RandInt(2) != 1 )
      {
          v10 = !CHECK_POP_ESP(_struct_prev_prev) && !CHECK_POP_ESP(_struct_prev);
          _ret = v10;
      }
      else
      {
          if ( CHECK_POP_ESP(_struct_prev_prev) )
          {
            if ( _struct_prev->_var2_addressing_mode == 1 )
            *_struct_prev->_Displacement_Immediate += 4;
          }
          else if ( CHECK_POP_ESP(_struct_prev) )
          {
            if ( _struct_prev_prev->_var2_addressing_mode == 1 )
            *_struct_prev_prev->_Displacement_Immediate -= 4;
          }
          else
          {
            _ret = 1;
          }
          if ( _struct_prev->_var2_addressing_mode != 2
            || _struct_prev_prev->_var2_addressing_mode != 2
            || _struct_prev_prev->_reg_index != _struct_prev->_reg_index )
          {
            _struct_prev_prev->_other |= 4u;
            _struct_prev->_other |= 4u;
            _ExchangePos(*(a3 - 4), _struct_prev, _struct_prev_prev);// 交换当前的前两个
          }
          else
          {
            _struct_prev->_reg_index = 4;            // fix
            _struct_prev->_var3_lval_type_byte_word_dword_or_vm_displacement = 2;
            _struct_prev->_other |= 4u;
            _struct_new = (*(**(a3 - 4) + 16))();
            _struct_new->_var1_vm_interpreter_mnemonic = 1;// push ...
            _struct_new->_var2_addressing_mode = 3;
            _struct_new->_var4_index = 3;
            _struct_new->_var3_lval_type_byte_word_dword_or_vm_displacement = _first->_var3_lval_type_byte_word_dword_or_vm_displacement;
            _struct_new->_other |= 4u;
            _mov_var2_to_var3(*(a3 - 4), _struct_end, _struct_cur);
          }
      }
      }
      else
      {
      if ( CHECK_LODS_PUSH_CONTEXT(_struct_prev, __var3_lval_type_byte_word_dword_or_vm_displacement) )
      {
          --_cur;//指针-1
          if ( CHECK_POP_ESP(_struct_prev) )
            return _ret;
          _prev = _cur - 1;
          if ( Vmp_Fixed(&_prev, __var3_lval_type_byte_word_dword_or_vm_displacement, a3) == 0 )// 递归调用,这时候就体现出第3个的用处了
            return _ret;
          *v3 = _prev;
          _prev = _prev;
          _ret = 1;
      }
      else
      {
          _prev = _cur - 1;
          if ( Vmp_Fixed(&_prev, __var3_lval_type_byte_word_dword_or_vm_displacement, a3) )
          {
            _index = _prev;
            _struct_prev_prev = getStruct_1(*(a3 - 4), _prev - 1);
            _struct_prev = getStruct_1(*(a3 - 4), _prev);
            if ( CHECK_LODS_PUSH_CONTEXT(_struct_prev_prev, __var3_lval_type_byte_word_dword_or_vm_displacement) )
            {
            --_cur;
            if ( CHECK_POP_ESP(_struct_prev_prev) )
                return _ret;
            _prev__ = _cur;
            _ret = 1;
            }
            else
            {
            _prev = *v3 - 1;
            if ( Vmp_Fixed(&_prev, __var3_lval_type_byte_word_dword_or_vm_displacement, a3) )
            {
                *v3 = _prev;
                _prev = _prev;
                _ret = 1;
            }
            }
          }
      }
      if ( _ret != 0 && rand()%2 == 1 )
      {
          _prev_index = _retn_index(_struct_prev);
          _cur_index = _retn_index(_cur);
          _flag = _prev - _prev_index == _prev_index - _cur_index;// len
          if ( _prev__ - _prev_index == _prev_index - _cur_index && _cur_index - 1 >= _prev_index )
          {
            int dis = _cur_index - _prev_index;
            while ( 1 )
            {
            _success = sub_48AF88(_prev_index, _prev_index + _prev__ - _prev_index, v13, a3);// 直到找到不同的
            v13 = v16;
            if ( !v15 )
                break;
            ++v38;
            if ( !--v14 )
                goto LABEL_45;
            }
            _flag = 0;
          }
LABEL_45:
          if ( _flag != 0 )
          {
            _struct_prev = getStruct_1(*(a3 - 4), _prev_index);
            _struct_prev->_var1_vm_interpreter_mnemonic = 1;
            _struct_prev->_var2_addressing_mode = 2;
            _struct_prev->_reg_index = 4;
            _struct_prev->_var3_lval_type_byte_word_dword_or_vm_displacement = 2;
            _struct_prev->_other |= 4u;
            _struct_cur = getStruct_1(*(a3 - 4), _prev_index + 1);
            _struct_cur->_var1_vm_interpreter_mnemonic = 1;
            _struct_cur->_var2_addressing_mode = 3;
            _struct_cur->_var4_index = 3;
            _struct_cur->_var3_lval_type_byte_word_dword_or_vm_displacement = __var3_lval_type_byte_word_dword_or_vm_displacement;
            _struct_cur->_other |= 4u;
            if ( _prev_index + 2 <= _cur_index - 1 )
            {
            v19 = _prev_index + 2 - _cur_index;
            _prev_index = _cur_index - 1;
            do
            {
                (*(**(a3 - 4) + 12))(*(a3 - 4), v39--);
                ++v19;
            }
            while ( v19 );
            }
          }
          else
          {
            if ( _cur_index - 1 >= _prev_index )
            {
            v20 = _cur_index - _prev_index;
            v40 = _prev_index;
            do
            {
                v21 = getStruct_1(*(a3 - 4), v40);
                v21->_other |= 4u;
                ++v40;
                --v20;
            }
            while ( v20 );
            }
            if ( _cur_index - 1 >= _prev_index )
            {
            v22 = _cur_index - _prev_index;
            v41 = _prev_index;
            do
            {
                sub_47FB0C(*(a3 - 4), v41, v41 + _prev__ - _prev_index);//exchange
                ++v41;
                --v22;
            }
            while ( v22 );
            }
          }
      }
      }
      if ( _ret )
      _ret = (_cur->_other & 1) == 0;
    }
}
return _ret;
}当我看到这里的时候,真的,如果不是语言不在同一个频道,可能就膜拜了。
随意的,举个例子。这里可能会需要一些数据结构的知识
注意是倒叙进行判断的

记当前结构为一个_cur_node
记当前上一个节点为_prev_node
记当前下一个节点为_next_node

假定
_cur_node: LODS BYTE PTR DS:POP DWORD PTR DS:
_next_node: LODS BYTE PTR DS: PUSH DWORD PTR DS:这两条在操作什么,很明显,可以看出vmp不是没有一些组合的
那么可能会出现下面的情况
_next_node变成PUSH ESP
new node,生成一个新的节点,记为_tmp_node
_tmp_node: POP EAX PUSH DWORD PTR ES:之后在插入
Before:
LODS BYTE PTRDS:POP DWORD PTR DS:
LODS BYTE PTR DS:PUSH DWORD PTR DS: After:
LODS BYTE PTRDS:POP DWORD PTR DS:
PUSH ESP
POP EAX PUSH DWORDPTR ES:
注意到eax一致,然后明白这个函数了吗?
好像就这些了,我大概想阐述的东西,就这些。
0x11 说下我还原的思路
在我逆的过程中,一直在猜想vmp作者的构造思路。
最开始的时候,我的想法是把esi看成vmp_encodes,那么asm有套opcode的构造手册,vmp作者也应该有一套这样的手册,当然也不是说像intel手册那种。但我一直从各个角度去思考,都想不出,vmp作者是怎么构造出这些精彩的东西的。有兴趣的,可以尝试一下,如果是大一大二,我可能就这样继续下去了
之后我想既然知道了规则,可能不同版本,会有一些变化,但大致框架如此。
而不同的是,我所想的是,是还原为encodes,而不是asm,这点也重要,因为即使trace,或者使用插件,是的,你人工可能可以识别,那么代码认识么,当然可以写成分析树或写个虚拟引擎自己跑,但我想,这也是vmp作者愿意看到的情况
那么既然想还原成encodes,先解析esi,之后只要规则到位,那么我们可以得到disp,imm,sib,modrm,prefix。还需要确认的,就只剩下opcode了,所幸的是,相同mnemonic所对应的opcode并不多,就大致解决了类型不对等的情况了。

2402436533 发表于 2018-4-15 00:02:56

感谢,羡慕在学校就已这么厉害了
ps:另外冒昧能问一下在哪所大学 ?这么厉害 。

d8ln3x 发表于 2018-4-16 00:50:45

膜拜大佬。。。。

cnfeitong 发表于 2018-4-21 13:51:38

又免费学了一课,也感谢楼主费心写这么多话出来

pcb_86 发表于 2018-4-21 14:15:50

很棒,谢谢分享

langhua425 发表于 2018-4-23 20:14:51

学习下,表示没看懂

BinCrack 发表于 2018-5-2 11:02:06

菜鸟表示看不懂,依然感谢

killer5 发表于 2018-5-8 18:28:34

非常感谢分享,给力

小韩 发表于 2018-5-8 20:30:44

膜拜下大牛 支持!

cxj98 发表于 2018-5-8 22:26:28

这应该是博士生科研用的,我们幼儿园小朋友完全看不懂。
页: [1] 2 3 4 5 6 7 8
查看完整版本: 谈谈vmp的还原(3)