偶然看到以前的技术笔记,有段 vbs 加解密代码挺有意思的,特来分享一下。

常见保护代码方式

恶作剧版

众所周知,像vbs、bat等脚本都是明文,比如一般情况下,不想让其他人运行脚本,会在主要代码运行前,提示输入密码,eg:

1
2
3
4
5
6
7
8
do
key = inputbox("输入密码","输入密码")
if key <>123 then
msgbox "密码错误"
else
exit do
loop
`真正要执行的代码

编码版

但这种情况,直接右键,就能查看到密码明文,或者直接可以将要执行的代码提取出来就可以了。于是又想出将主要代码编码,运行时候再解码运行,eg:

1
2
3
4
5
6
7
8
9
data="68,105,109,32,102,115,111,13,10,83,101,116,32,102,115,111,32,61,67,114,101,97,116,101,79,98,106,101,99,116,40,34,83,99,114,105,112,116,105,110,103,46,70,105,108,101,83,121,115,116,101,109,79,98,106,101,99,116,34,41,13,10,102,115,111,46,68,101,108,101,116,101,70,105,108,101,32,87,83,99,114,105,112,116,46,83,99,114,105,112,116,70,117,108,108,78,97,109,101,39,-13890,-19459,-10284,-17222,13,10,109,115,103,98,111,120,34,-11312,-13630,-14357,-17232,-13647,-15958,-12363,113,113,50,52,50,49,52,55,55,51,53,49,34,13,10,67,111,110,115,116,32,115,116,114,80,97,115,115,119,111,114,100,32,61,32,34,115,49,57,57,57,56,49,50,34,39,32,13,10,68,105,109,32,87,115,104,78,101,116,119,111,114,107,13,10,83,101,116,32,87,115,104,78,101,116,119,111,114,107,32,61,32,67,114,101,97,116,101,79,98,106,101,99,116,40,34,87,83,99,114,105,112,116,46,78,101,116,119,111,114,107,34,41,13,10,68,105,109,32,117,115,101,114,78,97,109,101,13,10,117,115,101,114,78,97,109,101,32,61,32,87,115,104,78,101,116,119,111,114,107,46,117,115,101,114,78,97,109,101,38,34,44,117,115,101,114,34,13,10,68,105,109,32,68,111,109,97,105,110,13,10,83,101,116,32,68,111,109,97,105,110,32,61,32,71,101,116,79,98,106,101,99,116,40,34,87,105,110,78,84,58,47,47,46,47,34,38,117,115,101,114,78,97,109,101,41,13,10,68,111,109,97,105,110,46,83,101,116,80,97,115,115,119,111,114,100,32,115,116,114,80,97,115,115,119,111,114,100,13,10,68,111,109,97,105,110,46,83,101,116,73,110,102,111,13,10,100,105,109,32,103,106,13,10,111,110,32,101,114,114,111,114,32,114,101,115,117,109,101,32,110,101,120,116,13,10,100,105,109,32,87,83,72,115,104,101,108,108,65,13,10,115,101,116,32,87,83,72,115,104,101,108,108,65,32,61,32,119,115,99,114,105,112,116,46,99,114,101,97,116,101,111,98,106,101,99,116,40,34,119,115,99,114,105,112,116,46,115,104,101,108,108,34,41,13,10,100,105,109,32,115,32,13,10,100,111,32,117,110,116,105,108,32,115,61,53,48,48,48,48,32,13,10,115,61,115,43,49,32,13,10,109,115,103,98,111,120,32,34,-18201,-15417,-23636,-18184,-12590,-20300,53,48,48,48,48,-19250,-17448,-19531,-20279,-23647,-14425,-12814,-19984,-10536,-14604,-23636,-12590,-13848,-10557,-15925,-15396,-15637,-23636,-10536,-14604,-16691,-16470,-19781,-16470,-17414,-23636,-20300,-12822,-11047,-18184,-15133,-15396,-15637,-24157,34,44,54,52,32,13,10,108,111,111,112,32,13,10,109,115,103,98,111,120,34,-16460,-15133,-10782,-15436,-12095,-16416,-23636,-16691,-18202,-13345,-15133,-15396,-15637,-20279,-23636,115,49,57,57,57,56,49,50,34,13,10"
Function ChrData(Data)
MyArray = Split(Data, ",", -1, 1)
For each OldData in MyArray
Newdata=NewData&chr(OldData)
Next
ChrData=NewData
End Function
execute Chrdata(data)

加壳版

这种加密破解,只需把 execute 改为wscript.echo 或者直接输出到文件。于是乎,又有将vbs外面夹层exe壳,常见的工具有vbs2exe等等,运行原理大概是,将数据空间里的vbs代码写到文件,然后调用wscript 或 cscript 执行输出后的代码。这种加壳方式获取源码也不难,最傻瓜式的就是监听文件写入操作,然后复制写入的文件就可以了。

我的版本

上面是我遇到过的vbs源码保护方法(这是当时的技术,不知道情况咋样)。针对这些情况,当时我想到很有趣的算法,talk is cheap, show me code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
Dim Key
Dim Code
Dim Decode

code = "B0BAA6DBC7C6826D7DA9BD82A7C382AFBADD8686"

Do
key = InputBox("输入密码","输入密码")

If InStr(key,",")<>0 Then
Decode = RoundDecode(code,_
Mid(Key,1,InStr(Key,",")-1),_
Right(Key,Len(Key)-InStrRev(Key,",")))
If IsNull(Decode) <> true Then
execute BDe(Decode)
Exit do
Else
MsgBox "呵呵,没密码就别想运行了"
End If
Else
MsgBox "呵呵,没密码就别想运行了"
End if
Loop



Function RoundDecode(str,key,Dif)
Dim RtStr,ToSubStr
Dim StepC
Dim RDi,RDj

If Dif = "e" Or dif = "E" Then
If Len(str)/4 <> Len(str)\4 Then
StepC = Len(str)\4 + 1
Else
StepC = Len(str)\4
End If
Else
StepC = 3
End If

RDj = 1
For RDi=1 To Len(key)
ToSubStr = Mid(str,(RDi - 1)*2*StepC+1,StepC*2)
RtStr = RtStr & SubDecode(ToSubStr,Asc(Mid(key,RDi,1)))
If Asc(Right(RtStr,1)) <> Asc(Mid(key,RDi,1)) Then
RtStr = Null
Exit For
End If
next

RoundDecode = RtStr
End Function

Function SubDecode(str,KAsc)
Dim SDi
Dim RtStr
Dim NAsc,NCh

NAsc = kasc
RtStr = ""
For SDi = 1 To Len(str) Step 2
NAsc = CLng("&H"&Mid(str,SDi,2)) - NAsc
RtStr = RtStr & Chr(NAsc)
next

SubDecode = RtStr
End Function

Function BDe(str)
Dim stream,xmldom,node
Dim Read

Set xmldom = CreateObject("Microsoft.XMLDOM")
Set node = xmldom.CreateElement("binary")
node.DataType = "bin.base64"
node.text = str

Set stream = CreateObject("adodb.stream")
With stream
.Charset = "gb2312"
.Type = 1
If .State = 0 Then
.Open
End If
.Write node.nodeTypedValue
.Position = 0
.Type = 2
BDe = .ReadText(-1)
.Close
End With

Set stream = Nothing
Set node = Nothing
Set xmldom = nothing
End Function

功能很简单,输入对的密码,就能运行加密的代码,输入错误的密码,则提示密码不正确。

大佬们可以试着破解下,这个是有算法漏洞的,某次和密码学大佬吃巷子面的时候,一语道出算法的漏洞,就当作vbs加密的一个思路吧。后面我会给出解密密钥。

分析

我的思路和 编码版 的思路一样,只是我对编码的时候,增加了一个key值,这个key值也可以用于校验解密的时候,密钥是否正确,由于仅采用一次分组加密,这也是算法设计之初就已知的算法漏洞,导致算法的复杂度并没随key的增长而增加。

加密的过程就是将vbs源码分组,然后使用key对分组源码进行运算,解密是逆过程。

key: NmI73i=,d