欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

VMP 之反 VM (还原代码) 之初初...

VMP 之反 VM (还原代码) 之初初...

原先是在这版某讨论帖写的. 之前这版块管制.
稍稍整理独立出来. 己看过的就飘过吧.
讲一下 VMP, 及还原代码的可行性.
VMP其实不复杂, 反而是他的太单纯导致复杂化
万用闸有二种, 一种叫 NOR Gate, 一种叫 NAND Gate.
即称万用, 就表示单靠他自己, 就能变换出 OR ' NOT ' AND ' XOR
VMP在VM内的运算选择了 NOR Gate 去实现 NOT'AND'OR'XOR
(实现图我列在最后)
致于加法, VMP内部自有这种指令(直接用 intel ADD 指令)
add(w,w) EFL,Result
add(d,d) ELF,Result
addb(wb,wb)  ELF,Result
VMP内部也有 shl(d,w) EFL,Result 及 FS:[d0]:= d1
等等一些较为有目的性的指令.
所以还原成可视(或称类asm)个人认为并没想像中的难.
-----------------------------------------------------------------------------
VM 的条件跳转
条件跳转跟我们平常的观念不太一样.
这是 jmp : #delta=0
          vm.jmp(d+#delta)  ;ESI=[EBP]+[EBP+4]
上面的#delta的值将决定所谓的跳或不跳.
通常 d=Target, #delta=0 为跳   jmp(d+#d)
若 #delta=-d 则 ESI 的值不会变, 也就是不跳(继续执行)的意思了.
-----------------------------------------------------------------------------
为何说没想像中的那么难呢?
下列是很常见的一连3个VM指令
原 VM.ESP:    *** 注: VM.ESP 是由 EBP 所表示 ***
   12FFB0  00000286
   12FFB4  0012FFB8
1.经过 VM.push_esp , 变成:
   12FFAC  0012FFB0
   12FFB0  00000286
   12FFB4  0012FFB8
2.再经过 VM.pushd([d]) , 变成:
   12FFAC  00000286  <- 复制一份了
   12FFB0  00000286
   12FFB4  0012FFB8
3.再经过 VM.nor(d,d) EFL,Result  变成:    注: nor is NOR 闸
   12FFAC  00000282 <- ELF
   12FFB0  FFFFFD79 <- Result
   12FFB4  0012FFB8
这3个步骤其实就是 NOT 0286h = FFFFFD79


紧接着上列3个步骤后, 常见下列的动作:
4. VM.pop  丢掉ELF, 变成:
   12FFB0  FFFFFD79 <- Result
   12FFB4  0012FFB8
5. pushd(decode(followWord)) <- 从opcode处取2 byte当数据, 符号扩展PUSHD
   12FFAC  FFFFF7EA <- 数据 (亦即 0815的not)
   12FFB0  FFFFFD79 <- Result
   12FFB4  0012FFB8
6. VM.nor(d,d) EFL,Result  变成:
   12FFAC  00000202 <- ELF
   12FFB0  00000004 <- Result
   12FFB4  0012FFB8
7. VM.pop  丢掉ELF, 变成:
   12FFB0  00000004 <- Result
   12FFB4  0012FFB8
1~7 其实实现的就是 00000286 & 00000815 = 00000004
(00000815被以NOT的型式硬编在OPCode)

所以 VMP 的 VM 是很正规的.
一个简单的80x86指令 :  and reg, 00000815h
被分解成 (假设reg的值已入Stack)
         VM.push_esp
         VM.pushd([d])
         VM.nor(d,d)
         VM.pop
         VM.pushd(decode(followWord))
         VM.nor(d,d)
         VM.pop
因为都是通过 VM.Execute 在分派的, 过于勇往直前的人就感觉 VM 一直在绕圈圈
-----------------------------------------------------------------------------
可以写个分析引擎, 全在 VM.Execute 这点分析
根据VM.Stack内容及ESI的变化, 应该可分析出90% 的接口入点 及 功能
(有些接口到底是什么功能得用套的)
当然了, 前提是 VM 有那些接口功能要知道
这可以算是我第一次看VMP, 现在的火热觉得不分析一下好像跟不上流行
我只看了半天就分析出大多数常用的接口功能
可见人眼对这种花指令'垃圾代码的适应性很强
从 EP 到 MessageBox('debugger has been found'), 下列是绰绰有余了(用不到那么多)
可见得大多功能的实现都只依赖固定几种在组合变化而己(配合上面我写的NOR闸的变化)

  popd
  pushd
  push_esp
  pushw(decode(flwb))
  pushw(decode(flww))
  pushd(decode(flwb))
  pushd(decode(flww))
  pushd(decode(flwd))
  pushw(b[vm.decode(flwb)])
  nor(d,d) EFL,Result
  nor(bw,bw)   ELF,wResult
  add(d,d) ELF,Result
  shr(d,bw) EFL,Result
  shl(d,w) EFL,Result
  add(d,d) ELF,Result
  addb(wb,wb)  ELF,wResult
  pushw(b[d])
  pushw(b(SS:[d]))
  vm.decode(flwb):=bw
  pushd([d])
  SetFlag(d)
  pushw(w(SS:[d]))
  pushw(nor(w,w))  EFL,Result
  add(w,w) EFL,Result
  [vm.decode(followByte)]:= w
  d:=[d]
  [d0]:=d1
  pushd(FS:[d])
  FS:[d0]:= d1
  SS:[d0]:= d1
  SS:[d]
  NewVM
  VM.jmp(d0 + #d1)
  VM.Exit
注 :
    flwb : follow Byte
    flww : follow Word
    flwd : follow DWord
至于popd及pushd到底是作用于VM.Context的那一个, 我写了个脚本可跑出来(AL在决定的)
所以可正确翻译 push xx , push xx
所以应该大部份都可翻译出来.
例如之前那 7 条 VM 指令, 可直接翻成 and xxx, 00000815(硬编)
至于 xxx 到底是 EAX 或 ECX 或 内存...  得再往前看了.
就到这而. 己有利日后我遇到VMP该做什么事 ' 该做什么的逆向.
至于实现的编程不是我的强项了. 事实上是个弱鸡.
有问题不要问我, 因为我才刚接触.
阿门





注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功