今天在研究pbd文件,突然想到pbd文件混淆器。因为还没看过它的原理,但是我已经大致猜测到了他的工作原理。

 

//---------------------------------------------------------------------------------------------------------

 


----------------------------------------------------------------------------------------------------------//

 

比如我们写上一个if then else end if结构。

if 1 <> 1 then
 //这里写这么多只是为了在p-code段有足够多的byte来做混淆。
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
 return "aaaaaaaaaaaaaaa"
else
 return "BBBBBBBBBBBBBBB"
end if

 

我们知道这个表达式恒等于返回"BBBBBBBBBBBBBBB"
但是反编译器有个致命弱点是必须顺序解析每个遇到的码,并把他翻译出来。也就是说是顺序解析的。因为执行码和数据是混合放置的。常规反编译是必须顺序流水解析的,就想asm一样,如果中途你插进一个byte,后续就会错完。

在if 和choose等选择结构的p-code或者机器码表达方式中,仍然是采用jp和jnp指令。也就是比较后跳转。所以这个1<>1恒false,也就是if前半段是永远无法执行的,也就不会引起vm解析错误,因为永远执行不到那里去。但是反编译器就会进入陷阱中而无法自拔。

 

内部表达序是:

取值(1),取值(1),JP(goto address1:)顺序:执行1,执行2,执行3........

address1:顺序:执行a,执行b,执行c........

可以看出,可以在"顺序:执行1,执行2,执行3........"进行大量p-code的伪造,比如goto到一个不存在的地址,或者取一个非法地址的数据,或者伪造不可识别的p-code码等。方法就很简单了。

这样伪造的结果是,程序执行没有任何影响,而反编译器陷入其中而导致异常处理时退出,或者显示一些错误结果,或者空白(出于自身保护)。

 

这当然是个矛盾相生的问题,因为混淆器无法知道你写的程序的本意,它只是一个transfer,所以如果你写:

if a>b then
   ...
else
  ...
end if

它是不可能去伪造的,必须是人为设置,就是说连这段if结构也必须是伪造的。当然反编译器也可以在遇到无法分析时中断上半段的解析,而直接goto到下半段解析。不过如果混淆的程度太高,可能不能够越过。

 

因为操作码有些要读取数据,有的是单码不读取数据,所以如果反编译器仍然采用顺序流水方式,必然无法顺利解析到指定的goto位置,因为伪造的p-code可能在取数据时已经越过了goto到的位置。所以必须把任何跳转都分开解析,一旦一条路出现数个连续错误,如:不可识别操作码或者非法资源地址访问等,即刻终止,并按goto的位置去解析下半段。在跳与不跳之间或者多个goto下,必然有一个是正确的出口,否则程序就不会顺利执行的(这是一个原则),只是最后的结果会出现很多goto标签而已,但是可以认为,程序是可读的。