穷举破解EXCEL、WORD文档密码
摘要:本文讨论了如何使用vb编程,通过穷举法解除excel文档和word文档的密码。并在破解过程中加入了中断,以方便用户随时中断破解过程。
关键字:穷举法、解密、excel文档、word文档、密码
excel和word提供了多种方法限制访问用户文档,以免未经授权者的查看和更改。但在信息化的今天,用户需要记忆的密码太多,一旦密码丢失,用户将无法打开或访问该文档,给用户造成很大的损失。能否借助计算机的高速运行,解开密码呢?通过尝试,笔者认为:在无法弄清excel和word加密算法的情况下,利用穷举法尝试解密文档,是解密唯一的选择。
1. 实现原理
本程序选用vb6.0编写,并充分利用了office组件中的对象库,穷举尝试各种口令,达到解密文档的目的。
⑴ 巧用整数的取整及取余,产生密码字符串
excel和word文档密码可以是字母、数字、空格以及符号的任意组合,最长可达 15 个字符,且区分大小写。
本程序的破解过程利用一个两层循环,产生选定字符的排列组合(尝试密码),其中外层循环控制密码的位数,内层循环生成n位密码的所有排列组合。产生尝试密码的方法是:将一个n位字符串密码(password)作为一个“数值”,该“数值”每个位上的“数字”属于选定字符范围,且该“数值”与一个整数(x)一一对应,并满足以下条件: 0 ≤x ≤arraylenn-1(arraylen是选定密码字符范围的总字符数,如:仅选定数字时,arraylen=10;仅选定数字和小写字母时,arraylen=10+26=36);对x整除、取余n-1次,对每次的余数y做以下操作:password = password + chararray(y) (注:chararray是存放选定字符的一维数组),最后做以下操作:password = chararray(x mod arraylen) + password,产生的password 就是整数x对应的n位字符串。WwW.11665.coM
⑵ 利用vb的错误处理功能,尝试口令破解
当运行程序尝试一个密码时(用该密码打开文档),若密码错误,则会产生运行错误。为此,必须在尝试口令前,使用on error 语句打开一个错误处理程序;由于本程序是尝试各种口令,当一个口令错误时,直接尝试下一个口令即可,因此,应使用 “on error resume next”语句。
那么,如何得知找到口令了呢? vb有一个内部错误对象err,它的 number 属性中的值是用来确定发生错误的原因。在尝试一个口令后,检查err.number中的值,以确定该口令是否正确。
⑶ 破解过程中的中断
利用穷举法解密对系统资源的占用是十分惊人的,在解密的过程中cpu的利用率几乎是100%,若不加入解密过程中的中断,计算机系统会处于一种假死机状态。为此,在破解过程的内循环中加入了doevents函数。doevents函数提供了一种取消任务的简便方法,它将控制切换到操作环境内核。只要此环境中的所有应用程序都有机会响应待处理事件,应用程序就又恢复控制。使用该函数的优点是:不会使应用程序放弃焦点,且后台事件能够得到有效处理。
2. 具体实现过程
编程实现时,需要机器安装有vb应用程序及microsoft office组件。
⑴ 新建vb工程,并对其初始化
新建一个vb工程,取名get_password,将启动窗体命名为frmmain。首先选择“工程”菜单中的“引用”,在“引用”对话框中选择“microsoft excel10.0 object library”和“microsoft word10.0 object library”(注意:如果安装的是office2000或office97,应该选择excel对象库和word对象库的9.0版或8.0版)。其次在“工程”菜单中“部件”对话框中,选择添加“microsoft windows common controls -2.5(sp2)”和“microsoft common dialog control 6.0”,以便在窗体设计中使用微调控件和对话框控件。
⑵ 在frmmain窗体上添加控件
在frmmain窗体上,按照下图的位置添加表1中的控件,然后根据表1修改每个对象的属性。
表1:
序号 控件名称 控件属性及其属性值
1 frame name=frame1,caption=选择加密文件(*.doc、*.xls)
2 frame name=frame2,caption=选定密码字符范围:
3 frame name=frame3,caption=选择密码的长度:
4 combobow name=combo1
5 commandbutton name=cmdbrowse,caption=浏览
6 commandbutton name=cmdstartcrack,caption=开始破解
7 commandbutton name=cmdquit,caption=退出系统
8 checkbox name=chkdigital,caption=数字(10)
9 checkbox name=chklowercase,caption=小写字母(26)
10 checkbox name=chkuppercase,caption=大写字母(26)
11 checkbox name=chkspace,caption=空格(1)
12 checkbox name=chkbracket,caption=括号(6)
13 checkbox name=chkothers,caption=其他oem字符(26)
14 textbox name=txtpasswordstartlong, text=2
15 textbox name=txtpasswordendlong,text=2
16 textbox name=text1
17 updown name=updown1,buddyproperty=text,wrap=true,increment=1
buddycontrol=txtpasswordstartlong,max=15,min=
18 updown name=updown2,buddyproperty=text,wrap=true,increment=1
buddycontrol=txtpasswordendlong,max=15,min=1
19 commondialog name=dialog,dialogtitle=请选择加密的excel或word文档
filter=excel(*.xls),word(*.doc)|*.xls;*.doc
20 label name=label1, caption=破解进度:
21 label name=label3,caption=从:
22 label name=label5,caption=到:
⑶ 为以上对象编写下列代码
为了便于理解,程序中增加了适当的注释。
option explicit
private sub cmdbrowse_click()
dialog.showopen 'show the dialog
combo1.text = dialog.filename 'set the filename text box to the selected file
combo1.refresh
end sub
private sub cmdquit_click()
end
end sub
private sub cmdstartcrack_click()
static blnprocessing as boolean
dim wd as new word.application, xls as new excel.application
dim openreturn
dim strpath, pass, strtemp, all_char(100) as string
dim j, k, password_start_long, password_end_long, arraylen as integer
dim i, temp as long
arraylen = 0 '数组初始化
if chkdigital.value = 1 then
for j = arraylen to arraylen + 9
all_char(j) = chr(asc("0") + j - arraylen)
next j
arraylen = arraylen + 10
end if
if chklowercase.value = 1 then
for j = arraylen to arraylen + 25
all_char(j) = chr(asc("a") + j - arraylen)
next j
arraylen = arraylen + 26
end if
if chkuppercase.value = 1 then
for j = arraylen to arraylen + 25
all_char(j) = chr(asc("a") + j - arraylen)
next j
arraylen = arraylen + 26
end if
if chkspace.value = 1 then
all_char(arraylen) = " "
arraylen = arraylen + 1
end if
if chkbracket.value = 1 then
all_char(arraylen) = "("
all_char(arraylen+1) = ")"
all_char(arraylen+2) = "{"
all_char(arraylen+3) = "}"
all_char(arraylen+4) = "["
all_char(arraylen+5) = "]"
arraylen = arraylen + 6
end if
if chkothers.value = 1 then
for j = arraylen to arraylen + 6 '33 to 39
all_char(j) = chr(33 + j - arraylen)
next
arraylen = arraylen + 7
for j = arraylen to arraylen + 5 '42 to 47
all_char(j) = chr(42 + j - arraylen)
next j
arraylen = arraylen + 6
for j = arraylen to arraylen + 6 '58 to 64
all_char(j) = chr(58 + j - arraylen)
next j
arraylen = arraylen + 7
all_char(arraylen) = chr(92)
arraylen = arraylen + 1
for j = arraylen to arraylen + 2 '94 to 96
all_char(j) = chr(94 + j - arraylen)
next j
arraylen = arraylen + 3
all_char(arraylen) = chr(124)
all_char(arraylen+1) = chr(126)
arraylen = arraylen + 2
end if
if arraylen = 0 then
msgbox "错误:没有选择'密码使用的字符'", , "请选择密码使用的字符范围..."
exit sub
end if
if blnprocessing then
if msgbox("真的要中断解密过程吗?", vbyesno, "用户中断任务") = vbyes then blnprocessing = false
else
cmdstartcrack.caption = "中断破解"
blnprocessing = true
strpath = combo1.text
if strpath = "" then
msgbox "错误:没有选择'需要解密的文件'", , "请选择需要解密的文件..."
exit sub
end if
strpath = trim(strpath)
password_start_long = val(txtpasswordstartlong.text)
password_end_long = val(txtpasswordendlong.text)
if password_start_long > password_end_long then
password_start_long = val(txtpasswordendlong.text)
password_end_long = val(txtpasswordstartlong.text)
end if
label1.caption = "破解进度:"
label1.refresh
on error resume next
if ucase(right(strpath, 3)) = "xls" then
for k = password_start_long to password_end_long '破解excel开始
for i = 0 to arraylen ^ k - 1
pass = ""
temp = i
for j = 1 to k - 1
temp = temp \ arrayle
pass = all_char(temp mod arraylen) + pass
next j
pass = pass + all_char(i mod arraylen)
set openreturn = xls.workbooks.open(filename:=strpath, password:=pass)
text1.text = pass '显示破解进度
text1.refresh
if err.number 0 then '如果解密成功,打开文档,显示密码,退出过程
err.clear
else
label1.caption = "文档密码:"
text1.text = pass
me.refresh
xls.visible = true
cmdstartcrack.mousepointer = 0
cmdstartcrack.caption = "开始破解"
blnprocessing = false
set xls = nothing
exit sub
end if
doevents
if not blnprocessing then exit for
next i
if not blnprocessing then exit for
next k
xls.quit
set xls = nothing
else
for k = password_start_long to password_end_long '破解word开始
for i = 0 to arraylen ^ k - 1
pass = ""
temp = i
for j = 1 to k -
temp = temp \ arraylen
pass = all_char(temp mod arraylen) + pass
next j
pass = pass + all_char(i mod arraylen)
openreturn = wd.documents.open(filename:=strpath, passworddocument:=pass)
text1.text = pass '显示破解进度
text1.refresh
if err.number 0 then '如果解密成功,打开文档,显示密码,退出过程
err.clear
else
'msgbox "word password"
label1.caption = "文档密码:"
text1.text = pass
me.refresh
wd.visible = true
cmdstartcrack.mousepointer = 0
cmdstartcrack.caption = "开始破解"
blnprocessing = false
set wd = nothing
exit sub
end if
doevents
if not blnprocessing then exit for
next i
if not blnprocessing then exit for
next k
wd.quit
set wd = nothing
end if
cmdstartcrack.caption = "开始破解"
if blnprocessing then msgbox "没有找到密码,可能是密码位数不对!", , "提示信息..."
blnprocessing = false
end sub
3. 时间复杂度分析
一个算法的时间复杂度,是指该算法的时间耗费,是该算法所求解问题规模n的函数。根据前面讲的实现原理,我们知道,破解算法的时间耗费主要集中在尝试打开office文档上,因此,当我们假设破解一个n位字符串密码,且选定密码字符范围的总字符数为arraylen时,该算法的时间复杂度是o(arraylen^n)。即,当n确定后,该算法的时间复杂度是n次方阶;当arraylen确定后,该算法的时间复杂度是指数阶。都是高数量级的时间复杂度。
4. 说明
穷举法解密对系统资源的占用是十分惊人的,在解密的过程中最好不要运行其他应用程序。如果安装有瑞星等杀毒软件,应将杀毒软件的“office安全助手”去掉,以便加快程序的运行速度。
该程序在winxp+officexp+vb6.0环境下测试通过,笔者随便测试了一个5位数字密码,在p4机器上,8分钟左右即可解开口令。
参考文献:
[1] 王建华 译. visual basic 6 开发人员指南. 北京:机械工业出版社,1999