Access 密码破解原理- -
ACCESS数据库的密码极容易被破,使用网上流行的工具,不管你的密码多少位很快就能被破出来。原以为ACCESS与WORD EXCEL一样,作为MS OFFICE的套件,一样采用DES加密,非破暴力破解不可。密码倍数越长越难破。结果,一试,果然很快被破出来。
经查询,原来ACCESS加密过于简单。如下:
在库文件的地址00000042处开始的40个字节是Access 2000库的密码位。如果一个未加密的库,这40个字节原始数据依次为:29 77 EC 37 F2 C8 9C FA 69 D2 28 E6 BC 3A 8A 60 FB 18 7B 36 5A FE DF B1 D8 78 13 43 60 23 B1 33 9B ED 79 5B 3D 33 3D 39 7C 2A 。当你给数据库设置了密码后,这40个字节就变成了密钥。因此,要破解密码而不需保持原库的密码,只要将00000042 处开始的40个字节还原成原始数据就行了,从而跳过口令。
另外,当你设置了密码后,Access 2000 就将你的密码的ASCII码与以上的40个字节数据进行异或操作,因此,从库文件的地址00000042开始的40个字节就变成了密钥了。例如,如果你设置的密码为12345678901234567890(注意:最多只能设20个ASCII字符),经过异或操作后,则从00000042处开始的40个字节的数据就变成了 18 77 DE 37 C1 C8 A8 FA 5C D2 1E E6 8B 3A B2 60 C2 18 4B 36 6B FE ED B1 EB 78 27 43 55 23 87 33 AC ED 41 5B 04 39 4C 2A 。大家都知道,一个数据经过一次异或操作后,再一次经过同样的异或操作就可还原了。因此,对已经设置了密码的Access 2000库,只要将40个密钥数据与原始的40个数据进行一次异或操作就可得到密码了。
最后,要注意的是,由于ACCESS 2000对每个密码字符采用双字节表示,故40个字节原始数据可依次分为20组,每组代表一个密码字符,进行异或操作的是每组的第一个字节,第二个字节不变。
// CrackAccessPassDlg.cpp : 实现文件 // #include "stdafx.h" #include "CrackAccessPass.h" #include "CrackAccessPassDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialog { public: CAboutDlg(); // 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() // CCrackAccessPassDlg 对话框 CCrackAccessPassDlg::CCrackAccessPassDlg(CWnd* pParent /**//*=NULL*/) : CDialog(CCrackAccessPassDlg::IDD, pParent) , m_filePath(_T("")) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CCrackAccessPassDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_filePath); } BEGIN_MESSAGE_MAP(CCrackAccessPassDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_BN_CLICKED(IDC_BUTTON1, &CCrackAccessPassDlg::OnBnClickedButton1) END_MESSAGE_MAP() // CCrackAccessPassDlg 消息处理程序 BOOL CCrackAccessPassDlg::OnInitDialog() { CDialog::OnInitDialog(); // 将“关于”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CCrackAccessPassDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CCrackAccessPassDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标显示。 // HCURSOR CCrackAccessPassDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CCrackAccessPassDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 CFileDialog fd(TRUE,"*.mdb","",OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"ACCESS数据库文件(*.mdb)|*.mdb|所有文件(*.*)|*.*||",NULL,0); INT_PTR nResponse = fd.DoModal(); if(nResponse == IDOK) { m_filePath = fd.GetPathName(); UpdateData(false); DoCrack(); } } /**//* * @ 解密工作 */ void CCrackAccessPassDlg::DoCrack() { CString PassWord; BYTE accVer[3] = {0}; BYTE accID = 0x00; BYTE accFile[40] = {0}; BYTE accDataKey[128] = {0}; char accFlagString[16] = {0}; //以只读方式打开 CFile myFile(m_filePath,CFile::modeRead | CFile::shareDenyNone); DWORD len = myFile.GetLength(); if(len > 320) { myFile.Seek(66,CFile::begin); myFile.Read(&accFile,sizeof(accFile)); myFile.Seek(156,CFile::begin); myFile.Read(&accVer,sizeof(accVer)); myFile.Seek(20,CFile::begin); myFile.Read(&accID,sizeof(accID)); myFile.Seek(24,CFile::begin); myFile.Read(&accDataKey,sizeof(accDataKey)); myFile.Seek(4,CFile::begin); myFile.Read(&accFlagString,sizeof(accFlagString)); } myFile.Close(); if(strcmp(accFlagString,"Standard Jet DB")) { SetDlgItemText(IDC_VER,"不是Access数据库!"); return; } char *sKey2K = "3074EC37EBCB9CFA70D128E6A5398A60E21B7B3643FDDFB1C17B13437920B13382EE795B243A7C2A"; char *sKey97 = "86FBEC375D449CFAC65E28E613"; CString Version; //判断版本虽然没用,先留着 if(accVer[0] == '0') { Version = "3.51"; } else { Version = (CString)accVer; } Version = (accID == 0 ? "Access 97":"Access 2000"); SetDlgItemText(IDC_VER,Version); //取密码 if(accID == 1) { PassWord = CrackPassDirect(accDataKey); if ( PassWord.GetLength() == 0) { PassWord = "没有密码"; } } else if(accID == 0) { PassWord = CrackPass97(accFile); if(PassWord.GetLength() ==0) { PassWord = "没有密码"; } } else { PassWord = "不是Access数据库"; } SetDlgItemText(IDC_PASS,PassWord); } CString CCrackAccessPassDlg::CrackPassDirect(BYTE (&f_bytes)[128]) { CString ret; long l; BYTE EncryptKey[4]; //初始密钥 BYTE EncryptRet[258] = {0}; EncryptKey[0] = 0xC7; EncryptKey[1] = 0xDA; EncryptKey[2] = 0x39; EncryptKey[3] = 0x6b; //得到解密Key for(l=0;l<=255;l++) { EncryptRet[l] = l; } long temp1,temp2,temp3,temp4,temp5,temp6,temp7,temp8; temp1 = temp2 = temp3 = temp4 = temp5 = temp6 = temp7 = temp8 = 0; for(l=0;l<=255;l++) { temp1 = temp2; temp1 = EncryptKey[temp1]; temp4 = EncryptRet[l]; temp1 = temp1 + temp4; temp4 = temp3; temp1 = temp1 + temp4; temp1 = temp1 & 0x800000FF; temp3 = temp1; temp1 = EncryptRet[l]; temp5 = temp1; temp1 = temp3; temp1 = EncryptRet[temp1]; EncryptRet[l] = temp1; temp4 = temp3; EncryptRet[temp4] = temp5; temp1 = temp2; temp1++; temp4 = temp1 % 4; temp2 = temp4; } //运算结束 //又一轮运算 temp1=temp2=temp3=temp4=temp5=temp6=temp7=temp8=0; temp4 = EncryptRet[0x100]; temp1 = EncryptRet[0x101]; for(l=1;l<=0x80;l++) { temp4++; temp4 &= 0x800000FF; temp3 = temp4 & 0xff; temp5 = EncryptRet[temp3]; temp1 &= 0xff; temp5 += temp1; temp1 = temp5 & 0x800000FF; temp6 = EncryptRet[temp4]; temp5 = EncryptRet[temp1]; EncryptRet[temp3] = temp5; temp2 = temp1; EncryptRet[temp2] = temp6; temp5 = EncryptRet[temp3]; temp3 = EncryptRet[temp1 & 0xff]; temp5 += temp3; temp5 &= 0x800000FF; temp7 = temp5; temp3 = temp8; temp5 = EncryptRet[temp5]; long t = f_bytes[temp3] ^ temp5; f_bytes[temp3] = t; temp8 ++; } EncryptRet[0x100] = temp4; EncryptRet[0x101] = temp1; //又一轮结束 double db; ::MoveMemory(&db,&f_bytes[90],8); //::RtlMoveMemory(&db,(&f_bytes[0])+90,8); long Key = (int)db; long lResult[20]; for(l=0;l<20;l++) { lResult[l] = f_bytes[l*2+42]+256*(long)f_bytes[l*2+43]; if(l%2 == 0) { lResult[l] ^= Key; } if(lResult[l] != 0) { ret += (char)lResult[l]; } } return ret; } CString CCrackAccessPassDlg::CrackPass97(BYTE (&f_bytes)[40]) { CString ret; BYTE KEY[] = {0X86,0xfb,0xec,0x37,0x5d,0x44,0x9c,0xfa,0xc6,0x5e,0x28,0xe6,0x13}; long lAsc; for(int i=0;i<sizeof(KEY);i++) { lAsc = f_bytes[i] ^ KEY[i]; if(lAsc != 0) { ret += (char)lAsc; } } return ret; }