過去公司的一個同事的一臺用於商務活動的手機壞了,機型爲MTK6253,裏面儲存了所有的電話號碼,不幸之萬幸在於T卡有一份電話本備份。但這個備份疑似使用了加密,電話本信息中用戶姓名顯示爲字母和數字之組合,而不是明文,這種備份只有原手機同款機型能導入使用,其他手機均無法正常導入,而原手機年代太過久遠,早已經停產。最糟糕的是也找不到相關代碼,無法從算法角度研究反解碼算法。
同事碾轉找到我,希望能破解電話本備份,把用戶名還原爲正常。破解中文加密信息其實十分麻煩,因爲中文信息的流行編碼非常多,常見的就有ASCII,UNICODE,GB2312,UTF8,UTF16,BIG5,GBK同時因爲兩個字節表示,又受高低位大小端影響,所以要破解中文加密資源難道相當大。一般情況下可以歸納爲三步走,一是先根據密文猜測加密算法,解出明文漢字編碼,二是根據明文判斷文字編碼,三是通過文字編碼按照不同高低位寫入新文件或者輸出,如果能解出明文,就可以寫出解密算法。
首先分析加密後的結果,都是形如:
1MgAwADQABVMfdw==|13 8 XXXXXX之類
因爲所有記錄都如此,觀察可知,記錄由豎框可分爲兩部分,豎框後面顯示明文的電話號碼,豎槓前是密文的用戶名,電話號碼每個字符由兩個字節表示,字符串整體可以顯示,因此可能意味着豎槓前後可能使用的是同一編碼,但數字字母的各種編碼ASC只佔一個字節,另一字節是0填充。因此沒辦法推測原文件編碼。同時觀察分析可知加密的應該只有用戶名。
接下來破解第一步,分析加密方法,DES和BASE64都能產生類似上面的密文,確切的說,BASE64不能算加密方式,但如果開發者出於某些目的打亂了編碼的索引表,產生的密文也將是幾乎沒有辦法破解的,除非獲取相關的索引表,希望沒有那麼複雜,但這兩種加密方式形成的密文長度不同,Base64是4的倍數,DES是12倍數,1MgAwADQABVMfdw==,數了密文長度後,發現17兩個長度均不對應,其實DES和BASE64都可以根據需要改變,但其密文長度一般是有規律的,很難改變。17接近16,是4的倍數,因此嘗試先使用BASE64突破口。
經過觀察和思考,發現如果去掉密文中前面的數字,長度正好和BASE64可以匹配,於是使用在線BASE64工具和在線漢字編碼查詢工具結合分析編號,使用BASE64在線工具嘗試解碼字串MgAwADQABVMfdw==,獲得一串亂碼,考慮到漢字編碼的不同和高低位的不同,使用十六進制輸出,獲得\x32\x00 \x30 \x00 \x34 \x00 \x05 \x53 \x1f \x77,使用編碼查詢得知對應的漢字是204包真,由於高低位的原因,網上大部分工具都無法直接正確解碼。至此,找到規律,破解完成,密文不包括前面的數字,數字可能是分組信息,沒有加密,用戶名字信息使用unicode加BASE64編碼。
找出了規律,就可以發揮軟件開發人員的優勢,寫一個軟件,一下子全破解。使用ECLIPSE,百度搜索一下JAVA代碼,找一個一簡單編輯器,添加上解密的思路一試,如下:
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import sun.misc.BASE64Decoder;
public class MenuFrame {
public static void main(String[] args) {
final Frame frame = new Frame();
frame.setSize(800, 800);
frame.setLocation(100, 100);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
final TextArea ta = new TextArea();
frame.add(ta);
// 創建菜單欄
MenuBar mb = new MenuBar();
// 創建菜單
Menu file = new Menu("File");
Menu edit = new Menu("Edit");
// 創建菜單項
MenuItem mi1 = new MenuItem("Open");
// 添加打開文件功能響應
mi1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
FileDialog fd = new FileDialog(frame, "打開文件", FileDialog.LOAD);
fd.setVisible(true);
String fileName = fd.getDirectory() + fd.getFile();
if (fileName != null)
{
try {
FileInputStream fis = new FileInputStream(fileName);
byte[] buf = new byte[10 * 1024];
try {
int len = fis.read(buf);
ta.append(new String(buf, 0, len));
fis.close();
} catch (IOException e1) {
e1.printStackTrace();
}
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
}
}
});
MenuItem mi2 = new MenuItem("解密");
mi2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
//System.exit(0);
//String key = decryptBASE64("MgAwADQAfXYclw==");
//String key = decryptBASE64("MgAwADQABVMfdw==");
String key = decryptBASE64(ta.getText());
byte[] kk = key.getBytes();
for (int i = 0; i < kk.length; i +=2){
byte[] b = new byte[2];
b[0] = kk[i+1];
b[1] = kk[i];
//System.out.println("xxxxx:"+byteToChar(b));
ta.append(String.valueOf((byteToChar(b))));
}
}
});
MenuItem mi3 = new MenuItem("Other Save");
MenuItem mi4 = new MenuItem("Close");
// 添加 關閉響應
mi4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
System.exit(0);
}
});
MenuItem mi5 = new MenuItem("Cope");
MenuItem mi6 = new MenuItem("Paste");
file.add(mi1);
file.add(mi2);
file.add(mi3);
file.add(mi4);
edit.add(mi5);
edit.add(mi6);
mb.add(file);
mb.add(edit);
frame.setMenuBar(mb);
frame.setVisible(true);
}
//解碼BASE64
private static BASE64Decoder decoder = new BASE64Decoder();// 解密
public static String decryptBASE64(String outputStr) {
String value = "";
try {
byte[] key = decoder.decodeBuffer(outputStr);
value = new String(key);
} catch (Exception e) {
}
return value;
}
//交換高低位
public static char byteToChar(byte[] b) {
char c = (char) (((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
return c;
}
}
由於JAVA對環境的依賴,運行JAR需要配置相關的JDK,對於沒運行過的人來說非常不方便,也可以開發一個VC解密軟件,創建一個VCDIALOG程序,加入下面代碼;
// bbbDlg.cpp : implementation file
//
#include "stdafx.h"
#include "bbb.h"
#include "bbbDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CBbbDlg dialog
CBbbDlg::CBbbDlg(CWnd* pParent /*=NULL*/)
: CDialog(CBbbDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CBbbDlg)
m_encode = _T("");
m_decode = _T("");
m_cmd = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CBbbDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CBbbDlg)
DDX_Text(pDX, IDC_EDIT1, m_encode);
DDX_Text(pDX, IDC_EDIT2, m_decode);
DDX_Text(pDX, IDC_EDIT3, m_cmd);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CBbbDlg, CDialog)
//{{AFX_MSG_MAP(CBbbDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
ON_BN_CLICKED(IDC_BUTTON3, OnButton3)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CBbbDlg message handlers
BOOL CBbbDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
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);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CBbbDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CBbbDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
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;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CBbbDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
BYTE Decode_GetByte(char c);
char Encode_GetChar(BYTE num);
//===================================
// Base64 解碼
//===================================
BYTE Decode_GetByte(char c)
{
if(c == '+')
return 62;
else if(c == '/')
return 63;
else if(c <= '9')
return (BYTE)(c - '0' + 52);
else if(c == '=')
return 64;
else if(c <= 'Z')
return (BYTE)(c - 'A');
else if(c <= 'z')
return (BYTE)(c - 'a' + 26);
return 64;
}
//解碼
size_t Base64_Decode(char *pDest, const char *pSrc, size_t srclen)
{
BYTE input[4];
size_t i, index = 0;
for(i = 0; i < srclen; i += 4)
{
//byte[0]
input[0] = Decode_GetByte(pSrc[i]);
input[1] = Decode_GetByte(pSrc[i + 1]);
pDest[index++] = (input[0] << 2) + (input[1] >> 4);
//byte[1]
if(pSrc[i + 2] != '=')
{
input[2] = Decode_GetByte(pSrc[i + 2]);
pDest[index++] = ((input[1] & 0x0f) << 4) + (input[2] >> 2);
}
//byte[2]
if(pSrc[i + 3] != '=')
{
input[3] = Decode_GetByte(pSrc[i + 3]);
pDest[index++] = ((input[2] & 0x03) << 6) + (input[3]);
}
}
//null-terminator
pDest[index] = 0;
return index;
}
//===================================
// Base64 編碼
//===================================
char Encode_GetChar(BYTE num)
{
return
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/="[num];
}
//編碼
size_t Base64_Encode(char *pDest, const char *pSrc, size_t srclen)
{
BYTE input[3], output[4];
size_t i, index_src = 0, index_dest = 0;
for(i = 0; i < srclen; i += 3)
{
//char [0]
input[0] = pSrc[index_src++];
output[0] = (BYTE)(input[0] >> 2);
pDest[index_dest++] = Encode_GetChar(output[0]);
//char [1]
if(index_src < srclen)
{
input[1] = pSrc[index_src++];
output[1] = (BYTE)(((input[0] & 0x03) << 4) + (input[1] >> 4));
pDest[index_dest++] = Encode_GetChar(output[1]);
}
else
{
output[1] = (BYTE)((input[0] & 0x03) << 4);
pDest[index_dest++] = Encode_GetChar(output[1]);
pDest[index_dest++] = '=';
pDest[index_dest++] = '=';
break;
}
//char [2]
if(index_src < srclen)
{
input[2] = pSrc[index_src++];
output[2] = (BYTE)(((input[1] & 0x0f) << 2) + (input[2] >> 6));
pDest[index_dest++] = Encode_GetChar(output[2]);
}
else
{
output[2] = (BYTE)((input[1] & 0x0f) << 2);
pDest[index_dest++] = Encode_GetChar(output[2]);
pDest[index_dest++] = '=';
break;
}
//char [3]
output[3] = (BYTE)(input[2] & 0x3f);
pDest[index_dest++] = Encode_GetChar(output[3]);
}
//null-terminator
pDest[index_dest] = 0;
return index_dest;
}
wchar_t* ANSIToUnicode(const char* str, wchar_t * dest)
{
int len = 0;
len = strlen(str);
int unicodeLen = ::MultiByteToWideChar( CP_ACP,
0,
str,
-1,
NULL,
0 );
wchar_t * pUnicode;
pUnicode = new wchar_t[unicodeLen+1];
memset(pUnicode,0,(unicodeLen+1)*sizeof(wchar_t));
::MultiByteToWideChar( CP_ACP,
0,
str,
-1,
(LPWSTR)pUnicode,
unicodeLen );
memcpy(dest, pUnicode, unicodeLen);
delete pUnicode;
return dest;
}
char* UnicodeToANSI( const wchar_t* str, char * dest )
{
char* pElementText;
int iTextLen;
// wide char to multi char
iTextLen = WideCharToMultiByte( CP_ACP,
0,
str,
-1,
NULL,
0,
NULL,
NULL );
pElementText = new char[iTextLen + 1];
memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );
::WideCharToMultiByte( CP_ACP,
0,
str,
-1,
pElementText,
iTextLen,
NULL,
NULL );
memcpy(dest, pElementText, iTextLen);
delete[] pElementText;
return dest;
}
void CBbbDlg::OnButton1()
{
// TODO: Add your control notification handler code here
UpdateData(true);
int len = m_encode.GetLength();
//char * tmp = new char[(len/3)*4+((len%3)!=0)?4:0];
//char tmp[(len/3)*4+((len%3)!=0)?4:0] = {0x00};
char tmp[1024] = {0x00};
//memset(tmp, 0x00, (len/3)*4+((len%3)!=0)?4:0);
wchar_t tmp1[1024] = {0x00};
ANSIToUnicode((char *)m_encode.GetBuffer(len), tmp1);
Base64_Encode(tmp,(char *)tmp1, len);
m_decode.Format("%s", tmp);
UpdateData(false);
//delete []tmp;
}
void CBbbDlg::OnButton2()
{
// TODO: Add your control notification handler code here
UpdateData(true);
int len = m_decode.GetLength();
//char tmp[(len/4)*3] = {0x00};
char tmp[1024] = {0x00};
Base64_Decode(tmp, (char *)m_decode.GetBuffer(len), len);
char tmp1[1024] = {0x00};
UnicodeToANSI((wchar_t *)tmp, tmp1);
m_encode.Format("%s", tmp1);
UpdateData(false);
//delete []tmp;
}
CString runCmd(char* strCommend)
{
//
//通過管道技術回顯cmd輸出信息
//
SECURITY_ATTRIBUTES sa;
HANDLE hRead,hWrite;
CString strOutput;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL; //使用系統默認的安全描述符
sa.bInheritHandle = TRUE; //創建的進程繼承句柄
if (!CreatePipe(&hRead,&hWrite,&sa,0)) //創建匿名管道
{
MessageBox(NULL,"CreatePipe Failed!","提示",MB_OK | MB_ICONWARNING);
return strOutput;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.hStdError = hWrite;
si.hStdOutput = hWrite; //新創建進程的標準輸出連在寫管道一端
si.wShowWindow = SW_HIDE; //隱藏窗口
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
char cmdline[500];
sprintf(cmdline,"cmd /C %s",strCommend);
if (!CreateProcess(NULL,cmdline,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) //創建子進程
{
MessageBox(NULL,"CreateProcess Failed!","提示",MB_OK | MB_ICONWARNING);
return strOutput;
}
CloseHandle(hWrite); //關閉管道句柄
char buffer[4096] = {0};
DWORD bytesRead;
while (true)
{
if (ReadFile(hRead,buffer,4095,&bytesRead,NULL) == NULL) //讀取管道
break;
strOutput.Format("%s", buffer);
Sleep(100);
}
CloseHandle(hRead);
return strOutput;
}
void CBbbDlg::OnButton3()
{
// TODO: Add your control notification handler code here
UpdateData(true);
m_cmd = runCmd("adb");
UpdateData(false);
}