经过对PowerShield1.0简易版用各种混淆复杂度进行研究,总结两点:
1. PowerShield混淆和加密对软件的保护微乎其微;因为基于东躲西藏的思路,不是可靠的加密方式;
2. 经过一周的仔细分析和研究,包括在pbdecomplier的增加对反混淆的编程时发现,PowerShield是很容易去掉冗余的跳转指令,并归并和整理还原成普通编译后的代码形式,从而再用以前开发的反编译器就能轻松反编译它。
下面简单介绍一下反混淆原理,过多的细节不便说,因为如果没有反编译的基础,光靠能把跳转去掉,要处理并还原成源码还是不容易的。
1. 原则:再怎么混淆,它无法打破正常编译后的p-code顺序,这是铁定的事实。
2. 混淆原理:去掉p-code行数参数,致使pbkiller崩溃,因为pbkiller依靠这个参数(更正:依靠debug参数来参与还原一些行的布局,因为debug参数与源码的留行是一致的。pbkiller并不依靠这个参数来断行)
3. 设置行计数器,每行代码拼接后在插入到vector时,给一个行号;行号不重复;
4. LJP,逻辑判断跳转处记录下跳转地址,等第一个分支分析完后,再分析这些分支;最后得到完整的代码。当然因为欺骗跳转的原因,得到的代码是错乱的。
5. 在每一行内,欺骗跳转只做转移,不参与任何逻辑跳转,直到断行后;
6. 在行与行之间的欺骗跳转,不可以直接忽略,因为它有可能影响到逻辑走向,因为原始的pbd中也会有很多跳转。欺骗跳转与正常跳转混杂在一起,或者欺骗代码用数个跳转,代替原本的一个跳转,我想的办法是压缩跳转,取得其首地址和跳转的最终目标地址二个参数,作为后续分析跳转用。
7. 在4步得到的结果如下:
//local variables list qty = 4
//param: string commandline
boolean l_bb
long ll_xx
long ll_yy
00000000:(00010000/00000001) //expression lines: 0
//Confused expression by PowerShield!
0000FFFF:(00010000/00000001) _._._.J.X.F: 43C8FFFF/00000000: //vcJP010203_3900 insert
43C8FFFF:(00010000/00000002) L._._.J.F.F: 212EFFFF/00000000: l_bb = true
1AACFFFF:(00010000/00000003) _._._.J.X.F: 4406FFFF/00000000: //vcJP010203_3900 insert
4406FFFF:(00010000/00000004) ll_xx = 3
216EFFFF:(00010000/00000005) _._._.J.X.F: 1612FFFF/00000000: //vcJP010203_3900 insert
1612FFFF:(00010000/00000006) ll_xx = 4
21C0FFFF:(00010000/00000007) _._._.J.X.F: 407EFFFF/00000000: //vcJP010203_3900 insert
407EFFFF:(00010000/00000008) L._._.J.F.F: 41F4FFFF/00000000: l_bb = true
3D62FFFF:(00010000/00000009) _._._.J.X.F: 1B04FFFF/00000000: //vcJP010203_3900 insert
1B04FFFF:(00010000/0000000A) ll_xx = 7
3310FFFF:(00010000/0000000B) _._._.J.X.F: 305CFFFF/00000000: //vcJP010203_3900 insert
305CFFFF:(00010000/0000000C) ll_xx = 8
0A18FFFF:(00010000/0000000D) _._._.J.X.F: 45E8FFFF/00000000:
41F4FFFF:(00000008/0000000E) ll_yy = 9
0562FFFF:(00000008/0000000F) _._._.J.X.F: 164AFFFF/00000000: //vcJP010203_3900 insert
164AFFFF:(00000008/00000010) ll_xx = 10
0442FFFF:(00000008/00000011) _._._.J.X.F: 45E8FFFF/00000000:
212EFFFF:(00000002/00000012) ll_yy = 5
2252FFFF:(00000002/00000013) _._._.J.X.F: 3CAAFFFF/00000000: //vcJP010203_3900 insert
3CAAFFFF:(00000002/00000014) ll_xx = 6
131AFFFF:(00000002/00000015) _._._.J.X.B: 407EFFFF/00000000: //vcJP010203_3900 insert
45E8FFFF:(FFFFFFFF/FFFFFFFF) __endof__
那接下来就是按视觉顺序和逻辑顺序进行整理排序:
//local variables list qty = 4
//param: string commandline
boolean l_bb
long ll_xx
long ll_yy
00000000:(00010000/00000001) //expression lines: 0
//Confused expression by PowerShield!
43C8FFFF:(00010000/00000002) L._._.J.F.F: 212EFFFF/00000000: l_bb = true
4406FFFF:(00010000/00000004) ll_xx = 3
1612FFFF:(00010000/00000006) ll_xx = 4
21C0FFFF:(00010000/00000007) _._._.J.X.F: 407EFFFF/00000000: //vcJP010203_3900 insert
212EFFFF:(00000002/00000012) ll_yy = 5
3CAAFFFF:(00000002/00000014) ll_xx = 6
407EFFFF:(00010000/00000008) L._._.J.F.F: 41F4FFFF/00000000: l_bb = true
1B04FFFF:(00010000/0000000A) ll_xx = 7
305CFFFF:(00010000/0000000C) ll_xx = 8
0A18FFFF:(00010000/0000000D) _._._.J.X.F: 45E8FFFF/00000000:
41F4FFFF:(00000008/0000000E) ll_yy = 9
164AFFFF:(00000008/00000010) ll_xx = 10
45E8FFFF:(FFFFFFFF/FFFFFFFF) __endof__
很快的半个小时编程就弄好了,所以我增补上来:
//local variables list qty = 4
//param: string commandline
boolean l_bb
long ll_xx
long ll_yy
//expression lines: 0
//Confused expression by PowerShield!
if l_bb = true then
ll_xx = 3
ll_xx = 4
else
ll_yy = 5
ll_xx = 6
end if
if l_bb = true then
ll_xx = 7
ll_xx = 8
else
ll_yy = 9
ll_xx = 10
end if
8. 还原成正常的代码顺序后,再使用反编译器的statement还原程序进行多种结构体的判断(这个我前期已经弄好,而且测试过大量代码无错误),逐步恢复
包括try。。。catch;for。。。next,do。。。while;if。。。else。。。end if等。上次我看到一个人说要写一个反混淆器,但是他贴一个界面出来,刻意模仿pbkiller完全一样,甚至菜单上的图标都一样(搞笑),他的目标是去除混淆代码,但是如果混淆层次和复杂度比较高,代码冗余非常大,通常没有反编译器很难完成任务。而且不靠反编译器难于还原statement,也就是不可阅读。
我已经经过一周的分析和编程,反复修改程序,目前刚刚可以接近未混淆之前的代码了,但是还限于一个if。。。else。。。end if结构体,其他结构尚未细调。
//晚上熬夜了,终于完成90%的任务,只有两个字的感受,完美!
代码如下,for。。。next还有点问题:
******************************************************************
* <2>public open ( string commandline) 0000CBCF *
******************************************************************
//local variables list qty = 5
//param: string commandline
boolean l_bb
long ll_xx
long ll_yy
//expression lines: 0
//Confused expression by PowerShield!
ll_xx = 100
if l_bb = true then
ll_xx = 101
ll_xx = 102
else
ll_yy = 103
ll_xx = 104
end if
if l_bb = true then
ll_xx = 105
ll_xx = 106
else
ll_yy = 107
ll_xx = 108
end if
ll_xx = 110
if l_bb = true then
ll_xx = 1
if l_bb = true then
ll_xx = 2
if l_bb = true then
ll_xx = 3
ll_xx = 4
else
ll_yy = 5
ll_xx = 6
end if
ll_xx = 7
else
ll_yy = 8
ll_xx = 9
end if
ll_xx = 10
else
ll_yy = 11
ll_xx = 12
end if
do while ll_xx > 0
ll_xx = 21
ll_xx = 22
ll_xx = 23
loop
do
ll_xx = 24
ll_xx = 25
ll_xx = 26
loop while ll_xx > 0
ll_xx = 0
do while 10000 >= ll_xx
ll_xx = 1
ll_xx = 2
ll_xx = 3
ll_xx ++
loop
choose case ll_xx
case 1
ll_xx = 111
ll_xx = 111
goto: Label_003DFFFF
ll_xx = 222
ll_xx = 222
case 2
case 3
ll_xx = 333
ll_xx = 333
end choose
但是话又说回来,简单的statement还不要怎么用电脑来算,眼睛就能看出来。但是实际上的程序或者商业软件的编写者总不是傻子,而且对反编译器和混淆器都有相当的研究,也就是它会反复地进行测试,直到市面上的反编译器对它无法造成伤害的前提下他才会发布商业软件。在冗余度很大或者很变态的写法时,还是很难搞清楚他的statement的,所以总结一句话就是:除非你有非常好的归纳能力,能够把各种statement的特点归纳出来,否则类似各种statement相互嵌套和混杂的写法可能是很难还原的,甚至直接用goto写法。如果goto没法还原成正常的statement,那感觉上是反编译出错了,反编译者肯定也难以相信代码的准确性。
写商业代码的人都知道把一些全局变量或者许多控制参数放入注册码验证的流程中,而且在许多地方进行校验,并且有联合注册表,sql库,程序三方联合作用对抗反编译的。所以除非反编译回来的代码跟源代码一致,所以很难保证能达到破解作用。很简单的做法,如把文件的md5码送入dw,或者送入sql后台,后台再进一步作用,如此种种极尽所能的做法。都对软件保护有效果。在注册部分被打上印记的全局参数也会在各个界面用作参数,或者进一步设置到其他参数上去。。
多层嵌套去掉假跳转后的代码如下:
0000FFFF:(00000000/00000001) _._._.J.X.F: B2FAFFFF/00000000: //vcJP0102033900INSERT
B2FAFFFF:(00000000/00000002) ll_xx = 1
3420FFFF:(00000000/00000003) _._._.J.X.F: 94CEFFFF/00000000: //vcJP0102033900INSERT
94CEFFFF:(00000000/00000004) L._._.J.F.F: FCC6FFFF/00000000: 2 >= ll_xx
EEFCFFFF:(00000000/00000005) _._._.J.X.F: A31EFFFF/00000000: //vcJP0102033900INSERT
A31EFFFF:(00000000/00000006) ll_xx = 3
A758FFFF:(00000000/00000007) _._._.J.X.F: 6304FFFF/00000000: //vcJP0102033900INSERT
6304FFFF:(00000000/00000008) ll_xx = 4
5B7CFFFF:(00000000/00000009) _._._.J.X.F: 613AFFFF/00000000: //vcJP0102033900INSERT
613AFFFF:(00000000/0000000A) L._._.J.F.F: E756FFFF/00000000: ll_xx = 5
CB60FFFF:(00000000/0000000B) _._._.J.X.F: 9F30FFFF/00000000: //vcJP0102033900INSERT
9F30FFFF:(00000000/0000000C) ll_xx = 6
B0B8FFFF:(00000000/0000000D) _._._.J.X.F: 75B2FFFF/00000000: //vcJP0102033900INSERT
75B2FFFF:(00000000/0000000E) ll_xx = 7
D2C2FFFF:(00000000/0000000F) _._._.J.X.F: A85CFFFF/00000000: //vcJP0102033900INSERT
A85CFFFF:(00000000/00000010) _.C._._._._: case135 = ll_xx
2302FFFF:(00000000/00000011) _._._.J.X.F: B780FFFF/00000000: //vcJP0102033900INSERT
B780FFFF:(00000000/00000012) L.C._.J.F.F: 0D3EFFFF/00000000: 901 = case135
0F58FFFF:(00000000/00000013) _._._.J.X.F: A590FFFF/00000000: //vcJP0102033900INSERT
A590FFFF:(00000000/00000014) ll_xx = 8
48FCFFFF:(00000000/00000015) _._._.J.X.F: B484FFFF/00000000: //vcJP0102033900INSERT
B484FFFF:(00000000/00000016) ll_xx = 9
D416FFFF:(00000000/00000017) _._._.J.X.F: 7BB2FFFF/00000000: //vcJP0102033900INSERT
7BB2FFFF:(00000000/00000018) L._._.J.F.F: 420CFFFF/00000000: ll_xx = 10
51F4FFFF:(00000000/00000019) _._._.J.X.F: D580FFFF/00000000: //vcJP0102033900INSERT
D580FFFF:(00000000/0000001A) ll_xx = 11
CE5CFFFF:(00000000/0000001B) _._._.J.X.F: 3BFCFFFF/00000000: //vcJP0102033900INSERT
3BFCFFFF:(00000000/0000001C) ll_xx = 12
F92CFFFF:(00000000/0000001D) _._._.J.X.F: B7C8FFFF/00000000: //vcJP0102033900INSERT
B7C8FFFF:(00000000/0000001E) L._._.J.F.F: 6BFEFFFF/00000000: ll_xx = 13
0736FFFF:(0000001E/0000001F) _._._.J.X.F: 420CFFFF/00000000: //vcJP0102033900INSERT
6BFEFFFF:(0000001E/00000020) ll_xx = 14
E608FFFF:(0000001E/00000021) _._._.J.X.F: 753EFFFF/00000000: //vcJP0102033900INSERT
753EFFFF:(0000001E/00000022) ll_xx = 15
5472FFFF:(0000001E/00000023) _._._.J.X.F: 7BB2FFFF/00000000: //vcJP0102033900INSERT
420CFFFF:(00000018/00000024) ll_xx = 16
5AC6FFFF:(00000018/00000025) _._._.J.X.F: D90AFFFF/00000000: //vcJP0102033900INSERT
D90AFFFF:(00000018/00000026) ll_xx = 17
BE6EFFFF:(00000018/00000027) _._._.J.X.F: F054FFFF/00000000: //vcJP0102033900INSERT
F054FFFF:(00000018/00000028) ll_xx = 49
19C4FFFF:(00000018/00000029) _._._.J.X.F: 4094FFFF/00000000: //vcJP0102033900INSERT
4094FFFF:(00000018/0000002A) ll_xx = 50
E76EFFFF:(00000012/0000002B) _._._.J.X.F: E756FFFF/00000000: //vcJP0102033900INSERT
0D3EFFFF:(00000012/0000002C) L.C._.J.F.F: EA46FFFF/00000000: 902 = case135
5D8AFFFF:(00000012/0000002D) _._._.J.X.F: AA0AFFFF/00000000: //vcJP0102033900INSERT
AA0AFFFF:(00000012/0000002E) ll_xx = 18
5708FFFF:(00000012/0000002F) _._._.J.X.F: B926FFFF/00000000: //vcJP0102033900INSERT
B926FFFF:(00000012/00000030) ll_xx = 19
D5F6FFFF:(00000012/00000031) _._._.J.X.F: E4E4FFFF/00000000: //vcJP0102033900INSERT
E4E4FFFF:(00000012/00000032) ll_xx = 20
60B0FFFF:(00000012/00000033) _._._.J.X.F: E950FFFF/00000000: //vcJP0102033900INSERT
E950FFFF:(00000012/00000034) ll_xx = 21
70FCFFFF:(00000012/00000035) _._._.J.X.F: 3798FFFF/00000000: //vcJP0102033900INSERT
3798FFFF:(00000012/00000036) L._._.J.F.F: 9C82FFFF/00000000: ll_xx = 22
3440FFFF:(00000012/00000037) _._._.J.X.F: 4DBAFFFF/00000000: //vcJP0102033900INSERT
4DBAFFFF:(00000012/00000038) ll_xx = 24
2F9AFFFF:(00000012/00000039) _._._.J.X.F: 1BEEFFFF/00000000: //vcJP0102033900INSERT
1BEEFFFF:(00000012/0000003A) ll_xx = 25
B9FCFFFF:(00000012/0000003B) _._._.J.X.F: F054FFFF/00000000: //vcJP0102033900INSERT
9C82FFFF:(00000036/0000003C) L._._.J.T.F: E4E4FFFF/00000000: ll_xx = 23
AF72FFFF:(00000036/0000003D) _._._.J.X.F: 4DBAFFFF/00000000: //vcJP0102033900INSERT
EA46FFFF:(0000002C/0000003E) L.C._.J.F.F: F054FFFF/00000000: 903 = case135
84D8FFFF:(0000002C/0000003F) _._._.J.X.F: D6A0FFFF/00000000: //vcJP0102033900INSERT
D6A0FFFF:(0000002C/00000040) ll_xx = 26
C350FFFF:(0000002C/00000041) _._._.J.X.F: 1E76FFFF/00000000: //vcJP0102033900INSERT
1E76FFFF:(0000002C/00000042) ll_xx = 27
C428FFFF:(0000002C/00000043) _._._.J.X.F: CD48FFFF/00000000: //vcJP0102033900INSERT
CD48FFFF:(0000002C/00000044) L._._.J.F.F: 4688FFFF/00000000: ll_xx = 28
851CFFFF:(0000002C/00000045) _._._.J.X.F: 603CFFFF/00000000: //vcJP0102033900INSERT
603CFFFF:(0000002C/00000046) ll_xx = 29
1BC0FFFF:(0000002C/00000047) _._._.J.X.F: F4CAFFFF/00000000: //vcJP0102033900INSERT
F4CAFFFF:(0000002C/00000048) ll_xx = 30
850CFFFF:(0000002C/00000049) _._._.J.X.F: E126FFFF/00000000: //vcJP0102033900INSERT
E126FFFF:(0000002C/0000004A) L._._.J.F.F: 5758FFFF/00000000: ll_xx = 31
D03AFFFF:(0000002C/0000004B) _._._.J.X.F: E82EFFFF/00000000: //vcJP0102033900INSERT
E82EFFFF:(0000002C/0000004C) ll_xx = 32
2900FFFF:(0000002C/0000004D) _._._.J.X.F: 9866FFFF/00000000: //vcJP0102033900INSERT
9866FFFF:(0000002C/0000004E) ll_xx = 33
6DCEFFFF:(0000002C/0000004F) _._._.J.X.F: 9D0AFFFF/00000000: //vcJP0102033900INSERT
9D0AFFFF:(0000002C/00000050) L._._.J.F.F: D532FFFF/00000000: ll_xx = 34
4D9EFFFF:(0000002C/00000051) _._._.J.X.F: AF92FFFF/00000000: //vcJP0102033900INSERT
AF92FFFF:(0000002C/00000052) ll_xx = 35
55A6FFFF:(0000002C/00000053) _._._.J.X.F: 3AA8FFFF/00000000: //vcJP0102033900INSERT
3AA8FFFF:(0000002C/00000054) ll_xx = 36
DAC6FFFF:(0000002C/00000055) _._._.J.X.F: 298EFFFF/00000000: //vcJP0102033900INSERT
298EFFFF:(0000002C/00000056) ll_xx = 39
0E4AFFFF:(0000002C/00000057) _._._.J.X.F: ACCEFFFF/00000000: //vcJP0102033900INSERT
ACCEFFFF:(0000002C/00000058) ll_xx = 40
1120FFFF:(0000002C/00000059) _._._.J.X.F: 3B4CFFFF/00000000: //vcJP0102033900INSERT
3B4CFFFF:(0000002C/0000005A) ll_xx = 43
4DF4FFFF:(0000002C/0000005B) _._._.J.X.F: 9EDAFFFF/00000000: //vcJP0102033900INSERT
9EDAFFFF:(0000002C/0000005C) ll_xx = 44
D7CCFFFF:(0000002C/0000005D) _._._.J.X.F: 15A0FFFF/00000000: //vcJP0102033900INSERT
15A0FFFF:(0000002C/0000005E) ll_xx = 47
8CE8FFFF:(0000002C/0000005F) _._._.J.X.F: FA9EFFFF/00000000: //vcJP0102033900INSERT
FA9EFFFF:(0000002C/00000060) ll_xx = 48
8768FFFF:(00000050/00000061) _._._.J.X.F: F054FFFF/00000000: //vcJP0102033900INSERT
D532FFFF:(00000050/00000062) ll_yy = 37
9CD0FFFF:(00000050/00000063) _._._.J.X.F: AB88FFFF/00000000: //vcJP0102033900INSERT
AB88FFFF:(00000050/00000064) ll_xx = 38
A2A0FFFF:(00000050/00000065) _._._.J.X.F: 298EFFFF/00000000: //vcJP0102033900INSERT
5758FFFF:(0000004A/00000066) ll_yy = 41
80A2FFFF:(0000004A/00000067) _._._.J.X.F: 3F96FFFF/00000000: //vcJP0102033900INSERT
3F96FFFF:(0000004A/00000068) ll_xx = 42
9E8EFFFF:(0000004A/00000069) _._._.J.X.F: 3B4CFFFF/00000000: //vcJP0102033900INSERT
4688FFFF:(00000044/0000006A) ll_yy = 45
07E2FFFF:(00000044/0000006B) _._._.J.X.F: B07CFFFF/00000000: //vcJP0102033900INSERT
B07CFFFF:(00000044/0000006C) ll_xx = 46
0BB2FFFF:(00000044/0000006D) _._._.J.X.F: 15A0FFFF/00000000: //vcJP0102033900INSERT
E756FFFF:(0000000A/0000006E) ll_xx = 51
7902FFFF:(0000000A/0000006F) _._._.J.X.F: 8DC8FFFF/00000000: //vcJP0102033900INSERT
8DC8FFFF:(0000000A/00000070) ll_xx = 52
FAE8FFFF:(0000000A/00000071) _._._.J.X.F: 8108FFFF/00000000: //vcJP0102033900INSERT
8108FFFF:(0000000A/00000072) ll_xx ++
569CFFFF:(0000000A/00000073) _._._.J.X.F: 94CEFFFF/00000000: //vcJP0102033900INSERT
FCC6FFFF:(FFFFFFFF/FFFFFFFF) __endof__
想把这个代码恢复我用了三天也还没眉目,因为实际上的写法可能还更复杂,嵌套更多层。用尽各种变态的写法和冗余的写法。还是比较难的。
//软件保护和加密从来都是矛盾相生的两个对立面。在此提醒更多pb用户,混淆器的实质就是这么回事。在此之后,我已经总结30多个想法意图再弄弄混淆器。很多方面,反编译器都需要依赖pbd中的没有在编译时抹掉的文字等信息。这些信息尚未有工具去除。另外我目前了解如果要做到终极加密,只有改造vm内部对文件的读取和解析的过程,修改vm参与到对vm的解析过程,比如可以把pbd的文件格式给改掉,这应该是一个终极的加密思路。而反编译时针对这样的加密方式是无法成功的。因为pbd已经跟随修改过的vm进行了匹配,用反编译器的一些规则已经无法分析了。
前几天问到好几个人,用11写程序,都是未有任何保护措施。。所以目前急忙赶完反编译器,下一步着手混淆器。参考java和.net的混淆器,大凡只做到变量名,函数名,对象属性和函数名,流程(statement),数字和文字加密等几种实现。具体还有没有突破现在还不知道。