过去公司的一个同事的一台用于商务活动的手机坏了,机型为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);
}