谈谈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并不多,就大致解决了类型不对等的情况了。 感谢,羡慕在学校就已这么厉害了
ps:另外冒昧能问一下在哪所大学 ?这么厉害 。 膜拜大佬。。。。 又免费学了一课,也感谢楼主费心写这么多话出来 很棒,谢谢分享 学习下,表示没看懂 菜鸟表示看不懂,依然感谢 非常感谢分享,给力 膜拜下大牛 支持! 这应该是博士生科研用的,我们幼儿园小朋友完全看不懂。